def igetattr(self, name, context=None): """inferred getattr""" if not context: context = InferenceContext() try: context.lookupname = name # avoid recursively inferring the same attr on the same class if context.push(self._proxied): raise InferenceError( message="Cannot infer the same attribute again", node=self, context=context, ) # XXX frame should be self._proxied, or not ? get_attr = self.getattr(name, context, lookupclass=False) yield from _infer_stmts(self._wrap_attr(get_attr, context), context, frame=self) except AttributeInferenceError: try: # fallback to class.igetattr since it has some logic to handle # descriptors # But only if the _proxied is the Class. if self._proxied.__class__.__name__ != "ClassDef": raise attrs = self._proxied.igetattr(name, context, class_context=False) yield from self._wrap_attr(attrs, context) except AttributeInferenceError as error: raise InferenceError(**vars(error)) from error
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 InferenceError(node=self, context=context) if asname: try: name = self.real_name(name) except AttributeInferenceError as exc: # See https://github.com/PyCQA/pylint/issues/4692 raise InferenceError(node=self, context=context) from exc try: module = self.do_import_module() except AstroidBuildingError as exc: raise InferenceError(node=self, context=context) from exc try: context = copy_context(context) context.lookupname = name stmts = module.getattr(name, ignore_locals=module is self.root()) return bases._infer_stmts(stmts, context) except AttributeInferenceError as error: raise InferenceError( str(error), target=self, attribute=name, context=context ) from error
def infer_global(self, context=None, lookupname=None): if lookupname is None: raise InferenceError() try: return _infer_stmts(self.root().getattr(lookupname), context) except NotFoundError: raise InferenceError()
def infer_subscript(self, context=None): """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" value = next(self.value.infer(context)) if value is YES: yield YES return index = next(self.slice.infer(context)) if index is YES: yield YES return if isinstance(index, nodes.Const): try: assigned = value.getitem(index.value, context) except AttributeError: raise InferenceError() except (IndexError, TypeError): yield YES return # Prevent inferring if the infered subscript # is the same as the original subscripted object. if self is assigned: yield YES return for infered in assigned.infer(context): yield infered else: raise InferenceError()
def infer_subscript( self: nodes.Subscript, context: InferenceContext | None = None, **kwargs: Any ) -> Generator[InferenceResult, None, InferenceErrorInfo | 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 InferenceError(node=self, context=context) try: assigned = value.getitem(index_value, context) except ( AstroidTypeError, AstroidIndexError, AttributeInferenceError, AttributeError, ) as exc: raise 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 InferenceErrorInfo(node=self, context=context) return None
def infer_global(self, context=None): if context.lookupname is None: raise InferenceError(node=self, context=context) try: return bases._infer_stmts(self.root().getattr(context.lookupname), context) except AttributeInferenceError as error: raise InferenceError( str(error), target=self, attribute=context.lookupname, context=context ) from error
def infer_from(self, context=None, asname=True, lookupname=None): """infer a From nodes: return the imported module/object""" if lookupname is None: raise InferenceError() if asname: lookupname = self.real_name(lookupname) module = self.do_import_module() try: return _infer_stmts(module.getattr(lookupname, ignore_locals=module is self.root()), context, lookupname=lookupname) except NotFoundError: raise InferenceError(lookupname)
def infer_global(self: nodes.Global, context: InferenceContext | None = None, **kwargs: Any) -> Generator[InferenceResult, None, None]: if context is None or context.lookupname is None: raise InferenceError(node=self, context=context) try: return bases._infer_stmts(self.root().getattr(context.lookupname), context) except AttributeInferenceError as error: raise InferenceError(str(error), target=self, attribute=context.lookupname, context=context) from error
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 InferenceError(**error.args[0]) from error raise InferenceError( "StopIteration raised without any error information.") from error yield from generator
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 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 AstroidBuildingError as exc: raise InferenceError(node=self, context=context) from exc
def starred_assigned_stmts(self, node=None, context=None, asspath=None): stmt = self.statement() if not isinstance(stmt, (nodes.Assign, nodes.For)): raise 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 InferenceError() if context is None: context = InferenceContext() try: rhs = next(value.infer(context)) except InferenceError: yield YES return if rhs is YES or not hasattr(rhs, 'elts'): # Not interested in inferred values without elts. yield YES return elts = collections.deque(rhs.elts[:]) if len(lhs.elts) > len(rhs.elts): # a, *b, c = (1, 2) raise 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 for elt in elts: yield elt break
def infer_from(self, context=None, asname=True): """infer a From nodes: return the imported module/object""" name = context.lookupname if name is None: raise InferenceError() if asname: name = self.real_name(name) module = self.do_import_module(self.modname) try: context = copy_context(context) context.lookupname = name return _infer_stmts( module.getattr(name, ignore_locals=module is self.root()), context) except NotFoundError: raise InferenceError(name)
def infer_call_result(self, caller=None, context=None): nonlocal func_setter if caller and len(caller.args) != 2: raise InferenceError( "fset() needs two arguments", target=self, context=context ) yield from func_setter.infer_call_result(caller=caller, context=context)
def infer_arguments(self: nodes.Arguments, context: InferenceContext | None = None, **kwargs: Any) -> Generator[InferenceResult, None, None]: if context is None or context.lookupname is None: raise InferenceError(node=self, context=context) return protocols._arguments_infer_argname(self, context.lookupname, context)
def infer_typing_namedtuple_class(class_node, context=None): """Infer a subclass of typing.NamedTuple""" # Check if it has the corresponding bases annassigns_fields = [ annassign.target.name for annassign in class_node.body if isinstance(annassign, nodes.AnnAssign) ] code = dedent( """ from collections import namedtuple namedtuple({typename!r}, {fields!r}) """ ).format(typename=class_node.name, fields=",".join(annassigns_fields)) node = extract_node(code) try: generated_class_node = next(infer_named_tuple(node, context)) except StopIteration as e: raise InferenceError(node=node, context=context) from e for method in class_node.mymethods(): generated_class_node.locals[method.name] = [method] for body_node in class_node.body: if isinstance(body_node, nodes.Assign): for target in body_node.targets: attr = target.name generated_class_node.locals[attr] = class_node.locals[attr] elif isinstance(body_node, nodes.ClassDef): generated_class_node.locals[body_node.name] = [body_node] return iter((generated_class_node,))
def _infer_stmts(stmts, context, frame=None, lookupname=None): """return an iterator on statements inferred by each statement in <stmts> """ stmt = None infered = False if context is None: context = InferenceContext() for stmt in stmts: if stmt is YES: yield stmt infered = True continue kw = {} infered_name = stmt._infer_name(frame, lookupname) if infered_name is not None: # only returns not None if .infer() accepts a lookupname kwarg kw['lookupname'] = infered_name try: for infered in stmt.infer(context, **kw): yield infered infered = True except UnresolvableName: continue except InferenceError: yield YES infered = True if not infered: raise InferenceError(str(stmt))
def wrapper(*args, **kwargs): infered = False for node in func(*args, **kwargs): infered = True yield node if not infered: raise InferenceError()
def _infer_stmts(stmts, context, frame=None): """Return an iterator on statements inferred by each statement in *stmts*.""" inferred = False if context is not None: name = context.lookupname context = context.clone() else: name = None context = InferenceContext() for stmt in stmts: if stmt is Uninferable: yield stmt inferred = True continue context.lookupname = stmt._infer_name(frame, name) try: for inf in stmt.infer(context=context): yield inf inferred = True except NameInferenceError: continue except InferenceError: yield Uninferable inferred = True if not inferred: raise InferenceError( "Inference failed for all members of {stmts!r}.", stmts=stmts, frame=frame, context=context, )
def igetattr(self, name, context=None): """inferred getattr, need special treatment in class to handle descriptors """ # set lookup name since this is necessary to infer on import nodes for # instance context = copy_context(context) context.lookupname = name try: for infered in _infer_stmts(self.getattr(name, context), context, frame=self): # yield YES object instead of descriptors when necessary if not isinstance(infered, Const) and isinstance( infered, Instance): try: infered._proxied.getattr('__get__', context) except NotFoundError: yield infered else: yield YES else: yield function_to_method(infered, self) except NotFoundError: if not name.startswith('__') and self.has_dynamic_getattr(context): # class handle some dynamic attributes, return a YES object yield YES else: raise InferenceError(name)
def _infer_stmts(stmts, context, frame=None): """return an iterator on statements inferred by each statement in <stmts> """ stmt = None infered = False if context is not None: name = context.lookupname context = context.clone() else: name = None context = InferenceContext() for stmt in stmts: if stmt is YES: yield stmt infered = True continue context.lookupname = stmt._infer_name(frame, name) try: for infered in stmt.infer(context): yield infered infered = True except UnresolvableName: continue except InferenceError: yield YES infered = True if not infered: raise InferenceError(str(stmt))
def infer_named_tuple(node, context=None): """Specific inference function for namedtuple Call node""" tuple_base_name = nodes.Name(name="tuple", parent=node.root()) class_node, name, attributes = infer_func_form( node, tuple_base_name, context=context ) call_site = arguments.CallSite.from_call(node, context=context) node = extract_node("import collections; collections.namedtuple") try: func = next(node.infer()) except StopIteration as e: raise InferenceError(node=node) from e try: rename = next(call_site.infer_argument(func, "rename", context)).bool_value() except (InferenceError, StopIteration): rename = False try: attributes = _check_namedtuple_attributes(name, attributes, rename) except AstroidTypeError as exc: raise UseInferenceDefault("TypeError: " + str(exc)) from exc except AstroidValueError as exc: raise UseInferenceDefault("ValueError: " + str(exc)) from exc replace_args = ", ".join(f"{arg}=None" for arg in attributes) field_def = ( " {name} = property(lambda self: self[{index:d}], " "doc='Alias for field number {index:d}')" ) field_defs = "\n".join( field_def.format(name=name, index=index) for index, name in enumerate(attributes) ) fake = AstroidBuilder(AstroidManager()).string_build( f""" class {name}(tuple): __slots__ = () _fields = {attributes!r} def _asdict(self): return self.__dict__ @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): return new(cls, iterable) def _replace(self, {replace_args}): return self def __getnewargs__(self): return tuple(self) {field_defs} """ ) class_node.locals["_asdict"] = fake.body[0].locals["_asdict"] class_node.locals["_make"] = fake.body[0].locals["_make"] class_node.locals["_replace"] = fake.body[0].locals["_replace"] class_node.locals["_fields"] = fake.body[0].locals["_fields"] for attr in attributes: class_node.locals[attr] = fake.body[0].locals[attr] # we use UseInferenceDefault, we can't be a generator so return an iterator return iter([class_node])
def infer_import(self, context=None, asname=True, lookupname=None): """infer an Import node: return the imported module/object""" if lookupname is None: raise InferenceError() if asname: yield self.do_import_module(self.real_name(lookupname)) else: yield self.do_import_module(lookupname)
def _infer_context_manager(self, mgr, context): try: inferred = next(mgr.infer(context=context)) except StopIteration as e: raise InferenceError(node=mgr) from e if isinstance(inferred, bases.Generator): # Check if it is decorated with contextlib.contextmanager. func = inferred.parent if not func.decorators: raise InferenceError( "No decorators found on inferred generator %s", node=func) for decorator_node in func.decorators.nodes: decorator = next(decorator_node.infer(context=context), None) if isinstance(decorator, nodes.FunctionDef): if decorator.qname() == _CONTEXTLIB_MGR: break else: # It doesn't interest us. raise InferenceError(node=func) try: yield next(inferred.infer_yield_types()) except StopIteration as e: raise InferenceError(node=func) from e elif isinstance(inferred, bases.Instance): try: enter = next(inferred.igetattr("__enter__", context=context)) except (InferenceError, AttributeInferenceError, StopIteration) as exc: raise InferenceError(node=inferred) from exc if not isinstance(enter, bases.BoundMethod): raise InferenceError(node=enter) yield from enter.infer_call_result(self, context) else: raise InferenceError(node=mgr)
def infer_call_result(self, caller, context=None): if len(caller.args) > 2 or len(caller.args) < 1: raise InferenceError( "Invalid arguments for descriptor binding", target=self, context=context, ) context = copy_context(context) try: cls = next(caller.args[0].infer(context=context)) except StopIteration as e: raise InferenceError(context=context, node=caller.args[0]) from e if cls is astroid.Uninferable: raise InferenceError( "Invalid class inferred", target=self, context=context ) # For some reason func is a Node that the below # code is not expecting if isinstance(func, bases.BoundMethod): yield func return # Rebuild the original value, but with the parent set as the # class where it will be bound. new_func = func.__class__( name=func.name, lineno=func.lineno, col_offset=func.col_offset, parent=func.parent, ) # pylint: disable=no-member new_func.postinit( func.args, func.body, func.decorators, func.returns, doc_node=func.doc_node, ) # 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 infer_call_result(self, caller=None, context=None): nonlocal func if caller and len(caller.args) != 1: raise InferenceError("fget() needs a single argument", target=self, context=context) yield from func.function.infer_call_result(caller=caller, context=context)
def infer_call_result(self, caller, context=None): """infer what a class instance is returning when called""" infered = False for node in self._proxied.igetattr('__call__', context): for res in node.infer_call_result(caller, context): infered = True yield res if not infered: raise InferenceError()
def _infer( self, context: InferenceContext | None = None, **kwargs: Any ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: """we don't know how to resolve a statement by default""" # this method is overridden by most concrete classes raise InferenceError("No inference function for {node!r}.", node=self, context=context)
def infer_import( self: nodes.Import, context: InferenceContext | None = None, asname: bool = True, **kwargs: Any, ) -> Generator[nodes.Module, None, None]: """infer an Import node: return the imported module/object""" context = context or InferenceContext() name = context.lookupname if name is None: raise 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 AstroidBuildingError as exc: raise InferenceError(node=self, context=context) from exc
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 InferenceError if not isinstance(double_starred, nodes.Dict): raise 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 InferenceError(node=node, context=context) values = _update_with_replacement(values, {key: value}) return values
def igetattr(self, name, context=None): """inferred getattr""" # set lookup name since this is necessary to infer on import nodes for # instance if not context: context = InferenceContext() try: return _infer_stmts(self.getattr(name, context), context, frame=self, lookupname=name) except NotFoundError: raise InferenceError(name)