From 69aea200d5ffbbb6c638f94f55e541e58ab46164 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Wed, 11 Sep 2024 16:22:19 -0700 Subject: [PATCH] ensure that all nodes have a Module as their eventual root that involved several changes - creating an "adhoc" module, specifically for nodes that are not based in the real syntactic tree, but created "ad-hoc". See "__astroid_adhoc" and 'AstroidManager().adhoc_module'. - eliminating all "Unknown" nodes as parents. They are not 'Module's. Most of the time, it is sufficient to either specify the actual parent in the constructor or the adhoc module from above. - fixing construction of in-place properties (`bar = property(getter)`). They just create a nameless object, not the one with the name of the getter. Thus, the name was changed to "". Furthermore, the definition of that property is not attached to any scope, as it's again nameless. - using "tuple" ClassDef for a base of 'namedtuple' instead of a Name. We're already doing it for "enum"s, and I don't know how to ensure that the Name actually refers to the actual tuple, and not something shadowing it in the scope. Removed the test that asserted that the base is a Name and not a ClassDef. If it is actually useful, it should be checked and reworked comprehensively across all nodes (cf. Enum). - appending a node to the body of the frame when it is also the parent. If it's not a parent, then the node should belong to the "body" of the parent if it existed. An example is a definition within an "if", where the parent is the If node, but the frame is the whole module. See FunctionDef.__init__. - fixing inference of the "special" attributes of Instances. Before these changes, the FunctionDef attributes wouldn't get wrapped into a BoundMethod. This was facilitated by extracting the logic of inferring attributes into 'FunctionDef._infer_attrs' and reusing it in BaseInstance. This issue wasn't visible before, because the special attributes were simply not found due not being attached to the parent (the instance). Which in turn was caused by not providing the actual parent in the constructor. - enforcing a non-None parent in various builders in raw_building.py - fixing tests to accomodate for changes attach classes to their parent; fix bugs uncovered by this 1. some '__doc__' fields of standard library symbols (e.g. WrapperDescriptorType.__doc__) don't return a string, they return a 'getset_descriptor'. Thus, an attempt to print "as string" fails. The solution is to check that __doc__ is an instance of str. 2. A "temporary_class" was attached to a function node, when it shouldn't have been. The solution is to reattach it to the adhoc module. --- astroid/nodes/scoped_nodes/scoped_nodes.py | 3 +-- astroid/raw_building.py | 12 ++++-------- tests/test_nodes.py | 5 ++++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 14f75ff9e3..a00a0f05fd 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -2067,7 +2067,7 @@ def _infer_type_call(self, caller, context): col_offset=0, end_lineno=0, end_col_offset=0, - parent=Unknown(), + parent=caller.parent, ) # Get the bases of the class. @@ -2101,7 +2101,6 @@ def _infer_type_call(self, caller, context): if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): result.locals[attr.value] = [value] - result.parent = caller.parent return result def infer_call_result( diff --git a/astroid/raw_building.py b/astroid/raw_building.py index 281d807419..156cbea1c1 100644 --- a/astroid/raw_building.py +++ b/astroid/raw_building.py @@ -626,9 +626,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - _GeneratorType.parent = astroid_builtin generator_doc_node = ( nodes.Const(value=types.GeneratorType.__doc__) if types.GeneratorType.__doc__ @@ -650,9 +649,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - _AsyncGeneratorType.parent = astroid_builtin async_generator_doc_node = ( nodes.Const(value=types.AsyncGeneratorType.__doc__) if types.AsyncGeneratorType.__doc__ @@ -674,9 +672,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - _UnionTypeType.parent = astroid_builtin union_type_doc_node = ( nodes.Const(value=types.UnionType.__doc__) if types.UnionType.__doc__ @@ -711,9 +708,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - klass.parent = astroid_builtin doc = _type.__doc__ if isinstance(_type.__doc__, str) else None klass.postinit( bases=[], diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 64cae2f676..5ec66ce581 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -37,6 +37,7 @@ ParentMissingError, StatementMissing, ) +from astroid.manager import AstroidManager from astroid.nodes.node_classes import ( AssignAttr, AssignName, @@ -1973,7 +1974,9 @@ def test_str_repr_no_warnings(node): if name == "self": continue - if "int" in param_type.annotation: + if name == "parent" and "NodeNG" in param_type.annotation: + args[name] = AstroidManager().adhoc_module + elif "int" in param_type.annotation: args[name] = random.randint(0, 50) elif ( "NodeNG" in param_type.annotation