Skip to content

Commit 32cf624

Browse files
committed
set proper parents for namedtuple's and enum's
- 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). it's a part of the campaign to get rid of non-module roots
1 parent dcf081c commit 32cf624

File tree

2 files changed

+25
-37
lines changed

2 files changed

+25
-37
lines changed

astroid/brain/brain_namedtuple_enum.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ def _extract_namedtuple_arg_or_keyword( # pylint: disable=inconsistent-return-s
7474

7575
def infer_func_form(
7676
node: nodes.Call,
77-
base_type: list[nodes.NodeNG],
77+
base_type: nodes.NodeNG,
78+
*,
79+
parent: nodes.NodeNG,
7880
context: InferenceContext | None = None,
7981
enum: bool = False,
8082
) -> tuple[nodes.ClassDef, str, list[str]]:
@@ -147,15 +149,11 @@ def infer_func_form(
147149
col_offset=node.col_offset,
148150
end_lineno=node.end_lineno,
149151
end_col_offset=node.end_col_offset,
150-
parent=nodes.Unknown(),
152+
parent=parent,
151153
)
152-
# A typical ClassDef automatically adds its name to the parent scope,
153-
# but doing so causes problems, so defer setting parent until after init
154-
# see: https://github.com/pylint-dev/pylint/issues/5982
155-
class_node.parent = node.parent
156154
class_node.postinit(
157155
# set base class=tuple
158-
bases=base_type,
156+
bases=[base_type],
159157
body=[],
160158
decorators=None,
161159
)
@@ -195,25 +193,18 @@ def infer_named_tuple(
195193
node: nodes.Call, context: InferenceContext | None = None
196194
) -> Iterator[nodes.ClassDef]:
197195
"""Specific inference function for namedtuple Call node."""
198-
tuple_base_name: list[nodes.NodeNG] = [
199-
nodes.Name(
200-
name="tuple",
201-
parent=node.root(),
202-
lineno=0,
203-
col_offset=0,
204-
end_lineno=None,
205-
end_col_offset=None,
206-
)
207-
]
196+
tuple_base = util.safe_infer(_extract_single_node("tuple"))
197+
assert isinstance(tuple_base, nodes.NodeNG)
198+
208199
class_node, name, attributes = infer_func_form(
209-
node, tuple_base_name, context=context
200+
node, tuple_base, parent=AstroidManager().adhoc_module, context=context
210201
)
202+
211203
call_site = arguments.CallSite.from_call(node, context=context)
212-
node = extract_node("import collections; collections.namedtuple")
213-
try:
214-
func = next(node.infer())
215-
except StopIteration as e:
216-
raise InferenceError(node=node) from e
204+
func = util.safe_infer(
205+
_extract_single_node("import collections; collections.namedtuple")
206+
)
207+
assert isinstance(func, nodes.NodeNG)
217208
try:
218209
rename = next(
219210
call_site.infer_argument(func, "rename", context or InferenceContext())
@@ -364,7 +355,17 @@ def value(self):
364355
__members__ = ['']
365356
"""
366357
)
367-
class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0]
358+
359+
# FIXME arguably, the base here shouldn't be the EnumMeta class definition
360+
# itself, but a reference (Name) to it. Otherwise, the invariant that all
361+
# children of a node have that node as their parent is broken.
362+
class_node = infer_func_form(
363+
node,
364+
enum_meta,
365+
parent=AstroidManager().adhoc_module,
366+
context=context,
367+
enum=True,
368+
)[0]
368369
return iter([class_node.instantiate_class()])
369370

370371

tests/brain/test_named_tuple.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -175,19 +175,6 @@ def test_namedtuple_func_form_args_and_kwargs(self) -> None:
175175
self.assertIn("b", inferred.locals)
176176
self.assertIn("c", inferred.locals)
177177

178-
def test_namedtuple_bases_are_actually_names_not_nodes(self) -> None:
179-
node = builder.extract_node(
180-
"""
181-
from collections import namedtuple
182-
Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE)
183-
Tuple #@
184-
"""
185-
)
186-
inferred = next(node.infer())
187-
self.assertIsInstance(inferred, astroid.ClassDef)
188-
self.assertIsInstance(inferred.bases[0], astroid.Name)
189-
self.assertEqual(inferred.bases[0].name, "tuple")
190-
191178
def test_invalid_label_does_not_crash_inference(self) -> None:
192179
code = """
193180
import collections

0 commit comments

Comments
 (0)