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: yield const_factory(()) return if name == self.kwarg: yield const_factory({}) return # if there is a default value, yield it. And then yield YES to reflect # we can't guess given argument value try: context = copy_context(context) for infered in self.default_value(name).infer(context): yield infered yield YES except NoDefault: yield YES
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 const_infer_unary_op(self, operator): if operator == 'not': return const_factory(not self.value) # XXX log potentially raised TypeError elif operator == '+': return const_factory(+self.value) else: # operator == '-': return const_factory(-self.value)
def _arguments_infer_argname(self, name, context): # arguments information may be missing, in which case we can't do anything # more if not (self.arguments or self.vararg or self.kwarg): yield util.Uninferable return functype = self.parent.type # first argument of instance/class method if ( self.arguments and getattr(self.arguments[0], "name", None) == name and functype != "staticmethod" ): cls = self.parent.parent.scope() is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass" # If this is a metaclass, then the first argument will always # be the class, not an instance. if context.boundnode and isinstance(context.boundnode, bases.Instance): cls = context.boundnode._proxied if is_metaclass or functype == "classmethod": yield cls return if functype == "method": yield cls.instantiate_class() return if context and context.callcontext: callee = context.callcontext.callee while hasattr(callee, "_proxied"): callee = callee._proxied if getattr(callee, "name", None) == self.parent.name: call_site = arguments.CallSite(context.callcontext, context.extra_context) yield from call_site.infer_argument(self.parent, name, context) return if name == self.vararg: vararg = nodes.const_factory(()) vararg.parent = self if not self.arguments and self.parent.name == "__init__": cls = self.parent.parent.scope() vararg.elts = [cls.instantiate_class()] yield vararg return if name == self.kwarg: kwarg = nodes.const_factory({}) kwarg.parent = self yield kwarg return # if there is a default value, yield it. And then yield Uninferable to reflect # we can't guess given argument value try: context = copy_context(context) yield from self.default_value(name).infer(context) yield util.Uninferable except NoDefault: yield util.Uninferable
def test_infered_dont_pollute(self): code = ''' def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 ''' test_utils.build_module(code) nonetype = nodes.const_factory(None) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs)
def test_inferred_dont_pollute(self): code = ''' def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 ''' builder.parse(code) nonetype = nodes.const_factory(None) self.assertNotIn('custom_attr', nonetype._locals) self.assertNotIn('custom_attr', nonetype._instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn('custom_attr', nonetype._locals) self.assertNotIn('custom_attr', nonetype._instance_attrs)
def test_infered_dont_pollute(self): code = ''' def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 ''' astroid = self.builder.string_build(code) nonetype = nodes.const_factory(None) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs)
def test_inferred_dont_pollute(self) -> None: code = """ def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 """ builder.parse(code) nonetype = nodes.const_factory(None) self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs)
def test_inferred_dont_pollute(self): code = """ def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 """ builder.parse(code) nonetype = nodes.const_factory(None) self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs)
def test_inferred_dont_pollute(self): code = ''' def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 ''' builder.parse(code) nonetype = nodes.const_factory(None) # pylint: disable=no-member; Infers two potential values self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs)
def test_inferred_dont_pollute(self): code = """ def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 """ builder.parse(code) nonetype = nodes.const_factory(None) # pylint: disable=no-member; Infers two potential values self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn("custom_attr", nonetype.locals) self.assertNotIn("custom_attr", nonetype.instance_attrs)
def test_inferred_dont_pollute(self): code = ''' def func(a=None): a.custom_attr = 0 def func2(a={}): a.custom_attr = 0 ''' builder.parse(code) nonetype = nodes.const_factory(None) # pylint: disable=no-member; union type in const_factory, this shouldn't happen self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs) nonetype = nodes.const_factory({}) self.assertNotIn('custom_attr', nonetype.locals) self.assertNotIn('custom_attr', nonetype.instance_attrs)
def build_function( name, args: Optional[List[str]] = None, posonlyargs: Optional[List[str]] = None, defaults=None, doc=None, kwonlyargs: Optional[List[str]] = None, ) -> nodes.FunctionDef: """create and initialize an astroid FunctionDef node""" # first argument is now a list of decorators func = nodes.FunctionDef(name, doc) func.args = argsnode = nodes.Arguments(parent=func) argsnode.postinit( args=[ nodes.AssignName(name=arg, parent=argsnode) for arg in args or () ], defaults=[], kwonlyargs=[ nodes.AssignName(name=arg, parent=argsnode) for arg in kwonlyargs or () ], kw_defaults=[], annotations=[], posonlyargs=[ nodes.AssignName(name=arg, parent=argsnode) for arg in posonlyargs or () ], ) for default in defaults or (): argsnode.defaults.append(nodes.const_factory(default)) argsnode.defaults[-1].parent = argsnode if args: register_arguments(func) return func
def const_infer_binary_op(self, opnode, operator, other, context, _): not_implemented = nodes.Const(NotImplemented) if isinstance(other, nodes.Const): if (operator == "**" and isinstance(self, nodes.Const) and isinstance(self.value, (int, float)) and isinstance(other.value, (int, float)) and (self.value > 1e5 or other.value > 1e5)): yield not_implemented return try: impl = BIN_OP_IMPL[operator] try: yield nodes.const_factory(impl(self.value, other.value)) except TypeError: # ArithmeticError is not enough: float >> float is a TypeError yield not_implemented except Exception: # pylint: disable=broad-except yield util.Uninferable except TypeError: yield not_implemented elif isinstance(self.value, str) and operator == "%": # TODO(cpopa): implement string interpolation later on. yield util.Uninferable else: yield not_implemented
def _infer_unaryop( self: nodes.UnaryOp, context: InferenceContext | None = None ) -> Generator[InferenceResult | util.BadUnaryOperationMessage, None, 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 AttributeInferenceError: yield util.BadUnaryOperationMessage( operand, self.op, exc) continue meth = methods[0] inferred = next(meth.infer(context=context), None) if inferred is util.Uninferable or not inferred.callable(): continue context = copy_context(context) context.boundnode = operand context.callcontext = CallContext(args=[], callee=inferred) 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 AttributeInferenceError as inner_exc: # The unary operation special method was not found. yield util.BadUnaryOperationMessage( operand, self.op, inner_exc) except InferenceError: yield util.Uninferable
def build_function(name, args=None, posonlyargs=None, defaults=None, doc=None): """create and initialize an astroid FunctionDef node""" args, defaults, posonlyargs = args or [], defaults or [], posonlyargs or [] # first argument is now a list of decorators func = nodes.FunctionDef(name, doc) func.args = argsnode = nodes.Arguments() argsnode.args = [] argsnode.posonlyargs = [] for arg in args: argsnode.args.append(nodes.Name()) argsnode.args[-1].name = arg argsnode.args[-1].parent = argsnode for arg in posonlyargs: argsnode.posonlyargs.append(nodes.Name()) argsnode.posonlyargs[-1].name = arg argsnode.posonlyargs[-1].parent = argsnode argsnode.defaults = [] for default in defaults: argsnode.defaults.append(nodes.const_factory(default)) argsnode.defaults[-1].parent = argsnode argsnode.kwarg = None argsnode.vararg = None argsnode.parent = func if args: register_arguments(func) return func
def transform_function(node): assign = nodes.Assign() name = nodes.AssignName() name.name = "value" assign.targets = [name] assign.value = nodes.const_factory(42) node.body.append(assign)
def test_builtin_lookup(self): self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ()) intstmts = scoped_nodes.builtin_lookup('int')[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.ClassDef) self.assertEqual(intstmts[0].name, 'int') self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
def _test(self, value): node = nodes.const_factory(value) self.assertIsInstance(node._proxied, nodes.Class) self.assertEqual(node._proxied.name, value.__class__.__name__) self.assertIs(node.value, value) self.assertTrue(node._proxied.parent) self.assertEqual(node._proxied.root().name, value.__class__.__module__)
def test_builtin_lookup(self) -> None: self.assertEqual(nodes.builtin_lookup("__dict__")[1], ()) intstmts = nodes.builtin_lookup("int")[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.ClassDef) self.assertEqual(intstmts[0].name, "int") self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
def transform_function(node): assign = nodes.Assign() name = nodes.AssignName() name.name = 'value' assign.targets = [name] assign.value = nodes.const_factory(42) node.body.append(assign)
def _infer_builtin_new( self, caller: nodes.Call, context: InferenceContext, ) -> collections.abc.Generator[nodes.Const | Instance | type[Uninferable], None, None]: if not caller.args: return # Attempt to create a constant if len(caller.args) > 1: value = None if isinstance(caller.args[1], nodes.Const): value = caller.args[1].value else: inferred_arg = next(caller.args[1].infer(), None) if isinstance(inferred_arg, nodes.Const): value = inferred_arg.value if value is not None: yield nodes.const_factory(value) return node_context = context.extra_context.get(caller.args[0]) for inferred in caller.args[0].infer(context=node_context): if inferred is Uninferable: yield inferred if isinstance(inferred, nodes.ClassDef): yield Instance(inferred) raise InferenceError
def test_builtin_lookup(self): self.assertEqual(builtin_lookup("__dict__")[1], ()) intstmts = builtin_lookup("int")[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.Class) self.assertEqual(intstmts[0].name, "int") self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
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 util.Uninferable return # first argument of instance/class method if self.args and getattr(self.args[0], 'name', None) == name: functype = self.parent.type cls = self.parent.parent.scope() is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == 'metaclass' # If this is a metaclass, then the first argument will always # be the class, not an instance. if is_metaclass or functype == 'classmethod': yield cls return if functype == 'method': yield bases.Instance(self.parent.parent.frame()) return if context and context.callcontext: call_site = arguments.CallSite(context.callcontext) for value in call_site.infer_argument(self.parent, name, context): yield value return # TODO: just provide the type here, no need to have an empty Dict. if name == self.vararg: vararg = nodes.const_factory(()) vararg.parent = self yield vararg return if name == self.kwarg: kwarg = nodes.const_factory({}) kwarg.parent = self yield kwarg return # if there is a default value, yield it. And then yield Uninferable to reflect # we can't guess given argument value try: context = contextmod.copy_context(context) for inferred in self.default_value(name).infer(context): yield inferred yield util.Uninferable except exceptions.NoDefault: yield util.Uninferable
def _test(self, value): node = nodes.const_factory(value) # pylint: disable=no-member; Infers two potential values self.assertIsInstance(node._proxied, nodes.ClassDef) self.assertEqual(node._proxied.name, value.__class__.__name__) self.assertIs(node.value, value) self.assertTrue(node._proxied.parent) self.assertEqual(node._proxied.root().name, value.__class__.__module__)
def test_builtin_lookup(self): self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ()) intstmts = scoped_nodes.builtin_lookup('int')[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.ClassDef) self.assertEqual(intstmts[0].name, 'int') # pylint: disable=no-member; union type in const_factory, this shouldn't happen self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
def _test(self, value): # pylint: disable=no-member; union type in const_factory, this shouldn't happen node = nodes.const_factory(value) self.assertIsInstance(node._proxied, nodes.ClassDef) self.assertEqual(node._proxied.name, value.__class__.__name__) self.assertIs(node.value, value) self.assertTrue(node._proxied.parent) self.assertEqual(node._proxied.root().name, value.__class__.__module__)
def test_builtin_lookup(self): self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ()) intstmts = scoped_nodes.builtin_lookup('int')[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.ClassDef) self.assertEqual(intstmts[0].name, 'int') # pylint: disable=no-member; Infers two potential values self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
def _infer_unary_op(obj: Any, op: str) -> ConstFactoryResult: """Perform unary operation on `obj`, unless it is `NotImplemented`. Can raise TypeError if operation is unsupported. """ if obj is NotImplemented: value = obj else: func = _UNARY_OPERATORS[op] value = func(obj) return nodes.const_factory(value)
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 build_function( name: str, args: list[str] | None = None, posonlyargs: list[str] | None = None, defaults: list[Any] | None = None, doc: str | None = None, kwonlyargs: list[str] | None = None, ) -> nodes.FunctionDef: """create and initialize an astroid FunctionDef node""" # first argument is now a list of decorators func = nodes.FunctionDef(name) argsnode = nodes.Arguments(parent=func) # If args is None we don't have any information about the signature # (in contrast to when there are no arguments and args == []). We pass # this to the builder to indicate this. if args is not None: arguments = [ nodes.AssignName(name=arg, parent=argsnode) for arg in args ] else: arguments = None argsnode.postinit( args=arguments, defaults=[], kwonlyargs=[ nodes.AssignName(name=arg, parent=argsnode) for arg in kwonlyargs or () ], kw_defaults=[], annotations=[], posonlyargs=[ nodes.AssignName(name=arg, parent=argsnode) for arg in posonlyargs or () ], ) func.postinit( args=argsnode, body=[], doc_node=nodes.Const(value=doc) if doc else None, ) for default in defaults or (): argsnode.defaults.append(nodes.const_factory(default)) argsnode.defaults[-1].parent = argsnode if args: register_arguments(func) return func
def const_infer_binary_op(self, operator, other, context, _): not_implemented = nodes.Const(NotImplemented) if isinstance(other, nodes.Const): try: impl = BIN_OP_IMPL[operator] try: yield nodes.const_factory(impl(self.value, other.value)) except TypeError: # ArithmeticError is not enough: float >> float is a TypeError yield not_implemented except Exception: # pylint: disable=broad-except yield util.Uninferable except TypeError: yield not_implemented elif isinstance(self.value, six.string_types) and operator == '%': # TODO(cpopa): implement string interpolation later on. yield util.Uninferable else: yield not_implemented
def _infer_str_format_call( node: nodes.Call, context: InferenceContext | None = None ) -> Iterator[nodes.Const | type[util.Uninferable]]: """Return a Const node based on the template and passed arguments.""" call = arguments.CallSite.from_call(node, context=context) if isinstance(node.func.expr, nodes.Name): value: nodes.Const = helpers.safe_infer(node.func.expr) else: value = node.func.expr format_template = value.value # Get the positional arguments passed inferred_positional = [ helpers.safe_infer(i, context) for i in call.positional_arguments ] if not all(isinstance(i, nodes.Const) for i in inferred_positional): return iter([util.Uninferable]) pos_values: list[str] = [i.value for i in inferred_positional] # Get the keyword arguments passed inferred_keyword = { k: helpers.safe_infer(v, context) for k, v in call.keyword_arguments.items() } if not all(isinstance(i, nodes.Const) for i in inferred_keyword.values()): return iter([util.Uninferable]) keyword_values: dict[str, str] = { k: v.value for k, v in inferred_keyword.items() } try: formatted_string = format_template.format(*pos_values, **keyword_values) except (IndexError, KeyError): # If there is an IndexError there are too few arguments to interpolate return iter([util.Uninferable]) return iter([nodes.const_factory(formatted_string)])
def build_function(name, args=None, defaults=None, flag=0, doc=None): """create and initialize a astroid Function node""" args, defaults = args or [], defaults or [] # first argument is now a list of decorators func = Function(name, doc) func.args = argsnode = Arguments() argsnode.args = [] for arg in args: argsnode.args.append(Name()) argsnode.args[-1].name = arg argsnode.args[-1].parent = argsnode argsnode.defaults = [] for default in defaults: argsnode.defaults.append(const_factory(default)) argsnode.defaults[-1].parent = argsnode argsnode.kwarg = None argsnode.vararg = None argsnode.parent = func if args: register_arguments(func) return func
def const_infer_binary_op(self, operator, other, context): for other in other.infer(context): if isinstance(other, nodes.Const): try: impl = BIN_OP_IMPL[operator] try: yield const_factory(impl(self.value, other.value)) except Exception: # ArithmeticError is not enough: float >> float is a TypeError # TODO : let pylint know about the problem pass except TypeError: # XXX log TypeError continue elif other is YES: yield other else: try: for val in other.infer_binary_op(operator, self, context): yield val except AttributeError: yield YES
def _infer_old_style_string_formatting( instance: nodes.Const, other: nodes.NodeNG, context: InferenceContext ) -> tuple[type[util.Uninferable] | nodes.Const]: """Infer the result of '"string" % ...'. TODO: Instead of returning Uninferable we should rely on the call to '%' to see if the result is actually uninferable. """ values = None if isinstance(other, nodes.Tuple): if util.Uninferable in other.elts: return (util.Uninferable, ) inferred_positional = [ helpers.safe_infer(i, context) for i in other.elts ] if all(isinstance(i, nodes.Const) for i in inferred_positional): values = tuple(i.value for i in inferred_positional) elif isinstance(other, nodes.Dict): values: dict[Any, Any] = {} for pair in other.items: key = helpers.safe_infer(pair[0], context) if not isinstance(key, nodes.Const): return (util.Uninferable, ) value = helpers.safe_infer(pair[1], context) if not isinstance(value, nodes.Const): return (util.Uninferable, ) values[key.value] = value.value elif isinstance(other, nodes.Const): values = other.value else: return (util.Uninferable, ) try: return (nodes.const_factory(instance.value % values), ) except (TypeError, KeyError, ValueError): return (util.Uninferable, )
def attach_const_node(node, name, value): """create a Const node and register it in the locals of the given node with the specified name """ if name not in node.special_attributes: _attach_local_node(node, nodes.const_factory(value), name)
def transform_function(node): return nodes.const_factory(42)
def infer_argument(self, funcnode, name, context): """infer a function argument value according to the call context Arguments: funcnode: The function being called. name: The name of the argument whose value is being inferred. context: Inference context object """ if name in self.duplicated_keywords: raise exceptions.InferenceError( "The arguments passed to {func!r} " " have duplicate keywords.", call_site=self, func=funcnode, arg=name, context=context, ) # Look into the keywords first, maybe it's already there. try: return self.keyword_arguments[name].infer(context) except KeyError: pass # Too many arguments given and no variable arguments. if len(self.positional_arguments) > len(funcnode.args.args): if not funcnode.args.vararg and not funcnode.args.posonlyargs: raise exceptions.InferenceError( "Too many positional arguments " "passed to {func!r} that does " "not have *args.", call_site=self, func=funcnode, arg=name, context=context, ) positional = self.positional_arguments[:len(funcnode.args.args)] vararg = self.positional_arguments[len(funcnode.args.args):] argindex = funcnode.args.find_argname(name)[0] kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs} kwargs = { key: value for key, value in self.keyword_arguments.items() if key not in kwonlyargs } # If there are too few positionals compared to # what the function expects to receive, check to see # if the missing positional arguments were passed # as keyword arguments and if so, place them into the # positional args list. if len(positional) < len(funcnode.args.args): for func_arg in funcnode.args.args: if func_arg.name in kwargs: arg = kwargs.pop(func_arg.name) positional.append(arg) if argindex is not None: # 2. first argument of instance/class method if argindex == 0 and funcnode.type in ("method", "classmethod"): if context.boundnode is not None: boundnode = context.boundnode else: # XXX can do better ? boundnode = funcnode.parent.frame() if isinstance(boundnode, nodes.ClassDef): # Verify that we're accessing a method # of the metaclass through a class, as in # `cls.metaclass_method`. In this case, the # first argument is always the class. method_scope = funcnode.parent.scope() if method_scope is boundnode.metaclass(): return iter((boundnode, )) if funcnode.type == "method": if not isinstance(boundnode, bases.Instance): boundnode = boundnode.instantiate_class() return iter((boundnode, )) if funcnode.type == "classmethod": return iter((boundnode, )) # if we have a method, extract one position # from the index, so we'll take in account # the extra parameter represented by `self` or `cls` if funcnode.type in ("method", "classmethod"): argindex -= 1 # 2. search arg index try: return self.positional_arguments[argindex].infer(context) except IndexError: pass if funcnode.args.kwarg == name: # It wants all the keywords that were passed into # the call site. if self.has_invalid_keywords(): raise exceptions.InferenceError( "Inference failed to find values for all keyword arguments " "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " "{keyword_arguments!r}.", keyword_arguments=self.keyword_arguments, unpacked_kwargs=self._unpacked_kwargs, call_site=self, func=funcnode, arg=name, context=context, ) kwarg = nodes.Dict( lineno=funcnode.args.lineno, col_offset=funcnode.args.col_offset, parent=funcnode.args, ) kwarg.postinit([(nodes.const_factory(key), value) for key, value in kwargs.items()]) return iter((kwarg, )) if funcnode.args.vararg == name: # It wants all the args that were passed into # the call site. if self.has_invalid_arguments(): raise exceptions.InferenceError( "Inference failed to find values for all positional " "arguments to {func!r}: {unpacked_args!r} doesn't " "correspond to {positional_arguments!r}.", positional_arguments=self.positional_arguments, unpacked_args=self._unpacked_args, call_site=self, func=funcnode, arg=name, context=context, ) args = nodes.Tuple( lineno=funcnode.args.lineno, col_offset=funcnode.args.col_offset, parent=funcnode.args, ) args.postinit(vararg) return iter((args, )) # Check if it's a default parameter. try: return funcnode.args.default_value(name).infer(context) except exceptions.NoDefault: pass raise exceptions.InferenceError( "No value found for argument {arg} to {func!r}", call_site=self, func=funcnode, arg=name, context=context, )
def transform_compare(node): # Let's check the values of the ops _, right = node.ops[0] # Assume they are Consts and they were transformed before # us. return nodes.const_factory(node.left.value < right.value)
def infer_argument(self, funcnode, name, context): """infer a function argument value according to the call context""" if name in self.duplicated_keywords: raise exceptions.InferenceError(name) # Look into the keywords first, maybe it's already there. try: return self.keyword_arguments[name].infer(context) except KeyError: pass # Too many arguments given and no variable arguments. if len(self.positional_arguments) > len(funcnode.args.args): if not funcnode.args.vararg: raise exceptions.InferenceError(name) positional = self.positional_arguments[:len(funcnode.args.args)] vararg = self.positional_arguments[len(funcnode.args.args):] argindex = funcnode.args.find_argname(name)[0] kwonlyargs = set(arg.name for arg in funcnode.args.kwonlyargs) kwargs = { key: value for key, value in self.keyword_arguments.items() if key not in kwonlyargs } # If there are too few positionals compared to # what the function expects to receive, check to see # if the missing positional arguments were passed # as keyword arguments and if so, place them into the # positional args list. if len(positional) < len(funcnode.args.args): for func_arg in funcnode.args.args: if func_arg.name in kwargs: arg = kwargs.pop(func_arg.name) positional.append(arg) if argindex is not None: # 2. first argument of instance/class method if argindex == 0 and funcnode.type in ('method', 'classmethod'): if context.boundnode is not None: boundnode = context.boundnode else: # XXX can do better ? boundnode = funcnode.parent.frame() if funcnode.type == 'method': if not isinstance(boundnode, bases.Instance): boundnode = bases.Instance(boundnode) return iter((boundnode,)) if funcnode.type == 'classmethod': return iter((boundnode,)) # if we have a method, extract one position # from the index, so we'll take in account # the extra parameter represented by `self` or `cls` if funcnode.type in ('method', 'classmethod'): argindex -= 1 # 2. search arg index try: return self.positional_arguments[argindex].infer(context) except IndexError: pass if funcnode.args.kwarg == name: # It wants all the keywords that were passed into # the call site. if self.has_invalid_keywords(): raise exceptions.InferenceError kwarg = nodes.Dict() kwarg.lineno = funcnode.args.lineno kwarg.col_offset = funcnode.args.col_offset kwarg.parent = funcnode.args items = [(nodes.const_factory(key), value) for key, value in kwargs.items()] kwarg.items = items return iter((kwarg, )) elif funcnode.args.vararg == name: # It wants all the args that were passed into # the call site. if self.has_invalid_arguments(): raise exceptions.InferenceError args = nodes.Tuple() args.lineno = funcnode.args.lineno args.col_offset = funcnode.args.col_offset args.parent = funcnode.args args.elts = vararg return iter((args, )) # Check if it's a default parameter. try: return funcnode.args.default_value(name).infer(context) except exceptions.NoDefault: pass raise exceptions.InferenceError(name)
def infer_argument(self, funcnode, name, context): """infer a function argument value according to the call context""" # 1. search in named keywords try: return self.nargs[name].infer(context) except KeyError: # Function.args.args can be None in astroid (means that we don't have # information on argnames) argindex = funcnode.args.find_argname(name)[0] if argindex is not None: # 2. first argument of instance/class method if argindex == 0 and funcnode.type in ('method', 'classmethod'): if context.boundnode is not None: boundnode = context.boundnode else: # XXX can do better ? boundnode = funcnode.parent.frame() if funcnode.type == 'method': if not isinstance(boundnode, Instance): boundnode = Instance(boundnode) return iter((boundnode,)) if funcnode.type == 'classmethod': return iter((boundnode,)) # if we have a method, extract one position # from the index, so we'll take in account # the extra parameter represented by `self` or `cls` if funcnode.type in ('method', 'classmethod'): argindex -= 1 # 2. search arg index try: return self.args[argindex].infer(context) except IndexError: pass # 3. search in *args (.starargs) if self.starargs is not None: its = [] for infered in self.starargs.infer(context): if infered is YES: its.append((YES,)) continue try: its.append(infered.getitem(argindex, context).infer(context)) except (InferenceError, AttributeError): its.append((YES,)) except (IndexError, TypeError): continue if its: return chain(*its) # 4. XXX search in **kwargs (.dstarargs) if self.dstarargs is not None: its = [] for infered in self.dstarargs.infer(context): if infered is YES: its.append((YES,)) continue try: its.append(infered.getitem(name, context).infer(context)) except (InferenceError, AttributeError): its.append((YES,)) except (IndexError, TypeError): continue if its: return chain(*its) # 5. */** argument, (Tuple or Dict) if name == funcnode.args.vararg: return iter((nodes.const_factory(()))) if name == funcnode.args.kwarg: return iter((nodes.const_factory({}))) # 6. return default value if any try: return funcnode.args.default_value(name).infer(context) except NoDefault: raise InferenceError(name)
def dict_infer_unary_op(self, operator): if operator == 'not': return const_factory(not bool(self.items)) raise TypeError() # XXX log unsupported operation
def attach_const_node(node, name, value): """create a Const node and register it in the locals of the given node with the specified name """ if not name in node.special_attributes: _attach_local_node(node, const_factory(value), name)
def _infer_unary_op(obj, op): func = _UNARY_OPERATORS[op] value = func(obj) return nodes.const_factory(value)
def infer_argument(self, funcnode, name, context): """infer a function argument value according to the call context Arguments: funcnode: The function being called. name: The name of the argument whose value is being inferred. context: Inference context object """ if name in self.duplicated_keywords: raise exceptions.InferenceError('The arguments passed to {func!r} ' ' have duplicate keywords.', call_site=self, func=funcnode, arg=name, context=context) # Look into the keywords first, maybe it's already there. try: return self.keyword_arguments[name].infer(context) except KeyError: pass # Too many arguments given and no variable arguments. if len(self.positional_arguments) > len(funcnode.args.args): if not funcnode.args.vararg: raise exceptions.InferenceError('Too many positional arguments ' 'passed to {func!r} that does ' 'not have *args.', call_site=self, func=funcnode, arg=name, context=context) positional = self.positional_arguments[:len(funcnode.args.args)] vararg = self.positional_arguments[len(funcnode.args.args):] argindex = funcnode.args.find_argname(name)[0] kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs} kwargs = { key: value for key, value in self.keyword_arguments.items() if key not in kwonlyargs } # If there are too few positionals compared to # what the function expects to receive, check to see # if the missing positional arguments were passed # as keyword arguments and if so, place them into the # positional args list. if len(positional) < len(funcnode.args.args): for func_arg in funcnode.args.args: if func_arg.name in kwargs: arg = kwargs.pop(func_arg.name) positional.append(arg) if argindex is not None: # 2. first argument of instance/class method if argindex == 0 and funcnode.type in ('method', 'classmethod'): if context.boundnode is not None: boundnode = context.boundnode else: # XXX can do better ? boundnode = funcnode.parent.frame() if isinstance(boundnode, nodes.ClassDef): # Verify that we're accessing a method # of the metaclass through a class, as in # `cls.metaclass_method`. In this case, the # first argument is always the class. method_scope = funcnode.parent.scope() if method_scope is boundnode.metaclass(): return iter((boundnode, )) if funcnode.type == 'method': if not isinstance(boundnode, bases.Instance): boundnode = bases.Instance(boundnode) return iter((boundnode,)) if funcnode.type == 'classmethod': return iter((boundnode,)) # if we have a method, extract one position # from the index, so we'll take in account # the extra parameter represented by `self` or `cls` if funcnode.type in ('method', 'classmethod'): argindex -= 1 # 2. search arg index try: return self.positional_arguments[argindex].infer(context) except IndexError: pass if funcnode.args.kwarg == name: # It wants all the keywords that were passed into # the call site. if self.has_invalid_keywords(): raise exceptions.InferenceError( "Inference failed to find values for all keyword arguments " "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " "{keyword_arguments!r}.", keyword_arguments=self.keyword_arguments, unpacked_kwargs=self._unpacked_kwargs, call_site=self, func=funcnode, arg=name, context=context) kwarg = nodes.Dict(lineno=funcnode.args.lineno, col_offset=funcnode.args.col_offset, parent=funcnode.args) kwarg.postinit([(nodes.const_factory(key), value) for key, value in kwargs.items()]) return iter((kwarg, )) elif funcnode.args.vararg == name: # It wants all the args that were passed into # the call site. if self.has_invalid_arguments(): raise exceptions.InferenceError( "Inference failed to find values for all positional " "arguments to {func!r}: {unpacked_args!r} doesn't " "correspond to {positional_arguments!r}.", positional_arguments=self.positional_arguments, unpacked_args=self._unpacked_args, call_site=self, func=funcnode, arg=name, context=context) args = nodes.Tuple(lineno=funcnode.args.lineno, col_offset=funcnode.args.col_offset, parent=funcnode.args) args.postinit(vararg) return iter((args, )) # Check if it's a default parameter. try: return funcnode.args.default_value(name).infer(context) except exceptions.NoDefault: pass raise exceptions.InferenceError('No value found for argument {name} to ' '{func!r}', call_site=self, func=funcnode, arg=name, context=context)