def check_tuple_assignments(name, value_set): """ Checks if tuples are assigned. """ lazy_value = None for index, node in name.assignment_indexes(): cn = ContextualizedNode(name.parent_context, node) iterated = value_set.iterate(cn) if isinstance(index, slice): # For no star unpacking is not possible. return NO_VALUES i = 0 while i <= index: try: lazy_value = next(iterated) except StopIteration: # We could do this with the default param in next. But this # would allow this loop to run for a very long time if the # index number is high. Therefore break if the loop is # finished. return NO_VALUES else: i += lazy_value.max value_set = lazy_value.infer() return value_set
def infer(self, context, name): def_ = name.get_definition(import_name_always=True) if def_ is not None: type_ = def_.type is_classdef = type_ == 'classdef' if is_classdef or type_ == 'funcdef': if is_classdef: c = ClassValue(self, context, name.parent) else: c = FunctionValue.from_context(context, name.parent) return ValueSet([c]) if type_ == 'expr_stmt': is_simple_name = name.parent.type not in ('power', 'trailer') if is_simple_name: return infer_expr_stmt(context, def_, name) if type_ == 'for_stmt': container_types = context.infer_node(def_.children[3]) cn = ContextualizedNode(context, def_.children[3]) for_types = iterate_values(container_types, cn) n = TreeNameDefinition(context, name) return check_tuple_assignments(n, for_types) if type_ in ('import_from', 'import_name'): return imports.infer_import(context, name) if type_ == 'with_stmt': return tree_name_to_values(self, context, name) elif type_ == 'param': return context.py__getattribute__(name.value, position=name.end_pos) else: result = follow_error_node_imports_if_possible(context, name) if result is not None: return result return helpers.infer_call_of_leaf(context, name)
def _nested(self, comp_fors, parent_context=None): comp_for = comp_fors[0] is_async = comp_for.parent.type == 'comp_for' input_node = comp_for.children[3] parent_context = parent_context or self._defining_context input_types = parent_context.infer_node(input_node) cn = ContextualizedNode(parent_context, input_node) iterated = input_types.iterate(cn, is_async=is_async) exprlist = comp_for.children[1] for i, lazy_value in enumerate(iterated): types = lazy_value.infer() dct = unpack_tuple_to_dict(parent_context, types, exprlist) context = self._get_comp_for_context( parent_context, comp_for, ) with context.predefine_names(comp_for, dct): try: for result in self._nested(comp_fors[1:], context): yield result except IndexError: iterated = context.infer_node(self._entry_node) if self.array_type == 'dict': yield iterated, context.infer_node(self._value_node) else: yield iterated
def get_yield_lazy_values(self, is_async=False): # TODO: if is_async, wrap yield statements in Awaitable/async_generator_asend for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef', 'while_stmt', 'if_stmt')) for y in get_yield_exprs(self.inference_state, self.tree_node)] # Calculate if the yields are placed within the same for loop. yields_order = [] last_for_stmt = None for yield_, for_stmt in for_parents: # For really simple for loops we can predict the order. Otherwise # we just ignore it. parent = for_stmt.parent if parent.type == 'suite': parent = parent.parent if for_stmt.type == 'for_stmt' and parent == self.tree_node \ and parser_utils.for_stmt_defines_one_name(for_stmt): # Simplicity for now. if for_stmt == last_for_stmt: yields_order[-1][1].append(yield_) else: yields_order.append((for_stmt, [yield_])) elif for_stmt == self.tree_node: yields_order.append((None, [yield_])) else: types = self.get_return_values(check_yields=True) if types: yield LazyKnownValues(types, min=0, max=float('inf')) return last_for_stmt = for_stmt for for_stmt, yields in yields_order: if for_stmt is None: # No for_stmt, just normal yields. for yield_ in yields: yield from self._get_yield_lazy_value(yield_) else: input_node = for_stmt.get_testlist() cn = ContextualizedNode(self, input_node) ordered = cn.infer().iterate(cn) ordered = list(ordered) for lazy_value in ordered: dct = {str(for_stmt.children[1].value): lazy_value.infer()} with self.predefine_names(for_stmt, dct): for yield_in_same_for_stmt in yields: yield from self._get_yield_lazy_value(yield_in_same_for_stmt)
def _paths_from_assignment(module_context, expr_stmt): """ Extracts the assigned strings from an assignment that looks as follows:: sys.path[0:0] = ['module/path', 'another/module/path'] This function is in general pretty tolerant (and therefore 'buggy'). However, it's not a big issue usually to add more paths to Jedi's sys_path, because it will only affect Jedi in very random situations and by adding more paths than necessary, it usually benefits the general user. """ for assignee, operator in zip(expr_stmt.children[::2], expr_stmt.children[1::2]): try: assert operator in ['=', '+='] assert assignee.type in ('power', 'atom_expr') and \ len(assignee.children) > 1 c = assignee.children assert c[0].type == 'name' and c[0].value == 'sys' trailer = c[1] assert trailer.children[0] == '.' and trailer.children[ 1].value == 'path' # TODO Essentially we're not checking details on sys.path # manipulation. Both assigment of the sys.path and changing/adding # parts of the sys.path are the same: They get added to the end of # the current sys.path. """ execution = c[2] assert execution.children[0] == '[' subscript = execution.children[1] assert subscript.type == 'subscript' assert ':' in subscript.children """ except AssertionError: continue cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt) for lazy_value in cn.infer().iterate(cn): for value in lazy_value.infer(): if is_string(value): abs_path = _abs_path(module_context, value.get_safe_value()) if abs_path is not None: yield abs_path
def builtins_reversed(sequences, value, arguments): # While we could do without this variable (just by using sequences), we # want static analysis to work well. Therefore we need to generated the # values again. key, lazy_value = next(arguments.unpack()) cn = None if isinstance(lazy_value, LazyTreeValue): cn = ContextualizedNode(lazy_value.context, lazy_value.data) ordered = list(sequences.iterate(cn)) # Repack iterator values and then run it the normal way. This is # necessary, because `reversed` is a function and autocompletion # would fail in certain cases like `reversed(x).__iter__` if we # just returned the result directly. seq, = value.inference_state.typing_module.py__getattribute__('Iterator').execute_with_values() return ValueSet([ReversedObject(seq, list(reversed(ordered)))])
def unpack_tuple_to_dict(context, 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(context, 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_value in types.iterate(ContextualizedNode(context, exprlist)): n += 1 try: part = next(parts) except StopIteration: analysis.add( context, 'value-error-too-many-values', part, message= "ValueError: too many values to unpack (expected %s)" % n) else: dct.update( unpack_tuple_to_dict(context, lazy_value.infer(), part)) has_parts = next(parts, None) if types and has_parts is not None: analysis.add( context, '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 infer_trailer(context, atom_values, trailer): trailer_op, node = trailer.children[:2] if node == ')': # `arglist` is optional. node = None if trailer_op == '[': trailer_op, node, _ = trailer.children return atom_values.get_item(_infer_subscript_list(context, node), ContextualizedNode(context, trailer)) else: debug.dbg('infer_trailer: %s in %s', trailer, atom_values) if trailer_op == '.': return atom_values.py__getattribute__(name_context=context, name_or_str=node) else: assert trailer_op == '(', 'trailer_op is actually %s' % trailer_op args = arguments.TreeArguments(context.inference_state, context, node, trailer) return atom_values.execute(args)
def tree_name_to_values(inference_state, context, tree_name): value_set = NO_VALUES module_node = context.get_root_context().tree_node # First check for annotations, like: `foo: int = 3` if module_node is not None: names = module_node.get_used_names().get(tree_name.value, []) found_annotation = False for name in names: expr_stmt = name.parent if expr_stmt.type == "expr_stmt" and expr_stmt.children[ 1].type == "annassign": correct_scope = parser_utils.get_parent_scope( name) == context.tree_node if correct_scope: found_annotation = True value_set |= annotation.infer_annotation( context, expr_stmt.children[1].children[1]).execute_annotation( ) if found_annotation: return value_set types = [] node = tree_name.get_definition(import_name_always=True, include_setitem=True) if node is None: node = tree_name.parent if node.type == 'global_stmt': c = context.create_context(tree_name) if c.is_module(): # In case we are already part of the module, there is no point # in looking up the global statement anymore, because it's not # valid at that point anyway. return NO_VALUES # For global_stmt lookups, we only need the first possible scope, # which means the function itself. filter = next(c.get_filters()) names = filter.get(tree_name.value) return ValueSet.from_sets(name.infer() for name in names) elif node.type not in ('import_from', 'import_name'): c = context.create_context(tree_name) return infer_atom(c, tree_name) typ = node.type if typ == 'for_stmt': types = annotation.find_type_from_comment_hint_for( context, node, tree_name) if types: return types if typ == 'with_stmt': types = annotation.find_type_from_comment_hint_with( context, node, tree_name) if types: return types if typ in ('for_stmt', 'comp_for', 'sync_comp_for'): try: types = context.predefined_names[node][tree_name.value] except KeyError: cn = ContextualizedNode(context, node.children[3]) for_types = iterate_values( cn.infer(), contextualized_node=cn, is_async=node.parent.type == 'async_stmt', ) n = TreeNameDefinition(context, tree_name) types = check_tuple_assignments(n, for_types) elif typ == 'expr_stmt': types = infer_expr_stmt(context, node, tree_name) elif typ == 'with_stmt': value_managers = context.infer_node( node.get_test_node_from_name(tree_name)) if node.parent.type == 'async_stmt': # In the case of `async with` statements, we need to # first get the coroutine from the `__aenter__` method, # then "unwrap" via the `__await__` method enter_methods = value_managers.py__getattribute__('__aenter__') coro = enter_methods.execute_with_values() return coro.py__await__().py__stop_iteration_returns() enter_methods = value_managers.py__getattribute__('__enter__') return enter_methods.execute_with_values() elif typ in ('import_from', 'import_name'): types = imports.infer_import(context, tree_name) elif typ in ('funcdef', 'classdef'): types = _apply_decorators(context, node) elif typ == 'try_stmt': # TODO an exception can also be a tuple. Check for those. # TODO check for types that are not classes and add it to # the static analysis report. exceptions = context.infer_node( tree_name.get_previous_sibling().get_previous_sibling()) types = exceptions.execute_with_values() elif typ == 'param': types = NO_VALUES elif typ == 'del_stmt': types = NO_VALUES elif typ == 'namedexpr_test': types = infer_node(context, node) else: raise ValueError("Should not happen. type: %s" % typ) return types
def _infer_expr_stmt(context, stmt, seek_name=None): """ The starting point of the completion. A statement always owns a call list, which are the calls, that a statement does. In case multiple names are defined in the statement, `seek_name` returns the result for this name. expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) annassign: ':' test ['=' test] augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=') :param stmt: A `tree.ExprStmt`. """ def check_setitem(stmt): atom_expr = stmt.children[0] if atom_expr.type not in ('atom_expr', 'power'): return False, None name = atom_expr.children[0] if name.type != 'name' or len(atom_expr.children) != 2: return False, None trailer = atom_expr.children[-1] return trailer.children[0] == '[', trailer.children[1] debug.dbg('infer_expr_stmt %s (%s)', stmt, seek_name) rhs = stmt.get_rhs() value_set = context.infer_node(rhs) if seek_name: n = TreeNameDefinition(context, seek_name) value_set = check_tuple_assignments(n, value_set) first_operator = next(stmt.yield_operators(), None) is_setitem, subscriptlist = check_setitem(stmt) is_annassign = first_operator not in ( '=', None) and first_operator.type == 'operator' if is_annassign or is_setitem: # `=` is always the last character in aug assignments -> -1 name = stmt.get_defined_names(include_setitem=True)[0].value left_values = context.py__getattribute__(name, position=stmt.start_pos) if is_setitem: def to_mod(v): c = ContextualizedSubscriptListNode(context, subscriptlist) if v.array_type == 'dict': return DictModification(v, value_set, c) elif v.array_type == 'list': return ListModification(v, value_set, c) return v value_set = ValueSet(to_mod(v) for v in left_values) else: operator = copy.copy(first_operator) operator.value = operator.value[:-1] for_stmt = tree.search_ancestor(stmt, 'for_stmt') if for_stmt is not None and for_stmt.type == 'for_stmt' and value_set \ and parser_utils.for_stmt_defines_one_name(for_stmt): # Iterate through result and add the values, that's possible # only in for loops without clutter, because they are # predictable. Also only do it, if the variable is not a tuple. node = for_stmt.get_testlist() cn = ContextualizedNode(context, node) ordered = list(cn.infer().iterate(cn)) for lazy_value in ordered: dct = {for_stmt.children[1].value: lazy_value.infer()} with context.predefine_names(for_stmt, dct): t = context.infer_node(rhs) left_values = _infer_comparison( context, left_values, operator, t) value_set = left_values else: value_set = _infer_comparison(context, left_values, operator, value_set) debug.dbg('infer_expr_stmt result %s', value_set) return value_set