From aa4f5bed58dc6521a6c2c0927ca0e0da48fd5ea5 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com> Date: Wed, 29 Dec 2021 20:53:33 +0530 Subject: [PATCH] Add future=True to frame calls (#1305) Co-authored-by: Pierre Sassoulas Co-authored-by: Jacob Walls --- astroid/arguments.py | 2 +- astroid/bases.py | 4 ++-- astroid/builder.py | 4 ++-- astroid/helpers.py | 2 +- astroid/nodes/node_classes.py | 6 ++--- astroid/nodes/scoped_nodes/scoped_nodes.py | 26 ++++++++++++---------- astroid/protocols.py | 2 +- tests/unittest_builder.py | 5 +++++ tests/unittest_inference.py | 2 ++ tests/unittest_manager.py | 4 ++++ tests/unittest_nodes.py | 10 +++++++++ tests/unittest_scoped_nodes.py | 9 ++++++++ 12 files changed, 54 insertions(+), 22 deletions(-) diff --git a/astroid/arguments.py b/astroid/arguments.py index fadb5a8b94..9ae8bab26d 100644 --- a/astroid/arguments.py +++ b/astroid/arguments.py @@ -226,7 +226,7 @@ def infer_argument(self, funcnode, name, context): return positional[0].infer(context=context) if boundnode is None: # XXX can do better ? - boundnode = funcnode.parent.frame() + boundnode = funcnode.parent.frame(future=True) if isinstance(boundnode, nodes.ClassDef): # Verify that we're accessing a method diff --git a/astroid/bases.py b/astroid/bases.py index c39e887a26..9fbdc3a3b3 100644 --- a/astroid/bases.py +++ b/astroid/bases.py @@ -368,7 +368,7 @@ class UnboundMethod(Proxy): special_attributes = lazy_descriptor(lambda: objectmodel.UnboundMethodModel()) def __repr__(self): - frame = self._proxied.parent.frame() + frame = self._proxied.parent.frame(future=True) return "<{} {} of {} at 0x{}".format( self.__class__.__name__, self._proxied.name, frame.qname(), id(self) ) @@ -404,7 +404,7 @@ def infer_call_result(self, caller, context): # instance of the class given as first argument. if ( self._proxied.name == "__new__" - and self._proxied.parent.frame().qname() == "builtins.object" + and self._proxied.parent.frame(future=True).qname() == "builtins.object" ): if caller.args: node_context = context.extra_context.get(caller.args[0]) diff --git a/astroid/builder.py b/astroid/builder.py index ea9d7dc867..150eeed8b4 100644 --- a/astroid/builder.py +++ b/astroid/builder.py @@ -234,7 +234,7 @@ def delayed_assattr(self, node): This adds name to locals and handle members definition. """ try: - frame = node.frame() + frame = node.frame(future=True) for inferred in node.expr.infer(): if inferred is util.Uninferable: continue @@ -263,7 +263,7 @@ def delayed_assattr(self, node): if ( frame.name == "__init__" and values - and values[0].frame().name != "__init__" + and values[0].frame(future=True).name != "__init__" ): values.insert(0, node) else: diff --git a/astroid/helpers.py b/astroid/helpers.py index eadd8cb6ab..6e74558bee 100644 --- a/astroid/helpers.py +++ b/astroid/helpers.py @@ -255,7 +255,7 @@ def object_len(node, context=None): # prevent self referential length calls from causing a recursion error # see https://github.com/PyCQA/astroid/issues/777 - node_frame = node.frame() + node_frame = node.frame(future=True) if ( isinstance(node_frame, scoped_nodes.FunctionDef) and node_frame.name == "__len__" diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index 4e33bb5d1a..0f196812ca 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -4815,9 +4815,9 @@ def frame( raise ParentMissingError(target=self.parent) if not self.parent.parent.parent: raise ParentMissingError(target=self.parent.parent) - return self.parent.parent.parent.frame() + return self.parent.parent.parent.frame(future=True) - return self.parent.frame() + return self.parent.frame(future=True) def scope(self) -> "LocalsDictNodeNG": """The first parent node defining a new scope. @@ -4849,7 +4849,7 @@ def set_local(self, name: str, stmt: AssignName) -> None: :param stmt: The statement that defines the given name. """ - self.frame().set_local(name, stmt) + self.frame(future=True).set_local(name, stmt) class Unknown(mixins.AssignTypeMixin, NodeNG): diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index f2720c4cce..15e9c1c901 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -244,7 +244,7 @@ def qname(self): # pylint: disable=no-member; github.com/pycqa/astroid/issues/278 if self.parent is None: return self.name - return f"{self.parent.frame().qname()}.{self.name}" + return f"{self.parent.frame(future=True).qname()}.{self.name}" def scope(self: T) -> T: """The first parent node defining a new scope. @@ -1452,7 +1452,7 @@ def scope_lookup(self, node, name, offset=0): :rtype: tuple(str, list(NodeNG)) """ if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() + frame = self.parent.frame(future=True) # line offset to avoid that def func(f=func) resolve the default # value to the defined function offset = -1 @@ -1595,7 +1595,7 @@ def __init__( parent=parent, ) if parent: - frame = parent.frame() + frame = parent.frame(future=True) frame.set_local(name, self) # pylint: disable=arguments-differ; different than Lambdas @@ -1641,7 +1641,7 @@ def extra_decorators(self): :type: list(NodeNG) """ - frame = self.parent.frame() + frame = self.parent.frame(future=True) if not isinstance(frame, ClassDef): return [] @@ -1668,7 +1668,7 @@ def extra_decorators(self): # original method. if ( isinstance(meth, FunctionDef) - and assign_node.frame() == frame + and assign_node.frame(future=True) == frame ): decorators.append(assign.value) return decorators @@ -1687,7 +1687,7 @@ def type( if decorator.func.name in BUILTIN_DESCRIPTORS: return decorator.func.name - frame = self.parent.frame() + frame = self.parent.frame(future=True) type_name = "function" if isinstance(frame, ClassDef): if self.name == "__new__": @@ -1815,7 +1815,9 @@ def is_method(self): """ # check we are defined in a ClassDef, because this is usually expected # (e.g. pylint...) when is_method() return True - return self.type != "function" and isinstance(self.parent.frame(), ClassDef) + return self.type != "function" and isinstance( + self.parent.frame(future=True), ClassDef + ) @decorators_mod.cached def decoratornames(self, context=None): @@ -1999,7 +2001,7 @@ def scope_lookup(self, node, name, offset=0): # if any methods in a class body refer to either __class__ or super. # In our case, we want to be able to look it up in the current scope # when `__class__` is being used. - frame = self.parent.frame() + frame = self.parent.frame(future=True) if isinstance(frame, ClassDef): return self, [frame] return super().scope_lookup(node, name, offset) @@ -2124,12 +2126,12 @@ def get_wrapping_class(node): :rtype: ClassDef or None """ - klass = node.frame() + klass = node.frame(future=True) while klass is not None and not isinstance(klass, ClassDef): if klass.parent is None: klass = None else: - klass = klass.parent.frame() + klass = klass.parent.frame(future=True) return klass @@ -2260,7 +2262,7 @@ def __init__( parent=parent, ) if parent is not None: - parent.frame().set_local(name, self) + parent.frame(future=True).set_local(name, self) for local_name, node in self.implicit_locals(): self.add_local_node(node, local_name) @@ -2517,7 +2519,7 @@ def scope_lookup(self, node, name, offset=0): # class A(name.Name): # def name(self): ... - frame = self.parent.frame() + frame = self.parent.frame(future=True) # line offset to avoid that class A(A) resolve the ancestor to # the defined class offset = -1 diff --git a/astroid/protocols.py b/astroid/protocols.py index 58b60834f5..ef7f5c96f2 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -411,7 +411,7 @@ def arguments_assigned_stmts( if ( context.callcontext and node - and getattr(callee, "name", None) == node.frame().name + and getattr(callee, "name", None) == node.frame(future=True).name ): # reset call context/name callcontext = context.callcontext diff --git a/tests/unittest_builder.py b/tests/unittest_builder.py index 7dffbb7292..c1aa3bd424 100644 --- a/tests/unittest_builder.py +++ b/tests/unittest_builder.py @@ -609,6 +609,7 @@ def test_module_base_props(self) -> None: self.assertEqual(module.fromlineno, 0) self.assertIsNone(module.parent) self.assertEqual(module.frame(), module) + self.assertEqual(module.frame(future=True), module) self.assertEqual(module.root(), module) self.assertEqual(module.file, os.path.abspath(resources.find("data/module.py"))) self.assertEqual(module.pure_python, 1) @@ -651,6 +652,8 @@ def test_function_base_props(self) -> None: self.assertTrue(function.parent) self.assertEqual(function.frame(), function) self.assertEqual(function.parent.frame(), module) + self.assertEqual(function.frame(future=True), function) + self.assertEqual(function.parent.frame(future=True), module) self.assertEqual(function.root(), module) self.assertEqual([n.name for n in function.args.args], ["key", "val"]) self.assertEqual(function.type, "function") @@ -672,6 +675,8 @@ def test_class_base_props(self) -> None: self.assertTrue(klass.parent) self.assertEqual(klass.frame(), klass) self.assertEqual(klass.parent.frame(), module) + self.assertEqual(klass.frame(future=True), klass) + self.assertEqual(klass.parent.frame(future=True), module) self.assertEqual(klass.root(), module) self.assertEqual(klass.basenames, []) self.assertTrue(klass.newstyle) diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py index 89e9d59d88..8a53899b14 100644 --- a/tests/unittest_inference.py +++ b/tests/unittest_inference.py @@ -337,6 +337,7 @@ def test_unbound_method_inference(self) -> None: self.assertIsInstance(meth1, UnboundMethod) self.assertEqual(meth1.name, "meth1") self.assertEqual(meth1.parent.frame().name, "C") + self.assertEqual(meth1.parent.frame(future=True).name, "C") self.assertRaises(StopIteration, partial(next, inferred)) def test_bound_method_inference(self) -> None: @@ -345,6 +346,7 @@ def test_bound_method_inference(self) -> None: self.assertIsInstance(meth1, BoundMethod) self.assertEqual(meth1.name, "meth1") self.assertEqual(meth1.parent.frame().name, "C") + self.assertEqual(meth1.parent.frame(future=True).name, "C") self.assertRaises(StopIteration, partial(next, inferred)) def test_args_default_inference1(self) -> None: diff --git a/tests/unittest_manager.py b/tests/unittest_manager.py index f2feea17ab..69a91d87be 100644 --- a/tests/unittest_manager.py +++ b/tests/unittest_manager.py @@ -280,10 +280,12 @@ def test_ast_from_class(self) -> None: ast = self.manager.ast_from_class(int) self.assertEqual(ast.name, "int") self.assertEqual(ast.parent.frame().name, "builtins") + self.assertEqual(ast.parent.frame(future=True).name, "builtins") ast = self.manager.ast_from_class(object) self.assertEqual(ast.name, "object") self.assertEqual(ast.parent.frame().name, "builtins") + self.assertEqual(ast.parent.frame(future=True).name, "builtins") self.assertIn("__setattr__", ast) def test_ast_from_class_with_module(self) -> None: @@ -291,10 +293,12 @@ def test_ast_from_class_with_module(self) -> None: ast = self.manager.ast_from_class(int, int.__module__) self.assertEqual(ast.name, "int") self.assertEqual(ast.parent.frame().name, "builtins") + self.assertEqual(ast.parent.frame(future=True).name, "builtins") ast = self.manager.ast_from_class(object, object.__module__) self.assertEqual(ast.name, "object") self.assertEqual(ast.parent.frame().name, "builtins") + self.assertEqual(ast.parent.frame(future=True).name, "builtins") self.assertIn("__setattr__", ast) def test_ast_from_class_attr_error(self) -> None: diff --git a/tests/unittest_nodes.py b/tests/unittest_nodes.py index aab8c5a123..48f767c76a 100644 --- a/tests/unittest_nodes.py +++ b/tests/unittest_nodes.py @@ -741,25 +741,35 @@ def func_with_lambda( ) function = module.body[0] assert function.args.frame() == function + assert function.args.frame(future=True) == function function_two = module.body[1] assert function_two.args.args[0].frame() == function_two + assert function_two.args.args[0].frame(future=True) == function_two assert function_two.args.args[1].frame() == function_two + assert function_two.args.args[1].frame(future=True) == function_two assert function_two.args.defaults[0].frame() == module + assert function_two.args.defaults[0].frame(future=True) == module inherited_class = module.body[3] assert inherited_class.keywords[0].frame() == inherited_class + assert inherited_class.keywords[0].frame(future=True) == inherited_class assert inherited_class.keywords[0].value.frame() == module + assert inherited_class.keywords[0].value.frame(future=True) == module lambda_assignment = module.body[4].value assert lambda_assignment.args.args[0].frame() == lambda_assignment + assert lambda_assignment.args.args[0].frame(future=True) == lambda_assignment assert lambda_assignment.args.defaults[0].frame() == module + assert lambda_assignment.args.defaults[0].frame(future=True) == module lambda_named_expr = module.body[5].args.defaults[0] assert lambda_named_expr.value.args.defaults[0].frame() == module + assert lambda_named_expr.value.args.defaults[0].frame(future=True) == module comprehension = module.body[6].value assert comprehension.generators[0].ifs[0].frame() == module + assert comprehension.generators[0].ifs[0].frame(future=True) == module @staticmethod def test_scope() -> None: diff --git a/tests/unittest_scoped_nodes.py b/tests/unittest_scoped_nodes.py index 0aa3e5d7ab..33e1002d59 100644 --- a/tests/unittest_scoped_nodes.py +++ b/tests/unittest_scoped_nodes.py @@ -2315,17 +2315,24 @@ def method(): ) function = module.body[0] assert function.frame() == function + assert function.frame(future=True) == function assert function.body[0].frame() == function + assert function.body[0].frame(future=True) == function class_node = module.body[1] assert class_node.frame() == class_node + assert class_node.frame(future=True) == class_node assert class_node.body[0].frame() == class_node + assert class_node.body[0].frame(future=True) == class_node assert class_node.body[1].frame() == class_node.body[1] + assert class_node.body[1].frame(future=True) == class_node.body[1] lambda_assignment = module.body[2].value assert lambda_assignment.args.args[0].frame() == lambda_assignment + assert lambda_assignment.args.args[0].frame(future=True) == lambda_assignment assert module.frame() == module + assert module.frame(future=True) == module @staticmethod def test_non_frame_node(): @@ -2338,8 +2345,10 @@ def test_non_frame_node(): """ ) assert module.body[0].frame() == module + assert module.body[0].frame(future=True) == module assert module.body[1].value.locals["x"][0].frame() == module + assert module.body[1].value.locals["x"][0].frame(future=True) == module if __name__ == "__main__":