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 if not context: context = InferenceContext() try: for infered in _infer_stmts(self.getattr(name, context), context, frame=self, lookupname=name): # 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 test_absolute_import(self): astroid = resources.build_file('data/absimport.py') ctx = InferenceContext() # will fail if absolute import failed next(astroid['message'].infer(ctx, lookupname='message')) m = next(astroid['email'].infer(ctx, lookupname='email')) self.assertFalse(m.file.startswith(os.path.join('data', 'email.py')))
def ancestors(self, recurs=True, context=None): """return an iterator on the node base classes in a prefixed depth first order :param recurs: boolean indicating if it should recurse or return direct ancestors only """ # FIXME: should be possible to choose the resolution order # XXX inference make infinite loops possible here (see BaseTransformer # manipulation in the builder module for instance) yielded = set([self]) if context is None: context = InferenceContext() for stmt in self.bases: with context.restore_path(): try: for baseobj in stmt.infer(context): if not isinstance(baseobj, Class): # duh ? continue if baseobj in yielded: continue # cf xxx above yielded.add(baseobj) yield baseobj if recurs: for grandpa in baseobj.ancestors(True, context): if grandpa in yielded: continue # cf xxx above yielded.add(grandpa) yield grandpa except InferenceError: # XXX log error ? continue
def _arguments_infer_argname(self, name, context): # arguments information may be missing, in which case we can't do anything # more if not (self.args or self.vararg or self.kwarg): yield YES return # first argument of instance/class method if self.args and getattr(self.args[0], 'name', None) == name: functype = self.parent.type if functype == 'method': yield Instance(self.parent.parent.frame()) return if functype == 'classmethod': yield self.parent.parent.frame() return if name == self.vararg: vararg = const_factory(()) vararg.parent = self yield vararg return if name == self.kwarg: kwarg = const_factory({}) kwarg.parent = self yield kwarg return # if there is a default value, yield it. And then yield YES to reflect # we can't guess given argument value try: if context is None: context = InferenceContext() for infered in self.default_value(name).infer(context): yield infered yield YES except NoDefault: yield YES
def test_absolute_import(self): astroid = abuilder.file_build(self.datapath('absimport.py')) ctx = InferenceContext() ctx.lookupname = 'message' # will fail if absolute import failed astroid['message'].infer(ctx).next() ctx.lookupname = 'email' m = astroid['email'].infer(ctx).next() self.assertFalse(m.file.startswith(self.datapath('email.py')))
def ilookup(self, name): """infered lookup return an iterator on infered values of the statements returned by the lookup method """ frame, stmts = self.lookup(name) context = InferenceContext() return _infer_stmts(stmts, context, frame)
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)
def _newstyle_impl(self, context=None): if context is None: context = InferenceContext() if self._newstyle is not None: return self._newstyle for base in self.ancestors(recurs=False, context=context): if base._newstyle_impl(context): self._newstyle = True break if self._newstyle is None: self._newstyle = False return self._newstyle
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_getattr(self, context=None): """infer a Getattr node by using getattr on the associated object""" if not context: context = InferenceContext() for owner in self.expr.infer(context): if owner is YES: yield owner continue try: with context.scope(boundnode=owner): for obj in owner.igetattr(self.attrname, context): yield obj except (NotFoundError, InferenceError): pass except AttributeError: # XXX method / function pass
def _newstyle_impl(self, context=None): if context is None: context = InferenceContext() if self._newstyle is not None: return self._newstyle for base in self.ancestors(recurs=False, context=context): if base._newstyle_impl(context): self._newstyle = True break klass = self._explicit_metaclass() # could be any callable, we'd need to infer the result of klass(name, # bases, dict). punt if it's not a class node. if klass is not None and isinstance(klass, Class): self._newstyle = klass._newstyle_impl(context) if self._newstyle is None: self._newstyle = False return self._newstyle
def infer_callfunc(self, context=None): """infer a CallFunc node by trying to guess what the function returns""" if context is None: context = InferenceContext() for callee in self.func.infer(context): with context.scope( callcontext=CallContext(self.args, self.starargs, self.kwargs), boundnode=None, ): if callee is YES: yield callee continue try: if hasattr(callee, 'infer_call_result'): for infered in callee.infer_call_result(self, context): yield infered except InferenceError: ## XXX log error ? continue
def ancestors(self, recurs=True, context=None): """return an iterator on the node base classes in a prefixed depth first order :param recurs: boolean indicating if it should recurse or return direct ancestors only """ # FIXME: should be possible to choose the resolution order # FIXME: inference make infinite loops possible here yielded = set([self]) if context is None: context = InferenceContext() if sys.version_info[0] >= 3: if not self.bases and self.qname() != 'builtins.object': yield builtin_lookup("object")[1][0] return for stmt in self.bases: try: for baseobj in stmt.infer(context): if not isinstance(baseobj, Class): if isinstance(baseobj, Instance): baseobj = baseobj._proxied else: # duh ? continue if not baseobj.hide: if baseobj in yielded: continue # cf xxx above yielded.add(baseobj) yield baseobj if recurs: for grandpa in baseobj.ancestors(recurs=True, context=context): if grandpa in yielded: continue # cf xxx above yielded.add(grandpa) yield grandpa except InferenceError: # XXX log error ? continue
def infer_name_module(self, name): context = InferenceContext() return self.infer(context, asname=False, lookupname=name)