def call(self, node, _, args, alias_map=None): node, var = self.ctx.attribute_handler.get_attribute( node, self, "__call__", self.to_binding(node)) if var is not None and var.bindings: return function.call_function(self.ctx, node, var, args) else: raise function.NotCallable(self)
def _lookup_from_mro_and_handle_descriptors(self, node, cls, name, valself, skip): attr = self._lookup_from_mro(node, cls, name, valself, skip) if not attr.bindings: return node, None if isinstance(cls, abstract.InterpreterClass): result = self.ctx.program.NewVariable() nodes = [] # Deal with descriptors as a potential additional level of indirection. for v in attr.bindings: value = v.data if (isinstance(value, special_builtins.PropertyInstance) and valself and valself.data == cls): node2, getter = node, None else: node2, getter = self.get_attribute(node, value, "__get__", v) if getter is not None: posargs = [] if valself and valself.data != cls: posargs.append(valself.AssignToNewVariable()) else: posargs.append(self.ctx.convert.none.to_variable(node)) posargs.append(cls.to_variable(node)) node2, get_result = function.call_function( self.ctx, node2, getter, function.Args(tuple(posargs))) for getter in get_result.bindings: result.AddBinding(getter.data, [getter], node2) else: result.AddBinding(value, [v], node2) nodes.append(node2) if nodes: return self.ctx.join_cfg_nodes(nodes), result return node, attr
def call_pytd(self, node, name, *args): """Call the (original) pytd version of a method we overwrote.""" return function.call_function( self.ctx, node, self._super[name], function.Args(args), fallback_to_unsolvable=False)
def _call_method(self, node, value, method_name, args): node, method = self.ctx.attribute_handler.get_attribute( node, value.data, method_name, value) if method: call_repr = "%s.%s(..._)" % (self.name, method_name) log.debug("calling %s", call_repr) node, ret = function.call_function(self.ctx, node, method, args) log.debug("%s returned %r", call_repr, ret) return node
def call(self, node, _, args): self.match_args(node, args) arg = args.posargs[0] node, fn = self.get_underlying_method(node, arg, "__abs__") if fn is not None: return function.call_function(self.ctx, node, fn, function.Args( ())) else: return node, self.ctx.new_unsolvable(node)
def call(self, node, _, args): self.match_args(node, args) arg, default = self._get_args(args) node, fn = self.get_underlying_method(node, arg, "__next__") if fn is not None: node, ret = function.call_function(self.ctx, node, fn, function.Args(())) ret.PasteVariable(default) return node, ret else: return node, self.ctx.new_unsolvable(node)
def _call_new_and_init(self, node, value, args): """Call __new__ if it has been overridden on the given value.""" node, new = self.get_own_new(node, value) if new is None: return node, None cls = value.AssignToNewVariable(node) new_args = args.replace(posargs=(cls, ) + args.posargs) node, variable = function.call_function(self.ctx, node, new, new_args) for val in variable.bindings: # If val.data is a class, call_init mistakenly calls val.data's __init__ # method rather than that of val.data.cls. if not isinstance(val.data, Class) and self == val.data.cls: node = self.call_init(node, val, args) return node, variable
def _call_binop_on_bindings(node, name, xval, yval, ctx): """Call a binary operator on two cfg.Binding objects.""" rname = slots.REVERSE_NAME_MAPPING.get(name) if rname and isinstance(xval.data, abstract.AMBIGUOUS_OR_EMPTY): # If the reverse operator is possible and x is ambiguous, then we have no # way of determining whether __{op} or __r{op}__ is called. Technically, # the result is also unknown if y is ambiguous, but it is almost always # reasonable to assume that, e.g., "hello " + y is a string, even though # y could define __radd__. return node, ctx.program.NewVariable( [ctx.convert.unsolvable], [xval, yval], node) options = [(xval, yval, name)] if rname: options.append((yval, xval, rname)) if _overrides(yval.data.cls, xval.data.cls, rname): # If y is a subclass of x and defines its own reverse operator, then we # need to try y.__r{op}__ before x.__{op}__. options.reverse() error = None for left_val, right_val, attr_name in options: if (isinstance(left_val.data, abstract.Class) and attr_name == "__getitem__"): # We're parameterizing a type annotation. Set valself to None to # differentiate this action from a real __getitem__ call on the class. valself = None else: valself = left_val node, attr_var = ctx.attribute_handler.get_attribute( node, left_val.data, attr_name, valself) if attr_var and attr_var.bindings: args = function.Args(posargs=(right_val.AssignToNewVariable(),)) try: return function.call_function( ctx, node, attr_var, args, fallback_to_unsolvable=False) except (function.DictKeyMissing, function.FailedFunctionCall) as e: # It's possible that this call failed because the function returned # NotImplemented. See, e.g., # test_operators.ReverseTest.check_reverse(), in which 1 {op} Bar() ends # up using Bar.__r{op}__. Thus, we need to save the error and try the # other operator. if e > error: error = e if error: raise error # pylint: disable=raising-bad-type else: return node, None
def _get_attribute_computed(self, node, cls, name, valself, compute_function): """Call compute_function (if defined) to compute an attribute.""" assert isinstance(cls, (abstract.Class, abstract.AMBIGUOUS_OR_EMPTY)), cls if (valself and not isinstance(valself.data, abstract.Module) and self._computable(name)): attr_var = self._lookup_from_mro( node, cls, compute_function, valself, skip={self.ctx.convert.object_type}) if attr_var and attr_var.bindings: name_var = self.ctx.convert.constant_to_var(name, node=node) return function.call_function(self.ctx, node, attr_var, function.Args((name_var, ))) return node, None
def call_metaclass_init(self, node): """Call the metaclass's __init__ method if it does anything interesting.""" if self.cls.full_name == "builtins.type": return node node, init = self.ctx.attribute_handler.get_attribute( node, self.cls, "__init__") if not init or not any( _isinstance(f, "SignedFunction") for f in init.data): # Only SignedFunctions (InterpreterFunction and SimpleFunction) have # interesting side effects. return node args = function.Args( posargs=(self.to_variable(node), self.ctx.convert.build_string(node, self.name), self.ctx.convert.build_tuple(node, self.bases()), self.ctx.new_unsolvable(node))) log.debug("Calling __init__ on metaclass %s of class %s", self.cls.name, self.name) node, _ = function.call_function(self.ctx, node, init, args) return node
def call_with_fake_args(self, node0, funcv): """Attempt to call the given function with made-up arguments.""" # Note that this should only be used for functions that raised a # FailedFunctionCall error. This is not guaranteed to successfully call a # function that raised DictKeyMissing instead. nodes = [] rets = [] for funcb in funcv.bindings: func = funcb.data log.info("Trying %s with fake arguments", func) if isinstance(func, abstract.INTERPRETER_FUNCTION_TYPES): node1, args = self.create_method_arguments(node0, func) # Once the args are generated, try calling the function. # call_function will check fallback_to_unsolvable if a DictKeyMissing or # FailedFunctionCall error is raised when the target function is called. # DictKeyMissing doesn't trigger call_with_fake_args, so that shouldn't # be raised again, and generating fake arguments should avoid any # FailedFunctionCall errors. To prevent an infinite recursion loop, set # fallback_to_unsolvable to False just in case. # This means any additional errors that may be raised will be passed to # the call_function that called this method in the first place. node2, ret = function.call_function( self.ctx, node1, funcb.AssignToNewVariable(), args, fallback_to_unsolvable=False) nodes.append(node2) rets.append(ret) if nodes: ret = self.ctx.join_variables(node0, rets) node = self.ctx.join_cfg_nodes(nodes) if ret.bindings: return node, ret else: node = node0 log.info("Unable to generate fake arguments for %s", funcv) return node, self.ctx.new_unsolvable(node)
def call(self, node, func, args, alias_map=None): var = self.ctx.program.NewVariable(self.options, [], node) return function.call_function(self.ctx, node, var, args)
def call(self, node, func, args, alias_map=None): var = self.instance.get_instance_type_parameter(self.name) if var.bindings: return function.call_function(self.ctx, node, var, args) else: return node, self.ctx.convert.empty.to_variable(self.ctx.root_node)
def fdelete_slot(self, node, obj): return function.call_function(self.ctx, node, self.fdel, function.Args((obj, )))
def fset_slot(self, node, obj, value): return function.call_function(self.ctx, node, self.fset, function.Args((obj, value)))
def fget_slot(self, node, obj, objtype): return function.call_function(self.ctx, node, self.fget, function.Args((obj, )))