def _star_star_dict(evaluator, array, expression_list, func): dct = {} from jedi.evaluate.representation import Instance if isinstance(array, Instance) and array.name.get_code() == 'dict': # For now ignore this case. In the future add proper iterators and just # make one call without crazy isinstance checks. return {} if isinstance(array, iterable.Array) and array.type == pr.Array.DICT: for key_stmt, value_stmt in array.items(): # first index, is the key if syntactically correct call = key_stmt.expression_list()[0] if isinstance(call, pr.Name): key = call elif isinstance(call, pr.Call): key = call.name else: debug.warning('Ignored complicated **kwargs stmt %s' % call) continue # We ignore complicated statements here, for now. # If the string is a duplicate, we don't care it's illegal Python # anyway. dct[str(key)] = key, value_stmt else: if expression_list: m = "TypeError: %s argument after ** must be a mapping, not %s" \ % (func.name.get_code(), array) analysis.add(evaluator, 'type-error-star-star', expression_list[0], message=m) return dct
def find(self, scopes, attribute_lookup): """ :params bool attribute_lookup: Tell to logic if we're accessing the attribute or the contents of e.g. a function. """ # TODO rename scopes to names_dicts names = self.filter_name(scopes) if self._found_predefined_if_name is not None: return self._found_predefined_if_name types = self._names_to_types(names, attribute_lookup) if ( not names and not types and not (isinstance(self.name_str, tree.Name) and isinstance(self.name_str.parent.parent, tree.Param)) ): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if attribute_lookup: analysis.add_attribute_error(self._evaluator, self.scope, self.name_str) else: message = "NameError: name '%s' is not defined." % self.name_str analysis.add(self._evaluator, "name-error", self.name_str, message) debug.dbg("finder._names_to_types: %s -> %s", names, types) return types
def _element_calculate(evaluator, left, operator, right): from jedi.evaluate import iterable, representation as er l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Array) or _is_string(left): return [left] elif isinstance(right, iterable.Array) or _is_string(right): return [right] elif operator == '+': if l_is_num and r_is_num or _is_string(left) and _is_string(right): return [create(evaluator, left.obj + right.obj)] elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right): return [iterable.MergedArray(evaluator, (left, right))] elif operator == '-': if l_is_num and r_is_num: return [create(evaluator, left.obj - right.obj)] elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return [left] def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, er.Instance) and obj.name in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(evaluator, 'type-error-operation', operator, message % (left, right)) return [left, right]
def unpack_tuple_to_dict(evaluator, types, exprlist): """ Unpacking tuple assignments in for statements and expr_stmts. """ if exprlist.type == 'name': return {exprlist.value: types} elif exprlist.type == 'atom' and exprlist.children[0] in '([': return unpack_tuple_to_dict(evaluator, types, exprlist.children[1]) elif exprlist.type in ('testlist', 'testlist_comp', 'exprlist', 'testlist_star_expr'): dct = {} parts = iter(exprlist.children[::2]) n = 0 for iter_types in py__iter__(evaluator, types, exprlist): n += 1 try: part = next(parts) except StopIteration: analysis.add(evaluator, 'value-error-too-many-values', part, message="ValueError: too many values to unpack (expected %s)" % n) else: dct.update(unpack_tuple_to_dict(evaluator, iter_types, part)) has_parts = next(parts, None) if types and has_parts is not None: analysis.add(evaluator, 'value-error-too-few-values', has_parts, message="ValueError: need more than %s values to unpack" % n) return dct elif exprlist.type == 'power' or exprlist.type == 'atom_expr': # Something like ``arr[x], var = ...``. # This is something that is not yet supported, would also be difficult # to write into a dict. return {} raise NotImplementedError
def find(self, filters, attribute_lookup): """ :params bool attribute_lookup: Tell to logic if we're accessing the attribute or the contents of e.g. a function. """ names = self.filter_name(filters) if self._found_predefined_types is not None and names: check = flow_analysis.reachability_check( self._context, self._context.tree_node, self._name) if check is flow_analysis.UNREACHABLE: return set() return self._found_predefined_types types = self._names_to_types(names, attribute_lookup) if not names and not types \ and not (isinstance(self._name, tree.Name) and isinstance(self._name.parent.parent, tree.Param)): if isinstance(self._name, tree.Name): if attribute_lookup: analysis.add_attribute_error( self._name_context, self._context, self._name) else: message = ("NameError: name '%s' is not defined." % self._string_name) analysis.add(self._name_context, 'name-error', self._name, message) return types
def builtins_isinstance(evaluator, objects, types, arguments): bool_results = set([]) for o in objects: try: mro_func = o.py__class__().py__mro__ except AttributeError: # This is temporary. Everything should have a class attribute in # Python?! Maybe we'll leave it here, because some numpy objects or # whatever might not. return set([compiled.create(True), compiled.create(False)]) mro = mro_func() for cls_or_tup in types: if cls_or_tup.is_class(): bool_results.add(cls_or_tup in mro) elif cls_or_tup.name.string_name == 'tuple' \ and cls_or_tup.get_root_context() == evaluator.BUILTINS: # Check for tuples. classes = unite( lazy_context.infer() for lazy_context in cls_or_tup.py__iter__() ) bool_results.add(any(cls in mro for cls in classes)) else: _, lazy_context = list(arguments.unpack())[1] if isinstance(lazy_context, LazyTreeContext): node = lazy_context.data message = 'TypeError: isinstance() arg 2 must be a ' \ 'class, type, or tuple of classes and types, ' \ 'not %s.' % cls_or_tup analysis.add(lazy_context._context, 'type-error-isinstance', node, message) return set(compiled.create(evaluator, x) for x in bool_results)
def _eval_comparison_part(evaluator, context, left, operator, right): l_is_num = is_number(left) r_is_num = is_number(right) if isinstance(operator, unicode): str_operator = operator else: str_operator = force_unicode(str(operator.value)) if str_operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Sequence) or is_string(left): return ContextSet(left) elif isinstance(right, iterable.Sequence) or is_string(right): return ContextSet(right) elif str_operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return ContextSet(left.execute_operation(right, str_operator)) elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right): return ContextSet(iterable.MergedArray(evaluator, (left, right))) elif str_operator == '-': if l_is_num and r_is_num: return ContextSet(left.execute_operation(right, str_operator)) elif str_operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return ContextSet(left) elif str_operator in COMPARISON_OPERATORS: if is_compiled(left) and is_compiled(right): # Possible, because the return is not an option. Just compare. try: return ContextSet(left.execute_operation(right, str_operator)) except TypeError: # Could be True or False. pass else: if str_operator in ('is', '!=', '==', 'is not'): operation = COMPARISON_OPERATORS[str_operator] bool_ = operation(left, right) return ContextSet(_bool_to_context(evaluator, bool_)) return ContextSet(_bool_to_context(evaluator, True), _bool_to_context(evaluator, False)) elif str_operator == 'in': return NO_CONTEXTS def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, CompiledInstance) and \ obj.name.string_name in ('int', 'float') # Static analysis, one is a number, the other one is not. if str_operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(context, 'type-error-operation', operator, message % (left, right)) return ContextSet(left, right)
def _add_error(context, name, message=None): # Should be a name, not a string! if message is None: name_str = str(name.value) if isinstance(name, tree.Name) else name message = 'No module named ' + name_str if hasattr(name, 'parent'): analysis.add(context, 'import-error', name, message) else: debug.warning('ImportError without origin: ' + message)
def follow(self, is_goto=False): if self._evaluator.recursion_detector.push_stmt(self.import_stmt): # check recursion return [] try: if self.import_path: try: module, rest = self._importer.follow_file_system() except ModuleNotFound as e: analysis.add(self._evaluator, 'import-error', e.name_part) return [] if module is None: return [] if self.import_stmt.is_nested() and not self.nested_resolve: scopes = [NestedImportModule(module, self.import_stmt)] else: scopes = [module] star_imports = remove_star_imports(self._evaluator, module) if star_imports: scopes = [StarImportModule(scopes[0], star_imports)] # goto only accepts `Name` if is_goto and not rest: scopes = [s.name for s in scopes] # follow the rest of the import (not FS -> classes, functions) if len(rest) > 1 or rest and self.is_like_search: scopes = [] if ('os', 'path') == self.import_path[:2] \ and not self._is_relative_import(): # This is a huge exception, we follow a nested import # ``os.path``, because it's a very important one in Python # that is being achieved by messing with ``sys.modules`` in # ``os``. scopes = self._evaluator.follow_path(iter(rest), [module], module) elif rest: if is_goto: scopes = list(chain.from_iterable( self._evaluator.find_types(s, rest[0], is_goto=True) for s in scopes)) else: scopes = list(chain.from_iterable( self._evaluator.follow_path(iter(rest), [s], s) for s in scopes)) else: scopes = [ImportWrapper.GlobalNamespace] debug.dbg('after import: %s', scopes) if not scopes: analysis.add(self._evaluator, 'import-error', self._importer.import_path[-1]) finally: self._evaluator.recursion_detector.pop_stmt() return scopes
def py__getitem__(self, index_context_set, contextualized_node): from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( contextualized_node.context, 'type-error-not-subscriptable', contextualized_node.node, message="TypeError: '%s' object is not subscriptable" % self ) return NO_CONTEXTS
def py__getitem__(evaluator, types, trailer): from jedi.evaluate.representation import Class result = set() trailer_op, node, trailer_cl = trailer.children assert trailer_op == "[" assert trailer_cl == "]" # special case: PEP0484 typing module, see # https://github.com/davidhalter/jedi/issues/663 for typ in list(types): if isinstance(typ, Class): typing_module_types = \ pep0484.get_types_for_typing_module(evaluator, typ, node) if typing_module_types is not None: types.remove(typ) result |= typing_module_types if not types: # all consumed by special cases return result for index in create_index_types(evaluator, node): if isinstance(index, (compiled.CompiledObject, Slice)): index = index.obj if type(index) not in (float, int, str, unicode, slice): # If the index is not clearly defined, we have to get all the # possiblities. for typ in list(types): if isinstance(typ, Array) and typ.type == 'dict': types.remove(typ) result |= typ.dict_values() return result | py__iter__types(evaluator, types) for typ in types: # The actual getitem call. try: getitem = typ.py__getitem__ except AttributeError: analysis.add( evaluator, 'type-error-not-subscriptable', trailer_op, message="TypeError: '%s' object is not subscriptable" % typ) else: try: result |= getitem(index) except IndexError: result |= py__iter__types(evaluator, set([typ])) except KeyError: # Must be a dict. Lists don't raise KeyErrors. result |= typ.dict_values() return result
def _element_calculate(evaluator, context, left, operator, right): from jedi.evaluate import iterable, instance l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.AbstractSequence) or is_string(left): return set([left]) elif isinstance(right, iterable.AbstractSequence) or is_string(right): return set([right]) elif operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return set([create(evaluator, left.obj + right.obj)]) elif _is_tuple(left) and _is_tuple(right) or _is_list( left) and _is_list(right): return set([iterable.MergedArray(evaluator, (left, right))]) elif operator == '-': if l_is_num and r_is_num: return set([create(evaluator, left.obj - right.obj)]) elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return set([left]) elif operator in COMPARISON_OPERATORS: operation = COMPARISON_OPERATORS[operator] if isinstance(left, CompiledObject) and isinstance( right, CompiledObject): # Possible, because the return is not an option. Just compare. left = left.obj right = right.obj try: result = operation(left, right) except TypeError: # Could be True or False. return set([create(evaluator, True), create(evaluator, False)]) else: return set([create(evaluator, result)]) elif operator == 'in': return set() def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, instance.CompiledInstance) and \ obj.name.string_name in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(context, 'type-error-operation', operator, message % (left, right)) return set([left, right])
def _iterate_star_args(context, array, input_node, funcdef=None): try: iter_ = array.py__iter__ except AttributeError: if funcdef is not None: # TODO this funcdef should not be needed. m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (funcdef.name.value, array) analysis.add(context, 'type-error-star', input_node, message=m) else: for lazy_context in iter_(): yield lazy_context
def _element_calculate(evaluator, context, left, operator, right): from jedi.evaluate import iterable, instance l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.AbstractSequence) or is_string(left): return set([left]) elif isinstance(right, iterable.AbstractSequence) or is_string(right): return set([right]) elif operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return set([create(evaluator, left.obj + right.obj)]) elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right): return set([iterable.MergedArray(evaluator, (left, right))]) elif operator == '-': if l_is_num and r_is_num: return set([create(evaluator, left.obj - right.obj)]) elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return set([left]) elif operator in COMPARISON_OPERATORS: operation = COMPARISON_OPERATORS[operator] if isinstance(left, CompiledObject) and isinstance(right, CompiledObject): # Possible, because the return is not an option. Just compare. left = left.obj right = right.obj try: result = operation(left, right) except TypeError: # Could be True or False. return set([create(evaluator, True), create(evaluator, False)]) else: return set([create(evaluator, result)]) elif operator == 'in': return set() def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, instance.CompiledInstance) and \ obj.name.string_name in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(context, 'type-error-operation', operator, message % (left, right)) return set([left, right])
def _element_calculate(evaluator, left, operator, right): from jedi.evaluate import iterable, representation as er l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Array) or is_string(left): return [left] elif isinstance(right, iterable.Array) or is_string(right): return [right] elif operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return [create(evaluator, left.obj + right.obj)] elif _is_tuple(left) and _is_tuple(right) or _is_list( left) and _is_list(right): return [iterable.MergedArray(evaluator, (left, right))] elif operator == '-': if l_is_num and r_is_num: return [create(evaluator, left.obj - right.obj)] elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return [left] elif operator in COMPARISON_OPERATORS: operation = COMPARISON_OPERATORS[operator] if isinstance(left, CompiledObject) and isinstance( right, CompiledObject): # Possible, because the return is not an option. Just compare. left = left.obj right = right.obj try: return [keyword_from_value(operation(left, right))] except TypeError: # Could be True or False. return [true_obj, false_obj] elif operator == 'in': return [] def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance( obj, er.Instance) and obj.name.get_code() in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(evaluator, 'type-error-operation', operator, message % (left, right)) return [left, right]
def py__getitem__(evaluator, context, types, trailer): from jedi.evaluate.representation import ClassContext from jedi.evaluate.instance import TreeInstance result = set() trailer_op, node, trailer_cl = trailer.children assert trailer_op == "[" assert trailer_cl == "]" # special case: PEP0484 typing module, see # https://github.com/davidhalter/jedi/issues/663 for typ in list(types): if isinstance(typ, (ClassContext, TreeInstance)): typing_module_types = pep0484.py__getitem__(context, typ, node) if typing_module_types is not None: types.remove(typ) result |= typing_module_types if not types: # all consumed by special cases return result for index in create_index_types(evaluator, context, node): if isinstance(index, (compiled.CompiledObject, Slice)): index = index.obj if type(index) not in (float, int, str, unicode, slice): # If the index is not clearly defined, we have to get all the # possiblities. for typ in list(types): if isinstance(typ, AbstractSequence) and typ.array_type == 'dict': types.remove(typ) result |= typ.dict_values() return result | py__iter__types(evaluator, types) for typ in types: # The actual getitem call. try: getitem = typ.py__getitem__ except AttributeError: # TODO this context is probably not right. analysis.add(context, 'type-error-not-subscriptable', trailer_op, message="TypeError: '%s' object is not subscriptable" % typ) else: try: result |= getitem(index) except IndexError: result |= py__iter__types(evaluator, set([typ])) except KeyError: # Must be a dict. Lists don't raise KeyErrors. result |= typ.dict_values() return result
def _star_star_dict(context, array, input_node, funcdef): from jedi.evaluate.context.instance import CompiledInstance if isinstance(array, CompiledInstance) and array.name.string_name == 'dict': # For now ignore this case. In the future add proper iterators and just # make one call without crazy isinstance checks. return {} elif isinstance(array, iterable.AbstractIterable) and array.array_type == 'dict': return array.exact_key_items() else: if funcdef is not None: m = "TypeError: %s argument after ** must be a mapping, not %s" \ % (funcdef.name.value, array) analysis.add(context, 'type-error-star-star', input_node, message=m) return {}
def _star_star_dict(context, array, input_node, funcdef): from jedi.evaluate.instance import CompiledInstance if isinstance(array, CompiledInstance) and array.name.string_name == 'dict': # For now ignore this case. In the future add proper iterators and just # make one call without crazy isinstance checks. return {} elif isinstance(array, iterable.AbstractSequence) and array.array_type == 'dict': return array.exact_key_items() else: if funcdef is not None: m = "TypeError: %s argument after ** must be a mapping, not %s" \ % (funcdef.name.value, array) analysis.add(context, 'type-error-star-star', input_node, message=m) return {}
def _element_calculate(evaluator, left, operator, right): from jedi.evaluate import iterable, representation as er l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Array) or is_string(left): return [left] elif isinstance(right, iterable.Array) or is_string(right): return [right] elif operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return [create(evaluator, left.obj + right.obj)] elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right): return [iterable.MergedArray(evaluator, (left, right))] elif operator == '-': if l_is_num and r_is_num: return [create(evaluator, left.obj - right.obj)] elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return [left] elif operator in COMPARISON_OPERATORS: operation = COMPARISON_OPERATORS[operator] if isinstance(left, CompiledObject) and isinstance(right, CompiledObject): # Possible, because the return is not an option. Just compare. left = left.obj right = right.obj try: return [keyword_from_value(operation(left, right))] except TypeError: # Could be True or False. return [true_obj, false_obj] elif operator == 'in': return [] def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, er.Instance) and obj.name.get_code() in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(evaluator, 'type-error-operation', operator, message % (left, right)) return [left, right]
def iterate(self, contextualized_node=None): debug.dbg('iterate') try: iter_method = self.py__iter__ except AttributeError: if contextualized_node is not None: from jedi.evaluate import analysis analysis.add( contextualized_node.context, 'type-error-not-iterable', contextualized_node.node, message="TypeError: '%s' object is not iterable" % self) return iter([]) else: return iter_method()
def _iterate_star_args(evaluator, array, input_node, func=None): from jedi.evaluate.representation import Instance if isinstance(array, iterable.Array): for field_stmt in array: # yield from plz! yield field_stmt elif isinstance(array, iterable.Generator): for field_stmt in array.iter_content(): yield iterable.AlreadyEvaluated([field_stmt]) elif isinstance(array, Instance) and array.name.get_code() == 'tuple': debug.warning('Ignored a tuple *args input %s' % array) else: if func is not None: m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (func.name.value, array) analysis.add(evaluator, 'type-error-star', input_node, message=m)
def iterate(self, contextualized_node=None): debug.dbg('iterate') try: iter_method = self.py__iter__ except AttributeError: if contextualized_node is not None: from jedi.evaluate import analysis analysis.add(contextualized_node.context, 'type-error-not-iterable', contextualized_node.node, message="TypeError: '%s' object is not iterable" % self) return iter([]) else: return iter_method()
def get_item(self, index_contexts, contextualized_node): from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.context.iterable import Slice, Sequence result = ContextSet() for index in index_contexts: if isinstance(index, Slice): index = index.obj if isinstance(index, CompiledObject): try: index = index.get_safe_value() except ValueError: pass if type(index) not in (float, int, str, unicode, slice, bytes): # If the index is not clearly defined, we have to get all the # possiblities. if isinstance(self, Sequence) and self.array_type == 'dict': result |= self.dict_values() else: result |= iterate_contexts(ContextSet(self)) continue # The actual getitem call. try: getitem = self.py__getitem__ except AttributeError: from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( contextualized_node.context, 'type-error-not-subscriptable', contextualized_node.node, message="TypeError: '%s' object is not subscriptable" % self) else: try: result |= getitem(index) except EvaluatorIndexError: result |= iterate_contexts(ContextSet(self)) except EvaluatorKeyError: # Must be a dict. Lists don't raise KeyErrors. result |= self.dict_values() except EvaluatorTypeError: # The type is wrong and therefore it makes no sense to do # anything anymore. result = NO_CONTEXTS return result
def get_item(self, index_contexts, contextualized_node): from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.context.iterable import Slice, Sequence result = ContextSet() for index in index_contexts: if isinstance(index, Slice): index = index.obj if isinstance(index, CompiledObject): try: index = index.get_safe_value() except ValueError: pass if type(index) not in (float, int, str, unicode, slice, bytes): # If the index is not clearly defined, we have to get all the # possiblities. if isinstance(self, Sequence) and self.array_type == 'dict': result |= self.dict_values() else: result |= iterate_contexts(ContextSet(self)) continue # The actual getitem call. try: getitem = self.py__getitem__ except AttributeError: from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( contextualized_node.context, 'type-error-not-subscriptable', contextualized_node.node, message="TypeError: '%s' object is not subscriptable" % self ) else: try: result |= getitem(index) except EvaluatorIndexError: result |= iterate_contexts(ContextSet(self)) except EvaluatorKeyError: # Must be a dict. Lists don't raise KeyErrors. result |= self.dict_values() except EvaluatorTypeError: # The type is wrong and therefore it makes no sense to do # anything anymore. result = NO_CONTEXTS return result
def _analysis(self): #statements = set(chain(*self._parser.module().used_names.values())) stmts, imps = analysis.get_module_statements(self._parser.module()) # Sort the statements so that the results are reproducible. for i in imps: iw = imports.ImportWrapper(self._evaluator, i, nested_resolve=True).follow() if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw): analysis.add(self._evaluator, 'import-error', i.namespace.names[-1]) for stmt in sorted(stmts, key=lambda obj: obj.start_pos): if not (isinstance(stmt.parent, pr.ForFlow) and stmt.parent.set_stmt == stmt): self._evaluator.eval_statement(stmt) ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line)
def _iterate_star_args(evaluator, array, input_node, func=None): from jedi.evaluate.representation import Instance if isinstance(array, iterable.Array): # TODO ._items is not the call we want here. Replace in the future. id:131 gh:132 for node in array._items(): yield node elif isinstance(array, iterable.Generator): for types in array.py__iter__(): yield iterable.AlreadyEvaluated(types) elif isinstance(array, Instance) and array.name.get_code() == 'tuple': debug.warning('Ignored a tuple *args input %s' % array) else: if func is not None: m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (func.name.value, array) analysis.add(evaluator, 'type-error-star', input_node, message=m)
def _iterate_star_args(evaluator, array, expression_list, func): from jedi.evaluate.representation import Instance if isinstance(array, iterable.Array): for field_stmt in array: # yield from plz! yield field_stmt elif isinstance(array, iterable.Generator): for field_stmt in array.iter_content(): yield helpers.FakeStatement([field_stmt]) elif isinstance(array, Instance) and array.name == 'tuple': pass else: if expression_list: m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (func.name, array) analysis.add(evaluator, 'type-error-star', expression_list[0], message=m)
def _iterate_star_args(evaluator, array, input_node, func=None): from jedi.evaluate.representation import Instance if isinstance(array, iterable.Array): # TODO ._items is not the call we want here. Replace in the future. for node in array._items(): yield node elif isinstance(array, iterable.Generator): for types in array.py__iter__(): yield iterable.AlreadyEvaluated(types) elif isinstance(array, Instance) and array.name.get_code() == 'tuple': debug.warning('Ignored a tuple *args input %s' % array) else: if func is not None: m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (func.name.value, array) analysis.add(evaluator, 'type-error-star', input_node, message=m)
def _iterate_star_args(evaluator, array, expression_list, func): from jedi.evaluate.representation import Instance if isinstance(array, iterable.Array): for field_stmt in array: # yield from plz! yield field_stmt elif isinstance(array, iterable.Generator): for field_stmt in array.iter_content(): yield helpers.FakeStatement([field_stmt]) elif isinstance(array, Instance) and array.name.get_code() == 'tuple': debug.warning('Ignored a tuple *args input %s' % array) else: if expression_list: m = "TypeError: %s() argument after * must be a sequence, not %s" \ % (func.name.get_code(), array) analysis.add(evaluator, 'type-error-star', expression_list[0], message=m)
def unpack_tuple_to_dict(evaluator, types, exprlist): """ Unpacking tuple assignments in for statements and expr_stmts. """ if exprlist.type == 'name': return {exprlist.value: types} elif exprlist.type == 'atom' and exprlist.children[0] in '([': return unpack_tuple_to_dict(evaluator, types, exprlist.children[1]) elif exprlist.type in ('testlist', 'testlist_comp', 'exprlist', 'testlist_star_expr'): dct = {} parts = iter(exprlist.children[::2]) n = 0 for lazy_context in py__iter__(evaluator, types, exprlist): n += 1 try: part = next(parts) except StopIteration: # TODO this context is probably not right. analysis.add( next(iter(types)), 'value-error-too-many-values', part, message= "ValueError: too many values to unpack (expected %s)" % n) else: dct.update( unpack_tuple_to_dict(evaluator, lazy_context.infer(), part)) has_parts = next(parts, None) if types and has_parts is not None: # TODO this context is probably not right. analysis.add( next(iter(types)), 'value-error-too-few-values', has_parts, message="ValueError: need more than %s values to unpack" % n) return dct elif exprlist.type == 'power' or exprlist.type == 'atom_expr': # Something like ``arr[x], var = ...``. # This is something that is not yet supported, would also be difficult # to write into a dict. return {} elif exprlist.type == 'star_expr': # `a, *b, c = x` type unpackings # Currently we're not supporting them. return {} raise NotImplementedError
def follow(self, is_goto=False): if self._evaluator.recursion_detector.push_stmt(self._import): # check recursion return [] try: module = self._import.get_parent_until() import_path = self._import.path_for_name(self._name) importer = get_importer(self._evaluator, tuple(import_path), module, self._import.level) try: module, rest = importer.follow_file_system() except ModuleNotFound as e: analysis.add(self._evaluator, 'import-error', e.name) return [] if module is None: return [] #if self._import.is_nested() and not self.nested_resolve: # scopes = [NestedImportModule(module, self._import)] scopes = [module] # goto only accepts `Name` if is_goto and not rest: scopes = [s.name for s in scopes] # follow the rest of the import (not FS -> classes, functions) if rest: if is_goto: scopes = list( chain.from_iterable( self._evaluator.find_types( s, rest[0], is_goto=True) for s in scopes)) else: if self._import.type == 'import_from' \ or importer.str_import_path == ('os', 'path'): scopes = importer.follow_rest(scopes[0], rest) else: scopes = [] debug.dbg('after import: %s', scopes) if not scopes: analysis.add(self._evaluator, 'import-error', importer.import_path[-1]) finally: self._evaluator.recursion_detector.pop_stmt() return scopes
def py__iter__(evaluator, types, node=None): debug.dbg('py__iter__') type_iters = [] for typ in types: try: iter_method = typ.py__iter__ except AttributeError: if node is not None: analysis.add(evaluator, 'type-error-not-iterable', node, message="TypeError: '%s' object is not iterable" % typ) else: type_iters.append(iter_method()) #for result in iter_method(): #yield result for t in zip_longest(*type_iters, fillvalue=set()): yield unite(t)
def follow(self, is_goto=False): if self._evaluator.recursion_detector.push_stmt(self._import): # check recursion return [] try: module = self._import.get_parent_until() import_path = self._import.path_for_name(self._name) importer = get_importer(self._evaluator, tuple(import_path), module, self._import.level) try: module, rest = importer.follow_file_system() except ModuleNotFound as e: analysis.add(self._evaluator, 'import-error', e.name) return [] if module is None: return [] #if self._import.is_nested() and not self.nested_resolve: # scopes = [NestedImportModule(module, self._import)] scopes = [module] # goto only accepts `Name` if is_goto and not rest: scopes = [s.name for s in scopes] # follow the rest of the import (not FS -> classes, functions) if rest: if is_goto: scopes = list(chain.from_iterable( self._evaluator.find_types(s, rest[0], is_goto=True) for s in scopes)) else: if self._import.type == 'import_from' \ or importer.str_import_path == ('os', 'path'): scopes = importer.follow_rest(scopes[0], rest) else: scopes = [] debug.dbg('after import: %s', scopes) if not scopes: analysis.add(self._evaluator, 'import-error', importer.import_path[-1]) finally: self._evaluator.recursion_detector.pop_stmt() return scopes
def py__iter__(evaluator, types, node=None): debug.dbg('py__iter__') type_iters = [] for typ in types: try: iter_method = typ.py__iter__ except AttributeError: if node is not None: # TODO this context is probably not right. analysis.add(typ, 'type-error-not-iterable', node, message="TypeError: '%s' object is not iterable" % typ) else: type_iters.append(iter_method()) for lazy_contexts in zip_longest(*type_iters): yield context.get_merged_lazy_context( [l for l in lazy_contexts if l is not None] )
def py__iter__(evaluator, types, contextualized_node=None): debug.dbg('py__iter__') type_iters = [] for typ in types: try: iter_method = typ.py__iter__ except AttributeError: if contextualized_node is not None: analysis.add(contextualized_node.context, 'type-error-not-iterable', contextualized_node._node, message="TypeError: '%s' object is not iterable" % typ) else: type_iters.append(iter_method()) for lazy_contexts in zip_longest(*type_iters): yield context.get_merged_lazy_context( [l for l in lazy_contexts if l is not None])
def find(self, scopes, search_global=False): names = self.filter_name(scopes) types = self._names_to_types(names, search_global) if not names and not types \ and not (isinstance(self.name_str, pr.Name) and isinstance(self.name_str.parent.parent, pr.Param)): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if search_global: message = ("NameError: name '%s' is not defined." % self.name_str) analysis.add(self._evaluator, 'name-error', self.name_str, message) else: analysis.add_attribute_error(self._evaluator, self.scope, self.name_str) debug.dbg('finder._names_to_types: %s -> %s', names, types) return types
def find(self, scopes, resolve_decorator=True, search_global=False): names = self.filter_name(scopes) types = self._names_to_types(names, resolve_decorator) if not names and not types \ and not (isinstance(self.name_str, pr.Name) and isinstance(self.name_str.parent.parent, pr.Param)): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if search_global: message = ("NameError: name '%s' is not defined." % self.name_str) analysis.add(self._evaluator, 'name-error', self.name_str, message) else: analysis.add_attribute_error(self._evaluator, self.scope, self.name_str) debug.dbg('finder._names_to_types: %s -> %s', names, types) return self._resolve_descriptors(types)
def get_item(self, index_contexts, contextualized_node): from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.context.iterable import Slice, AbstractIterable result = ContextSet() for index in index_contexts: if isinstance(index, (CompiledObject, Slice)): index = index.obj if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)): # If the index is not clearly defined, we have to get all the # possiblities. if isinstance(self, AbstractIterable) and self.array_type == 'dict': result |= self.dict_values() else: result |= iterate_contexts(ContextSet(self)) continue # The actual getitem call. try: getitem = self.py__getitem__ except AttributeError: from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( contextualized_node.context, 'type-error-not-subscriptable', contextualized_node.node, message="TypeError: '%s' object is not subscriptable" % self) else: try: result |= getitem(index) except IndexError: result |= iterate_contexts(ContextSet(self)) except KeyError: # Must be a dict. Lists don't raise KeyErrors. result |= self.dict_values() return result
def builtins_isinstance(evaluator, objects, types, arguments): bool_results = set() for o in objects: cls = o.py__class__() try: mro_func = cls.py__mro__ except AttributeError: # This is temporary. Everything should have a class attribute in # Python?! Maybe we'll leave it here, because some numpy objects or # whatever might not. bool_results = set([True, False]) break mro = mro_func() for cls_or_tup in types: if cls_or_tup.is_class(): bool_results.add(cls_or_tup in mro) elif cls_or_tup.name.string_name == 'tuple' \ and cls_or_tup.get_root_context() == evaluator.builtins_module: # Check for tuples. classes = ContextSet.from_sets( lazy_context.infer() for lazy_context in cls_or_tup.iterate() ) bool_results.add(any(cls in mro for cls in classes)) else: _, lazy_context = list(arguments.unpack())[1] if isinstance(lazy_context, LazyTreeContext): node = lazy_context.data message = 'TypeError: isinstance() arg 2 must be a ' \ 'class, type, or tuple of classes and types, ' \ 'not %s.' % cls_or_tup analysis.add(lazy_context._context, 'type-error-isinstance', node, message) return ContextSet.from_iterable( compiled.builtin_from_name(evaluator, force_unicode(str(b))) for b in bool_results )
def get_item(self, index_contexts, contextualized_node): from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.context.iterable import Slice, AbstractIterable result = ContextSet() for index in index_contexts: if isinstance(index, (CompiledObject, Slice)): index = index.obj if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)): # If the index is not clearly defined, we have to get all the # possiblities. if isinstance(self, AbstractIterable) and self.array_type == 'dict': result |= self.dict_values() else: result |= iterate_contexts(ContextSet(self)) continue # The actual getitem call. try: getitem = self.py__getitem__ except AttributeError: from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( contextualized_node.context, 'type-error-not-subscriptable', contextualized_node.node, message="TypeError: '%s' object is not subscriptable" % self ) else: try: result |= getitem(index) except IndexError: result |= iterate_contexts(ContextSet(self)) except KeyError: # Must be a dict. Lists don't raise KeyErrors. result |= self.dict_values() return result
def builtins_isinstance(objects, types, arguments, evaluator): bool_results = set() for o in objects: cls = o.py__class__() try: cls.py__bases__ except AttributeError: # This is temporary. Everything should have a class attribute in # Python?! Maybe we'll leave it here, because some numpy objects or # whatever might not. bool_results = set([True, False]) break mro = list(cls.py__mro__()) for cls_or_tup in types: if cls_or_tup.is_class(): bool_results.add(cls_or_tup in mro) elif cls_or_tup.name.string_name == 'tuple' \ and cls_or_tup.get_root_context() == evaluator.builtins_module: # Check for tuples. classes = ContextSet.from_sets( lazy_context.infer() for lazy_context in cls_or_tup.iterate() ) bool_results.add(any(cls in mro for cls in classes)) else: _, lazy_context = list(arguments.unpack())[1] if isinstance(lazy_context, LazyTreeContext): node = lazy_context.data message = 'TypeError: isinstance() arg 2 must be a ' \ 'class, type, or tuple of classes and types, ' \ 'not %s.' % cls_or_tup analysis.add(lazy_context.context, 'type-error-isinstance', node, message) return ContextSet( compiled.builtin_from_name(evaluator, force_unicode(str(b))) for b in bool_results )
def _star_star_dict(evaluator, array, input_node, func): dct = defaultdict(lambda: []) from jedi.evaluate.representation import Instance if isinstance(array, Instance) and array.name.get_code() == 'dict': # For now ignore this case. In the future add proper iterators and just # make one call without crazy isinstance checks. return {} if isinstance(array, iterable.FakeDict): return array._dct elif isinstance(array, iterable.Array) and array.type == 'dict': # TODO bad call to non-public API for key_node, values in array._items(): for key in evaluator.eval_element(key_node): if precedence.is_string(key): dct[key.obj] += values else: if func is not None: m = "TypeError: %s argument after ** must be a mapping, not %s" \ % (func.name.value, array) analysis.add(evaluator, 'type-error-star-star', input_node, message=m) return dict(dct)
def _star_star_dict(evaluator, array, input_node, func): dct = defaultdict(lambda: []) from jedi.evaluate.representation import Instance if isinstance(array, Instance) and array.name.get_code() == 'dict': # For now ignore this case. In the future add proper iterators and just # make one call without crazy isinstance checks. return {} if isinstance(array, iterable.FakeDict): return array._dct elif isinstance(array, iterable.Array) and array.type == 'dict': # TODO bad call to non-public API id:610 gh:611 for key_node, values in array._items(): for key in evaluator.eval_element(key_node): if precedence.is_string(key): dct[key.obj] += values else: if func is not None: m = "TypeError: %s argument after ** must be a mapping, not %s" \ % (func.name.value, array) analysis.add(evaluator, 'type-error-star-star', input_node, message=m) return dict(dct)
def find(self, scopes, search_global=False): # TODO rename scopes to names_dicts names = self.filter_name(scopes) if self._found_predefined_if_name is not None: return self._found_predefined_if_name types = self._names_to_types(names, search_global) if ( not names and not types and not (isinstance(self.name_str, tree.Name) and isinstance(self.name_str.parent.parent, tree.Param)) ): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if search_global: message = "NameError: name '%s' is not defined." % self.name_str analysis.add(self._evaluator, "name-error", self.name_str, message) else: analysis.add_attribute_error(self._evaluator, self.scope, self.name_str) debug.dbg("finder._names_to_types: %s -> %s", names, types) return types
def _element_calculate(evaluator, left, operator, right): from jedi.evaluate import iterable, representation as er l_is_num = _is_number(left) r_is_num = _is_number(right) if operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Array) or _is_string(left): return [left] elif isinstance(right, iterable.Array) or _is_string(right): return [right] elif operator == '+': if l_is_num and r_is_num or _is_string(left) and _is_string(right): return [create(evaluator, left.obj + right.obj)] elif _is_tuple(left) and _is_tuple(right) or _is_list( left) and _is_list(right): return [iterable.MergedArray(evaluator, (left, right))] elif operator == '-': if l_is_num and r_is_num: return [create(evaluator, left.obj - right.obj)] elif operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return [left] def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, er.Instance) and obj.name in ('int', 'float') # Static analysis, one is a number, the other one is not. if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(evaluator, 'type-error-operation', operator, message % (left, right)) return [left, right]
def find(self, scopes, resolve_decorator=True, search_global=False): if unicode(self.name_str) == 'None': # Filter None, because it's really just a keyword, nobody wants to # access it. return [] names = self.filter_name(scopes) types = self._names_to_types(names, resolve_decorator) if not names and not types \ and not (isinstance(self.name_str, pr.NamePart) and isinstance(self.name_str.parent.parent, pr.Param)): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if search_global: message = ("NameError: name '%s' is not defined." % self.name_str) analysis.add(self._evaluator, 'name-error', self.name_str, message) else: analysis.add_attribute_error(self._evaluator, self.scope, self.name_str) debug.dbg('finder._names_to_types: %s -> %s', names, types) return self._resolve_descriptors(types)
def _add_error(evaluator, name, message=None): if hasattr(name, 'parent'): # Should be a name, not a string! analysis.add(evaluator, 'import-error', name, message)
def get_params(execution_context, var_args): result_params = [] param_dict = {} funcdef = execution_context.tree_node parent_context = execution_context.parent_context for param in funcdef.get_params(): param_dict[param.name.value] = param unpacked_va = list(var_args.unpack(funcdef)) var_arg_iterator = PushBackIterator(iter(unpacked_va)) non_matching_keys = defaultdict(lambda: []) keys_used = {} keys_only = False had_multiple_value_error = False for param in funcdef.get_params(): # The value and key can both be null. There, the defaults apply. # args / kwargs will just be empty arrays / dicts, respectively. # Wrong value count is just ignored. If you try to test cases that are # not allowed in Python, Jedi will maybe not show any completions. key, argument = next(var_arg_iterator, (None, None)) while key is not None: keys_only = True try: key_param = param_dict[key] except KeyError: non_matching_keys[key] = argument else: if key in keys_used: had_multiple_value_error = True m = ( "TypeError: %s() got multiple values for keyword argument '%s'." % (funcdef.name, key)) for node in var_args.get_calling_nodes(): analysis.add(parent_context, 'type-error-multiple-values', node, message=m) else: keys_used[key] = ExecutedParam(execution_context, key_param, argument) key, argument = next(var_arg_iterator, (None, None)) try: result_params.append(keys_used[param.name.value]) continue except KeyError: pass if param.star_count == 1: # *args param lazy_context_list = [] if argument is not None: lazy_context_list.append(argument) for key, argument in var_arg_iterator: # Iterate until a key argument is found. if key: var_arg_iterator.push_back((key, argument)) break lazy_context_list.append(argument) seq = iterable.FakeSequence(execution_context.evaluator, u'tuple', lazy_context_list) result_arg = LazyKnownContext(seq) elif param.star_count == 2: # **kwargs param dct = iterable.FakeDict(execution_context.evaluator, dict(non_matching_keys)) result_arg = LazyKnownContext(dct) non_matching_keys = {} else: # normal param if argument is None: # No value: Return an empty container if param.default is None: result_arg = LazyUnknownContext() if not keys_only: for node in var_args.get_calling_nodes(): m = _error_argument_count(funcdef, len(unpacked_va)) analysis.add(parent_context, 'type-error-too-few-arguments', node, message=m) else: result_arg = LazyTreeContext(parent_context, param.default) else: result_arg = argument result_params.append( ExecutedParam(execution_context, param, result_arg)) if not isinstance(result_arg, LazyUnknownContext): keys_used[param.name.value] = result_params[-1] if keys_only: # All arguments should be handed over to the next function. It's not # about the values inside, it's about the names. Jedi needs to now that # there's nothing to find for certain names. for k in set(param_dict) - set(keys_used): param = param_dict[k] if not (non_matching_keys or had_multiple_value_error or param.star_count or param.default): # add a warning only if there's not another one. for node in var_args.get_calling_nodes(): m = _error_argument_count(funcdef, len(unpacked_va)) analysis.add(parent_context, 'type-error-too-few-arguments', node, message=m) for key, lazy_context in non_matching_keys.items(): m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (funcdef.name, key) _add_argument_issue(parent_context, 'type-error-keyword-argument', lazy_context, message=m) remaining_arguments = list(var_arg_iterator) if remaining_arguments: m = _error_argument_count(funcdef, len(unpacked_va)) # Just report an error for the first param that is not needed (like # cPython). first_key, lazy_context = remaining_arguments[0] if var_args.get_calling_nodes(): # There might not be a valid calling node so check for that first. _add_argument_issue(parent_context, 'type-error-too-many-arguments', lazy_context, message=m) return result_params
def _add_argument_issue(parent_context, error_name, lazy_context, message): if isinstance(lazy_context, LazyTreeContext): node = lazy_context.data if node.parent.type == 'argument': node = node.parent analysis.add(parent_context, error_name, node, message)
def get_params(evaluator, func, var_args): param_names = [] param_dict = {} for param in func.params: param_dict[str(param.get_name())] = param unpacked_va = list(var_args.unpack(func)) from jedi.evaluate.representation import InstanceElement if isinstance(func, InstanceElement): # Include self at this place. unpacked_va.insert( 0, (None, [iterable.AlreadyEvaluated([func.instance])])) var_arg_iterator = common.PushBackIterator(iter(unpacked_va)) non_matching_keys = defaultdict(lambda: []) keys_used = {} keys_only = False had_multiple_value_error = False for param in func.params: # The value and key can both be null. There, the defaults apply. # args / kwargs will just be empty arrays / dicts, respectively. # Wrong value count is just ignored. If you try to test cases that are # not allowed in Python, Jedi will maybe not show any completions. default = [] if param.default is None else [param.default] key, va_values = next(var_arg_iterator, (None, default)) while key is not None: keys_only = True k = unicode(key) try: key_param = param_dict[unicode(key)] except KeyError: non_matching_keys[key] += va_values else: param_names.append( ExecutedParam(key_param, var_args, va_values).name) if k in keys_used: had_multiple_value_error = True m = ( "TypeError: %s() got multiple values for keyword argument '%s'." % (func.name, k)) calling_va = _get_calling_var_args(evaluator, var_args) if calling_va is not None: analysis.add(evaluator, 'type-error-multiple-values', calling_va, message=m) else: try: keys_used[k] = param_names[-1] except IndexError: # TODO this is wrong stupid and whatever. pass key, va_values = next(var_arg_iterator, (None, ())) values = [] if param.stars == 1: # *args param lst_values = [iterable.MergedNodes(va_values)] if va_values else [] for key, va_values in var_arg_iterator: # Iterate until a key argument is found. if key: var_arg_iterator.push_back((key, va_values)) break if va_values: lst_values.append(iterable.MergedNodes(va_values)) seq = iterable.FakeSequence(evaluator, lst_values, 'tuple') values = [iterable.AlreadyEvaluated([seq])] elif param.stars == 2: # **kwargs param dct = iterable.FakeDict(evaluator, dict(non_matching_keys)) values = [iterable.AlreadyEvaluated([dct])] non_matching_keys = {} else: # normal param if va_values: values = va_values else: # No value: Return an empty container values = [] if not keys_only: calling_va = var_args.get_calling_var_args() if calling_va is not None: m = _error_argument_count(func, len(unpacked_va)) analysis.add(evaluator, 'type-error-too-few-arguments', calling_va, message=m) # Now add to result if it's not one of the previously covered cases. if (not keys_only or param.stars == 2): param_names.append(ExecutedParam(param, var_args, values).name) keys_used[unicode(param.get_name())] = param_names[-1] if keys_only: # All arguments should be handed over to the next function. It's not # about the values inside, it's about the names. Jedi needs to now that # there's nothing to find for certain names. for k in set(param_dict) - set(keys_used): param = param_dict[k] values = [] if param.default is None else [param.default] param_names.append(ExecutedParam(param, var_args, values).name) if not (non_matching_keys or had_multiple_value_error or param.stars or param.default): # add a warning only if there's not another one. calling_va = _get_calling_var_args(evaluator, var_args) if calling_va is not None: m = _error_argument_count(func, len(unpacked_va)) analysis.add(evaluator, 'type-error-too-few-arguments', calling_va, message=m) for key, va_values in non_matching_keys.items(): m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (func.name, key) for value in va_values: analysis.add(evaluator, 'type-error-keyword-argument', value.parent, message=m) remaining_params = list(var_arg_iterator) if remaining_params: m = _error_argument_count(func, len(unpacked_va)) # Just report an error for the first param that is not needed (like # cPython). first_key, first_values = remaining_params[0] for v in first_values: if first_key is not None: # Is a keyword argument, return the whole thing instead of just # the value node. v = v.parent try: non_kw_param = keys_used[first_key] except KeyError: pass else: origin_args = non_kw_param.parent.var_args.argument_node # TODO calculate the var_args tree and check if it's in # the tree (if not continue). # print('\t\tnonkw', non_kw_param.parent.var_args.argument_node, ) if origin_args not in [ f.parent.parent for f in first_values ]: continue analysis.add(evaluator, 'type-error-too-many-arguments', v, message=m) return param_names
def get_params(execution_context, var_args): result_params = [] param_dict = {} funcdef = execution_context.tree_node parent_context = execution_context.parent_context for param in funcdef.params: param_dict[param.name.value] = param unpacked_va = list(var_args.unpack(funcdef)) var_arg_iterator = common.PushBackIterator(iter(unpacked_va)) non_matching_keys = defaultdict(lambda: []) keys_used = {} keys_only = False had_multiple_value_error = False for param in funcdef.params: # The value and key can both be null. There, the defaults apply. # args / kwargs will just be empty arrays / dicts, respectively. # Wrong value count is just ignored. If you try to test cases that are # not allowed in Python, Jedi will maybe not show any completions. key, argument = next(var_arg_iterator, (None, None)) while key is not None: keys_only = True try: key_param = param_dict[key] except KeyError: non_matching_keys[key] = argument else: if key in keys_used: had_multiple_value_error = True m = ("TypeError: %s() got multiple values for keyword argument '%s'." % (funcdef.name, key)) for node in var_args.get_calling_nodes(): analysis.add(parent_context, 'type-error-multiple-values', node, message=m) else: keys_used[key] = ExecutedParam(execution_context, key_param, argument) key, argument = next(var_arg_iterator, (None, None)) try: result_params.append(keys_used[param.name.value]) continue except KeyError: pass if param.star_count == 1: # *args param lazy_context_list = [] if argument is not None: lazy_context_list.append(argument) for key, argument in var_arg_iterator: # Iterate until a key argument is found. if key: var_arg_iterator.push_back((key, argument)) break lazy_context_list.append(argument) seq = iterable.FakeSequence(execution_context.evaluator, 'tuple', lazy_context_list) result_arg = context.LazyKnownContext(seq) elif param.star_count == 2: # **kwargs param dct = iterable.FakeDict(execution_context.evaluator, dict(non_matching_keys)) result_arg = context.LazyKnownContext(dct) non_matching_keys = {} else: # normal param if argument is None: # No value: Return an empty container if param.default is None: result_arg = context.LazyUnknownContext() if not keys_only: for node in var_args.get_calling_nodes(): m = _error_argument_count(funcdef, len(unpacked_va)) analysis.add(parent_context, 'type-error-too-few-arguments', node, message=m) else: result_arg = context.LazyTreeContext(parent_context, param.default) else: result_arg = argument result_params.append(ExecutedParam(execution_context, param, result_arg)) if not isinstance(result_arg, context.LazyUnknownContext): keys_used[param.name.value] = result_params[-1] if keys_only: # All arguments should be handed over to the next function. It's not # about the values inside, it's about the names. Jedi needs to now that # there's nothing to find for certain names. for k in set(param_dict) - set(keys_used): param = param_dict[k] if not (non_matching_keys or had_multiple_value_error or param.star_count or param.default): # add a warning only if there's not another one. for node in var_args.get_calling_nodes(): m = _error_argument_count(funcdef, len(unpacked_va)) analysis.add(parent_context, 'type-error-too-few-arguments', node, message=m) for key, lazy_context in non_matching_keys.items(): m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (funcdef.name, key) add_argument_issue( parent_context, 'type-error-keyword-argument', lazy_context, message=m ) remaining_arguments = list(var_arg_iterator) if remaining_arguments: m = _error_argument_count(funcdef, len(unpacked_va)) # Just report an error for the first param that is not needed (like # cPython). first_key, lazy_context = remaining_arguments[0] if var_args.get_calling_nodes(): # There might not be a valid calling node so check for that first. add_argument_issue(parent_context, 'type-error-too-many-arguments', lazy_context, message=m) return result_params
def add_argument_issue(parent_context, error_name, lazy_context, message): if isinstance(lazy_context, context.LazyTreeContext): node = lazy_context.data if node.parent.type == 'argument': node = node.parent analysis.add(parent_context, error_name, node, message)
def get_index_types(self, evaluator, index_array): #debug.warning('Tried to get array access on a generator: %s', self) analysis.add(self._evaluator, 'type-error-generator', index_array) return []
def _eval_comparison_part(evaluator, context, left, operator, right): l_is_num = is_number(left) r_is_num = is_number(right) if isinstance(operator, unicode): str_operator = operator else: str_operator = force_unicode(str(operator.value)) if str_operator == '*': # for iterables, ignore * operations if isinstance(left, iterable.Sequence) or is_string(left): return ContextSet([left]) elif isinstance(right, iterable.Sequence) or is_string(right): return ContextSet([right]) elif str_operator == '+': if l_is_num and r_is_num or is_string(left) and is_string(right): return ContextSet([left.execute_operation(right, str_operator)]) elif _is_tuple(left) and _is_tuple(right) or _is_list( left) and _is_list(right): return ContextSet([iterable.MergedArray(evaluator, (left, right))]) elif str_operator == '-': if l_is_num and r_is_num: return ContextSet([left.execute_operation(right, str_operator)]) elif str_operator == '%': # With strings and numbers the left type typically remains. Except for # `int() % float()`. return ContextSet([left]) elif str_operator in COMPARISON_OPERATORS: if left.is_compiled() and right.is_compiled(): # Possible, because the return is not an option. Just compare. try: return ContextSet( [left.execute_operation(right, str_operator)]) except TypeError: # Could be True or False. pass else: if str_operator in ('is', '!=', '==', 'is not'): operation = COMPARISON_OPERATORS[str_operator] bool_ = operation(left, right) return ContextSet([_bool_to_context(evaluator, bool_)]) if isinstance(left, VersionInfo): version_info = _get_tuple_ints(right) if version_info is not None: bool_result = compiled.access.COMPARISON_OPERATORS[ operator](evaluator.environment.version_info, tuple(version_info)) return ContextSet( [_bool_to_context(evaluator, bool_result)]) return ContextSet([ _bool_to_context(evaluator, True), _bool_to_context(evaluator, False) ]) elif str_operator == 'in': return NO_CONTEXTS def check(obj): """Checks if a Jedi object is either a float or an int.""" return isinstance(obj, TreeInstance) and \ obj.name.string_name in ('int', 'float') # Static analysis, one is a number, the other one is not. if str_operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" analysis.add(context, 'type-error-operation', operator, message % (left, right)) result = ContextSet([left, right]) debug.dbg('Used operator %s resulting in %s', operator, result) return result
def _add_error(context, name, message=None): # Should be a name, not a string! if hasattr(name, 'parent'): analysis.add(context, 'import-error', name, message)