def with_assigned_stmts(self, node=None, context=None, asspath=None): """Infer names and other nodes from a *with* statement. This enables only inference for name binding in a *with* statement. For instance, in the following code, inferring `func` will return the `ContextManager` class, not whatever ``__enter__`` returns. We are doing this intentionally, because we consider that the context manager result is whatever __enter__ returns and what it is binded using the ``as`` keyword. class ContextManager(object): def __enter__(self): return 42 with ContextManager() as f: pass # ContextManager().infer() will return ContextManager # f.infer() will return 42. Arguments: self: nodes.With node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. context: TODO asspath: TODO """ mgr = next(mgr for (mgr, vars) in self.items if vars == node) if asspath is None: for result in _infer_context_manager(self, mgr, context): yield result else: for result in _infer_context_manager(self, mgr, context): # Walk the asspath and get the item at the final index. obj = result for index in asspath: if not hasattr(obj, 'elts'): raise exceptions.InferenceError( 'Wrong type ({targets!r}) for {node!r} assignment', node=self, targets=node, assign_path=asspath, context=context) try: obj = obj.elts[index] except IndexError: util.reraise( exceptions.InferenceError( 'Tried to infer a nonexistent target with index {index} ' 'in {node!r}.', node=self, targets=node, assign_path=asspath, context=context)) yield obj # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. raise StopIteration( dict(node=self, unknown=node, assign_path=asspath, context=context))
def infer_global(self, context=None): if context.lookupname is None: raise exceptions.InferenceError(node=self, context=context) try: return bases._infer_stmts(self.root().getattr(context.lookupname), context) except exceptions.AttributeInferenceError as error: util.reraise(exceptions.InferenceError( error.message, target=self, attribute=context.lookupname, context=context))
def infer_subscript(self, context=None): """Inference for subscripts We're understanding if the index is a Const or a slice, passing the result of inference to the value's `getitem` method, which should handle each supported index type accordingly. """ found_one = False for value in self.value.infer(context): if value is util.Uninferable: yield util.Uninferable return None for index in self.slice.infer(context): if index is util.Uninferable: yield util.Uninferable return None # Try to deduce the index value. index_value = _SUBSCRIPT_SENTINEL if value.__class__ == bases.Instance: index_value = index elif index.__class__ == bases.Instance: instance_as_index = helpers.class_instance_as_index(index) if instance_as_index: index_value = instance_as_index else: index_value = index if index_value is _SUBSCRIPT_SENTINEL: raise exceptions.InferenceError(node=self, context=context) try: assigned = value.getitem(index_value, context) except ( exceptions.AstroidTypeError, exceptions.AstroidIndexError, exceptions.AttributeInferenceError, AttributeError, ) as exc: raise exceptions.InferenceError(node=self, context=context) from exc # Prevent inferring if the inferred subscript # is the same as the original subscripted object. if self is assigned or assigned is util.Uninferable: yield util.Uninferable return None yield from assigned.infer(context) found_one = True if found_one: return dict(node=self, context=context) return None
def infer_subscript(self, context=None): """Inference for subscripts We're understanding if the index is a Const or a slice, passing the result of inference to the value's `getitem` method, which should handle each supported index type accordingly. """ value = next(self.value.infer(context)) if value is util.Uninferable: yield util.Uninferable return index = next(self.slice.infer(context)) if index is util.Uninferable: yield util.Uninferable return # Try to deduce the index value. index_value = _SUBSCRIPT_SENTINEL if value.__class__ == bases.Instance: index_value = index else: if index.__class__ == bases.Instance: instance_as_index = helpers.class_instance_as_index(index) if instance_as_index: index_value = instance_as_index else: index_value = index if index_value is _SUBSCRIPT_SENTINEL: raise exceptions.InferenceError(node=self, context=context) try: assigned = value.getitem(index_value, context) except (exceptions.AstroidTypeError, exceptions.AstroidIndexError, exceptions.AttributeInferenceError, AttributeError) as exc: util.reraise(exceptions.InferenceError(node=self, error=exc, context=context)) # Prevent inferring if the inferred subscript # is the same as the original subscripted object. if self is assigned or assigned is util.Uninferable: yield util.Uninferable return for inferred in assigned.infer(context): yield inferred # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. raise StopIteration(dict(node=self, context=context))
def starred_assigned_stmts(self, node=None, context=None, asspath=None): stmt = self.statement() if not isinstance(stmt, (nodes.Assign, nodes.For)): raise exceptions.InferenceError() if isinstance(stmt, nodes.Assign): value = stmt.value lhs = stmt.targets[0] if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: # Too many starred arguments in the expression. raise exceptions.InferenceError() if context is None: context = contextmod.InferenceContext() try: rhs = next(value.infer(context)) except exceptions.InferenceError: yield util.YES return if rhs is util.YES or not hasattr(rhs, 'elts'): # Not interested in inferred values without elts. yield util.YES return elts = collections.deque(rhs.elts[:]) if len(lhs.elts) > len(rhs.elts): # a, *b, c = (1, 2) raise exceptions.InferenceError() # Unpack iteratively the values from the rhs of the assignment, # until the find the starred node. What will remain will # be the list of values which the Starred node will represent # This is done in two steps, from left to right to remove # anything before the starred node and from right to left # to remvoe anything after the starred node. for index, node in enumerate(lhs.elts): if not isinstance(node, nodes.Starred): elts.popleft() continue lhs_elts = collections.deque(reversed(lhs.elts[index:])) for node in lhs_elts: if not isinstance(node, nodes.Starred): elts.pop() continue # We're done packed = nodes.List() packed.elts = elts packed.parent = self yield packed break
def infer_import(self, context=None, asname=True): """infer an Import node: return the imported module/object""" name = context.lookupname if name is None: raise exceptions.InferenceError(node=self, context=context) try: if asname: yield self.do_import_module(self.real_name(name)) else: yield self.do_import_module(name) except exceptions.AstroidBuildingError as exc: raise exceptions.InferenceError(node=self, context=context) from exc
def raise_if_nothing_inferred(func, instance, args, kwargs): generator = func(*args, **kwargs) try: yield next(generator) except StopIteration as error: # generator is empty if error.args: # pylint: disable=not-a-mapping raise exceptions.InferenceError(**error.args[0]) raise exceptions.InferenceError( "StopIteration raised without any error information.") yield from generator
def object_len(node, context=None): """Infer length of given node object :param Union[nodes.ClassDef, nodes.Instance] node: :param node: Node to infer length of :raises AstroidTypeError: If an invalid node is returned from __len__ method or no __len__ method exists :raises InferenceError: If the given node cannot be inferred or if multiple nodes are inferred :rtype int: Integer length of node """ # pylint: disable=import-outside-toplevel; circular import from astroid.objects import FrozenSet inferred_node = safe_infer(node, context=context) if inferred_node is None or inferred_node is util.Uninferable: raise exceptions.InferenceError(node=node) if isinstance(inferred_node, nodes.Const) and isinstance( inferred_node.value, (bytes, str)): return len(inferred_node.value) if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)): return len(inferred_node.elts) if isinstance(inferred_node, nodes.Dict): return len(inferred_node.items) node_type = object_type(inferred_node, context=context) if not node_type: raise exceptions.InferenceError(node=node) try: len_call = next(node_type.igetattr("__len__", context=context)) except exceptions.AttributeInferenceError: raise exceptions.AstroidTypeError( "object of type '{}' has no len()".format(node_type.pytype())) result_of_len = next(len_call.infer_call_result(node, context)) if (isinstance(result_of_len, nodes.Const) and result_of_len.pytype() == "builtins.int"): return result_of_len.value if isinstance( result_of_len, bases.Instance) and result_of_len.is_subtype_of("builtins.int"): # Fake a result as we don't know the arguments of the instance call. return 0 raise exceptions.AstroidTypeError( "'{}' object cannot be interpreted as an integer".format( result_of_len))
def raise_if_nothing_inferred(func, instance, args, kwargs): inferred = False try: generator = func(*args, **kwargs) while True: yield next(generator) inferred = True except StopIteration as error: if not inferred: if error.args: # pylint: disable=not-a-mapping raise exceptions.InferenceError(**error.args[0]) else: raise exceptions.InferenceError( 'StopIteration raised without any error information.')
def _infer_seq(node, context=None): """Infer all values based on _BaseContainer.elts""" values = [] for elt in node.elts: if isinstance(elt, nodes.Starred): starred = helpers.safe_infer(elt.value, context) if starred in (None, util.Uninferable): raise exceptions.InferenceError(node=node, context=context) if not hasattr(starred, 'elts'): raise exceptions.InferenceError(node=node, context=context) values.extend(_infer_seq(starred)) else: values.append(elt) return values
def infer_import_from(self, context=None, asname=True): """infer a ImportFrom node: return the imported module/object""" name = context.lookupname if name is None: raise exceptions.InferenceError() if asname: name = self.real_name(name) module = self.do_import_module() try: context = contextmod.copy_context(context) context.lookupname = name stmts = module.getattr(name, ignore_locals=module is self.root()) return bases._infer_stmts(stmts, context) except exceptions.NotFoundError: raise exceptions.InferenceError(name)
def igetattr(self, name, context=None): """inferred getattr""" if not context: context = contextmod.InferenceContext() try: # avoid recursively inferring the same attr on the same class if context.push((self._proxied, name)): return # XXX frame should be self._proxied, or not ? get_attr = self.getattr(name, context, lookupclass=False) for stmt in _infer_stmts(self._wrap_attr(get_attr, context), context, frame=self): yield stmt except exceptions.AttributeInferenceError: try: # fallback to class.igetattr since it has some logic to handle # descriptors attrs = self._proxied.igetattr(name, context, class_context=False) for stmt in self._wrap_attr(attrs, context): yield stmt except exceptions.AttributeInferenceError as error: util.reraise(exceptions.InferenceError(**vars(error)))
def infer_call_result(self, caller, context=None, context_lookup=None): if len(caller.args) != 2: raise exceptions.InferenceError( "Invalid arguments for descriptor binding", target=self, context=context) context = contextmod.copy_context(context) cls = next(caller.args[0].infer(context=context)) # Rebuild the original value, but with the parent set as the # class where it will be bound. new_func = func.__class__(name=func.name, doc=func.doc, lineno=func.lineno, col_offset=func.col_offset, parent=cls) # pylint: disable=no-member new_func.postinit(func.args, func.body, func.decorators, func.returns) # Build a proper bound method that points to our newly built function. proxy = bases.UnboundMethod(new_func) yield bases.BoundMethod(proxy=proxy, bound=cls)
def _query_stmt(stmt, context, frame=None): queried = False if context is not None: name = context.lookupname context = context.clone() else: name = None context = contextmod.InferenenceContext() res = [] if stmt is util.Uninferable: assert False context.lookupname = stmt._query_name(frame, name) try: res.extend(stmt.query(context=context)) queried = True except exceptions.NameInferenceError: assert False # continue except exceptions.InferenceError: assert False # yield util.Uninferable # queried = True # continue if not queried: raise exceptions.InferenceError( "query failed for all members of {stmt!r}.", stmts=stmt, frame=frame, context=context, ) return res
def infer_call_result(self, caller=None, context=None): nonlocal func_setter if caller and len(caller.args) != 2: raise exceptions.InferenceError( "fset() needs two arguments", target=self, context=context ) yield from func_setter.infer_call_result(caller=caller, context=context)
def _infer_stmts(stmts, context, frame=None): """Return an iterator on statements inferred by each statement in *stmts*.""" stmt = None inferred = False if context is not None: name = context.lookupname context = context.clone() else: name = None context = contextmod.InferenceContext() for stmt in stmts: if stmt is util.YES: yield stmt inferred = True continue context.lookupname = stmt._infer_name(frame, name) try: for inferred in stmt.infer(context=context): yield inferred inferred = True except exceptions.UnresolvableName: continue except exceptions.InferenceError: yield util.YES inferred = True if not inferred: raise exceptions.InferenceError(str(stmt))
def _infer_stmts(stmts, context, frame=None): """Return an iterator on statements inferred by each statement in *stmts*.""" stmt = None inferred = False if context is not None: name = context.lookupname context = context.clone() else: name = None context = contextmod.InferenceContext() for stmt in stmts: if stmt is util.Uninferable: yield stmt inferred = True continue context.lookupname = stmt._infer_name(frame, name) try: for inferred in stmt.infer(context=context): yield inferred inferred = True except exceptions.NameInferenceError: continue except exceptions.InferenceError: yield util.Uninferable inferred = True if not inferred: raise exceptions.InferenceError( 'Inference failed for all members of {stmts!r}.', stmts=stmts, frame=frame, context=context)
def raise_if_nothing_inferred(func, instance, args, kwargs): inferred = False for node in func(*args, **kwargs): inferred = True yield node if not inferred: raise exceptions.InferenceError()
def infer_call_result(self, caller, context=None): if len(caller.args) > 2 or len(caller.args) < 1: raise exceptions.InferenceError( "Invalid arguments for descriptor binding", target=self, context=context, )
def _infer_context_manager(self, mgr, context): inferred = next(mgr.infer(context=context)) if isinstance(inferred, bases.Generator): # Check if it is decorated with contextlib.contextmanager. func = inferred.parent if not func.decorators: raise exceptions.InferenceError( "No decorators found on inferred generator %s", node=func ) for decorator_node in func.decorators.nodes: decorator = next(decorator_node.infer(context)) if isinstance(decorator, nodes.FunctionDef): if decorator.qname() == _CONTEXTLIB_MGR: break else: # It doesn't interest us. raise exceptions.InferenceError(node=func) # Get the first yield point. If it has multiple yields, # then a RuntimeError will be raised. possible_yield_points = func.nodes_of_class(nodes.Yield) # Ignore yields in nested functions yield_point = next( (node for node in possible_yield_points if node.scope() == func), None ) if yield_point: if not yield_point.value: const = nodes.Const(None) const.parent = yield_point const.lineno = yield_point.lineno yield const else: yield from yield_point.value.infer(context=context) elif isinstance(inferred, bases.Instance): try: enter = next(inferred.igetattr("__enter__", context=context)) except (exceptions.InferenceError, exceptions.AttributeInferenceError): raise exceptions.InferenceError(node=inferred) if not isinstance(enter, bases.BoundMethod): raise exceptions.InferenceError(node=enter) if not context.callcontext: context.callcontext = contextmod.CallContext(args=[inferred]) yield from enter.infer_call_result(self, context) else: raise exceptions.InferenceError(node=mgr)
def infer_import(self, context=None, asname=True): """infer an Import node: return the imported module/object""" name = context.lookupname if name is None: raise exceptions.InferenceError() if asname: yield self.do_import_module(self.real_name(name)) else: yield self.do_import_module(name)
def _infer_map(node, context): """Infer all values based on Dict.items""" values = {} for name, value in node.items: if isinstance(name, nodes.DictUnpack): double_starred = helpers.safe_infer(value, context) if double_starred in (None, util.Uninferable): raise exceptions.InferenceError if not isinstance(double_starred, nodes.Dict): raise exceptions.InferenceError(node=node, context=context) values.update(_infer_map(double_starred, context)) else: key = helpers.safe_infer(name, context=context) value = helpers.safe_infer(value, context=context) if key is None or value is None: raise exceptions.InferenceError(node=node, context=context) values[key] = value return values
def infer_call_result(self, caller=None, context=None): nonlocal func if caller and len(caller.args) != 1: raise exceptions.InferenceError( "fget() needs a single argument", target=self, context=context) yield from func.function.infer_call_result(caller=caller, context=context)
def _infer_map(node, context): """Infer all values based on Dict.items""" values = {} for name, value in node.items: if isinstance(name, nodes.DictUnpack): double_starred = helpers.safe_infer(value, context) if not double_starred: raise exceptions.InferenceError if not isinstance(double_starred, nodes.Dict): raise exceptions.InferenceError(node=node, context=context) unpack_items = _infer_map(double_starred, context) values = _update_with_replacement(values, unpack_items) else: key = helpers.safe_infer(name, context=context) value = helpers.safe_infer(value, context=context) if any(not elem for elem in (key, value)): raise exceptions.InferenceError(node=node, context=context) values = _update_with_replacement(values, {key: value}) return values
def raise_if_nothing_inferred(func, instance, args, kwargs): """All generators wrapped with raise_if_nothing_inferred *must* explicitly raise StopIteration with information to create an appropriate structured InferenceError. """ inferred = False try: generator = func(*args, **kwargs) while True: yield next(generator) inferred = True except StopIteration as error: if not inferred: if error.args: # pylint: disable=not-a-mapping raise exceptions.InferenceError(**error.args[0]) else: raise exceptions.InferenceError( 'StopIteration raised without any error information.')
def object_len(node, context=None): """Infer length of given node object :param Union[nodes.ClassDef, nodes.Instance] node: :param node: Node to infer length of :raises AstroidTypeError: If an invalid node is returned from __len__ method or no __len__ method exists :raises InferenceError: If the given node cannot be inferred or if multiple nodes are inferred :rtype int: Integer length of node """ from astroid.objects import FrozenSet inferred_node = safe_infer(node, context=context) if inferred_node is None or inferred_node is util.Uninferable: raise exceptions.InferenceError(node=node) if (isinstance(inferred_node, nodes.Const) and isinstance(inferred_node.value, (bytes, str))): return len(inferred_node.value) if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)): return len(inferred_node.elts) if isinstance(inferred_node, nodes.Dict): return len(inferred_node.items) try: node_type = object_type(inferred_node, context=context) len_call = next(node_type.igetattr("__len__", context=context)) except exceptions.AttributeInferenceError: raise exceptions.AstroidTypeError( "object of type '{}' has no len()".format(len_call.pytype())) try: result_of_len = next(len_call.infer_call_result(node, context)) # Remove StopIteration catch when #507 is fixed except StopIteration: raise exceptions.InferenceError(node=node) if (isinstance(result_of_len, nodes.Const) and result_of_len.pytype() == "builtins.int"): return result_of_len.value else: raise exceptions.AstroidTypeError( "'{}' object cannot be interpreted as an integer".format( result_of_len))
def infer_import_from(self, context=None, asname=True): """infer a ImportFrom node: return the imported module/object""" name = context.lookupname if name is None: raise exceptions.InferenceError(node=self, context=context) if asname: name = self.real_name(name) try: module = se# -*- coding: utf-8 -*-
def infer_call_result(self, caller, context=None): """infer what a class instance is returning when called""" inferred = False for node in self._proxied.igetattr('__call__', context): if node is util.YES or not node.callable(): continue for res in node.infer_call_result(caller, context): inferred = True yield res if not inferred: raise exceptions.InferenceError()
def _infer_sequence_helper(node, context=None): """Infer all values based on _BaseContainer.elts""" values = [] for elt in node.elts: if isinstance(elt, nodes.Starred): starred = helpers.safe_infer(elt.value, context) if not starred: raise exceptions.InferenceError(node=node, context=context) if not hasattr(starred, "elts"): raise exceptions.InferenceError(node=node, context=context) values.extend(_infer_sequence_helper(starred)) elif isinstance(elt, nodes.NamedExpr): value = helpers.safe_infer(elt.value, context) if not value: raise exceptions.InferenceError(node=node, context=context) values.append(value) else: values.append(elt) return values
def named_expr_assigned_stmts(self, node, context=None, assign_path=None): """Infer names and other nodes from an assignment expression""" if self.target == node: yield from self.value.infer(context=context) else: raise exceptions.InferenceError( "Cannot infer NamedExpr node {node!r}", node=self, assign_path=assign_path, context=context, )