def bool_value(self): """Infer the truth value for an Instance The truth value of an instance is determined by these conditions: * if it implements __bool__ on Python 3 or __nonzero__ on Python 2, then its bool value will be determined by calling this special method and checking its result. * when this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __bool__(), all its instances are considered true. """ context = contextmod.InferenceContext() context.callcontext = contextmod.CallContext(args=[]) context.boundnode = self try: result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context) except (exceptions.InferenceError, exceptions.AttributeInferenceError): # Fallback to __len__. try: result = _infer_method_result_truth(self, '__len__', context) except (exceptions.AttributeInferenceError, exceptions.InferenceError): return True return result
def query_call(self: nodes.Call, context=None): """query a Call node by trying to guess what the function returns""" callcontext = contextmod.copy_context(context) callcontext.callcontext = contextmod.CallContext(args=self.args, keywords=self.keywords) callcontext.boundnode = None if context is not None: # extra context saved the args, what's for? callcontext.extra_context = _populate_context_lookup( self, context.clone()) for callee in self.func.query(context): if callee is util.Uninferable: yield callee continue if callee is util.Unqueryable: if not isinstance(self.keywords, None): # not implemented print(kv) assert False return self.args try: if hasattr(callee, "query_call_result"): # no filed sensitive here yield from callee.query_call_result(caller=self, context=callcontext) except exceptions.InferenceError: continue return dict(node=self, context=context)
def infer_call(self, context=None): """infer a Call node by trying to guess what the function returns""" callcontext = context.clone() callcontext.callcontext = contextmod.CallContext(args=self.args, keywords=self.keywords) callcontext.boundnode = None extra_context = {} if context is not None: extra_context = _populate_context_lookup(self, context.clone()) callcontext.extra_context = extra_context for callee in self.func.infer(context): if callee is util.Uninferable: yield callee continue try: if hasattr(callee, 'infer_call_result'): yield from callee.infer_call_result( caller=self, context=callcontext, ) except exceptions.InferenceError: ## XXX log error ? continue # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. return dict(node=self, context=context)
def instance_getitem(self, index, context=None): # Rewrap index to Const for this case new_context = contextmod.bind_context_to_node(context, self) if not context: context = new_context # Create a new callcontext for providing index as an argument. new_context.callcontext = contextmod.CallContext(args=[index]) method = next(self.igetattr("__getitem__", context=context), None) if not isinstance(method, bases.BoundMethod): raise exceptions.InferenceError( "Could not find __getitem__ for {node!r}.", node=self, context=context) try: return next(method.infer_call_result(self, new_context)) except StopIteration as exc: raise exceptions.InferenceError( message="Inference for {node!r}[{index!s}] failed.", node=self, index=index, context=context, ) from exc
def instance_getitem(self, index, context=None): # Rewrap index to Const for this case if context: new_context = context.clone() else: context = new_context = contextmod.InferenceContext() # Create a new callcontext for providing index as an argument. new_context.callcontext = contextmod.CallContext(args=[index]) new_context.boundnode = self method = next(self.igetattr('__getitem__', context=context)) if not isinstance(method, bases.BoundMethod): raise exceptions.InferenceError( 'Could not find __getitem__ for {node!r}.', node=self, context=context) try: return next(method.infer_call_result(self, new_context)) except StopIteration: util.reraise( exceptions.InferenceError( message='Inference for {node!r}[{index!s}] failed.', node=self, index=index, context=context))
def _infer_unaryop(self, context=None): """Infer what an UnaryOp should return when evaluated.""" for operand in self.operand.infer(context): try: yield operand.infer_unary_op(self.op) except TypeError as exc: # The operand doesn't support this operation. yield util.BadUnaryOperationMessage(operand, self.op, exc) except AttributeError as exc: meth = protocols.UNARY_OP_METHOD[self.op] if meth is None: # `not node`. Determine node's boolean # value and negate its result, unless it is # Uninferable, which will be returned as is. bool_value = operand.bool_value() if bool_value is not util.Uninferable: yield nodes.const_factory(not bool_value) else: yield util.Uninferable else: if not isinstance(operand, (bases.Instance, nodes.ClassDef)): # The operation was used on something which # doesn't support it. yield util.BadUnaryOperationMessage(operand, self.op, exc) continue try: try: methods = dunder_lookup.lookup(operand, meth) except exceptions.AttributeInferenceError: yield util.BadUnaryOperationMessage( operand, self.op, exc) continue meth = methods[0] inferred = next(meth.infer(context=context)) if inferred is util.Uninferable or not inferred.callable(): continue context = contextmod.copy_context(context) context.callcontext = contextmod.CallContext( args=[operand]) call_results = inferred.infer_call_result(self, context=context) result = next(call_results, None) if result is None: # Failed to infer, return the same type. yield operand else: yield result except exceptions.AttributeInferenceError as exc: # The unary operation special method was not found. yield util.BadUnaryOperationMessage(operand, self.op, exc) except exceptions.InferenceError: yield util.Uninferable
def from_call(cls, call_node, context=None): """Get a CallSite object from the given Call node. :param context: An instance of :class:`astroid.context.Context` that will be used to force a single inference path. """ # Determine the callcontext from the given `context` object if any. context = context or contextmod.InferenceContext() callcontext = contextmod.CallContext(call_node.args, call_node.keywords) return cls(callcontext, context=context)
def _infer_context_manager(self, mgr, context): try: inferred = next(mgr.infer(context=context)) except (StopIteration, exceptions.InferenceError): return if isinstance(inferred, bases.Generator): # Check if it is decorated with contextlib.contextmanager. func = inferred.parent if not func.decorators: return for decorator_node in func.decorators.nodes: try: decorator = next(decorator_node.infer(context)) except StopIteration: return if isinstance(decorator, nodes.FunctionDef): if decorator.qname() == _CONTEXTLIB_MGR: break else: # It doesn't interest us. return # 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 ( StopIteration, exceptions.InferenceError, exceptions.AttributeInferenceError, ): return if not isinstance(enter, bases.BoundMethod): return if not context.callcontext: context.callcontext = contextmod.CallContext(args=[inferred]) yield from enter.infer_call_result(self, context)
def getitem(self, index, context=None): # TODO: Rewrap index to Const for this case new_context = contextmod.bind_context_to_node(context, self) if not context: context = new_context # Create a new CallContext for providing index as an argument. new_context.callcontext = contextmod.CallContext(args=[index]) method = next(self.igetattr("__getitem__", context=context), None) if not isinstance(method, BoundMethod): raise exceptions.InferenceError( "Could not find __getitem__ for {node!r}.", node=self, context=context) return next(method.infer_call_result(self, new_context))
def _get_binop_contexts(context, left, right): """Get contexts for binary operations. This will return two inference contexts, the first one for x.__op__(y), the other one for y.__rop__(x), where only the arguments are inversed. """ # The order is important, since the first one should be # left.__op__(right). for arg in (right, left): new_context = context.clone() new_context.callcontext = contextmod.CallContext(args=[arg]) new_context.boundnode = None yield new_context
def infer_call(self, context=None): """infer a Call node by trying to guess what the function returns""" callcontext = context.clone() callcontext.callcontext = contextmod.CallContext(args=self.args, keywords=self.keywords) callcontext.boundnode = None for callee in self.func.infer(context): if callee is util.YES: yield callee continue try: if hasattr(callee, 'infer_call_result'): for inferred in callee.infer_call_result(self, callcontext): yield inferred except exceptions.InferenceError: ## XXX log error ? continue
def _infer_context_manager(self, mgr, context): try: inferred = next(mgr.infer(context=context)) except exceptions.InferenceError: return if isinstance(inferred, bases.Generator): # Check if it is decorated with contextlib.contextmanager. func = inferred.parent if not func.decorators: return 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. return # Get the first yield point. If it has multiple yields, # then a RuntimeError will be raised. # TODO(cpopa): Handle flows. yield_point = next(func.nodes_of_class(nodes.Yield), None) if yield_point: if not yield_point.value: # TODO(cpopa): an empty yield. Should be wrapped to Const. const = nodes.Const(None) const.parent = yield_point const.lineno = yield_point.lineno yield const else: for inferred in yield_point.value.infer(context=context): yield inferred elif isinstance(inferred, bases.Instance): try: enter = next(inferred.igetattr('__enter__', context=context)) except (exceptions.InferenceError, exceptions.AttributeInferenceError): return if not isinstance(enter, bases.BoundMethod): return if not context.callcontext: context.callcontext = contextmod.CallContext(args=[inferred]) for result in enter.infer_call_result(self, context): yield result
def getitem(self, index, context=None): # TODO: Rewrap index to Const for this case new_context = contextmod.bind_context_to_node(context, self) if not context: context = new_context # Create a new CallContext for providing index as an argument. new_context.callcontext = contextmod.CallContext(args=[index]) method = next(self.igetattr("__getitem__", context=context), None) if not isinstance(method, BoundMethod): raise InferenceError("Could not find __getitem__ for {node!r}.", node=self, context=context) if len(method.args.arguments) != 2: # (self, index) raise AstroidTypeError( "__getitem__ for {node!r} does not have correct signature", node=self, context=context, ) return next(method.infer_call_result(self, new_context), None)
def infer_call(self, context=None): """infer a Call node by trying to guess what the function returns""" callcontext = contextmod.copy_context(context) callcontext.callcontext = contextmod.CallContext( args=self.args, keywords=self.keywords ) callcontext.boundnode = None if context is not None: callcontext.extra_context = _populate_context_lookup(self, context.clone()) for callee in self.func.infer(context): if callee is util.Uninferable: yield callee continue try: if hasattr(callee, "infer_call_result"): yield from callee.infer_call_result(caller=self, context=callcontext) except exceptions.InferenceError: continue return dict(node=self, context=context)
def instance_getitem(self, index, context=None): # Rewrap index to Const for this case index = nodes.Const(index) if context: new_context = context.clone() else: context = new_context = contextmod.InferenceContext() # Create a new callcontext for providing index as an argument. new_context.callcontext = contextmod.CallContext(args=[index]) new_context.boundnode = self method = next(self.igetattr('__getitem__', context=context)) if not isinstance(method, bases.BoundMethod): raise exceptions.InferenceError try: return next(method.infer_call_result(self, new_context)) except StopIteration: raise exceptions.InferenceError
def infer_call(self, context=None): """infer a Call node by trying to guess what the function returns""" callcontext = context.clone() callcontext.callcontext = contextmod.CallContext(args=self.args, keywords=self.keywords) callcontext.boundnode = None for callee in self.func.infer(context): if callee is util.Uninferable: yield callee continue try: if hasattr(callee, 'infer_call_result'): for inferred in callee.infer_call_result(self, callcontext): yield inferred except exceptions.InferenceError: ## XXX log error ? continue # Explicit StopIteration to return error information, see comment # in raise_if_nothing_inferred. raise StopIteration(dict(node=self, context=context))
def class_instance_as_index(node): """Get the value as an index for the given instance. If an instance provides an __index__ method, then it can be used in some scenarios where an integer is expected, for instance when multiplying or subscripting a list. """ context = contextmod.InferenceContext() context.callcontext = contextmod.CallContext(args=[node]) try: for inferred in node.igetattr("__index__", context=context): if not isinstance(inferred, bases.BoundMethod): continue for result in inferred.infer_call_result(node, context=context): if isinstance(result, nodes.Const) and isinstance(result.value, int): return result except exceptions.InferenceError: pass return None
def query_call(self: nodes.Call, context=None): """query a Call node by trying to guess what the function returns""" callcontext = contextmod.copy_context(context) callcontext.callcontext = contextmod.CallContext(args=self.args, keywords=self.keywords) callcontext.boundnode = None if context is not None: # extra context saved the args, what's for? callcontext.extra_context = _populate_context_lookup( self, context.clone()) res = [] for callee in self.func.query(context): if callee is util.Unqueryable: if self.keywords is not None: # not implemented print(self.keywords) assert False for arg in self.args: res.extend(arg.query(context)) continue if hasattr(callee, "query_end") and callee.query_end: res.append(callee) continue try: if hasattr(callee, "query_call_result"): for result in callee.query_call_result(caller=self, context=callcontext): if result is util.Unqueryable: for arg in self.args: res.extend(arg.query(context)) else: res.append(result) except exceptions.InferenceError: continue return res
def from_call(cls, call_node): """Get a CallSite object from the given Call node.""" callcontext = contextmod.CallContext(call_node.args, call_node.keywords) return cls(callcontext)