Skip to content

Commit b693781

Browse files
committed
WIP removed Unknown as a parent node
1 parent 261ca0b commit b693781

18 files changed

+138
-137
lines changed

astroid/bases.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"""This module contains base classes and functions for the nodes and some
66
inference utils.
77
"""
8+
# isort: off
9+
810
from __future__ import annotations
911

1012
import collections
@@ -273,27 +275,26 @@ def igetattr(
273275
try:
274276
context.lookupname = name
275277
# XXX frame should be self._proxied, or not ?
276-
get_attr = self.getattr(name, context, lookupclass=False)
277-
yield from _infer_stmts(
278-
self._wrap_attr(get_attr, context), context, frame=self
279-
)
278+
attrs = self.getattr(name, context, lookupclass=False)
279+
iattrs = self._proxied._infer_attrs(attrs, context, class_context=False)
280+
yield from self._wrap_attr(iattrs)
280281
except AttributeInferenceError:
281282
try:
282283
# fallback to class.igetattr since it has some logic to handle
283284
# descriptors
284285
# But only if the _proxied is the Class.
285286
if self._proxied.__class__.__name__ != "ClassDef":
286287
raise
287-
attrs = self._proxied.igetattr(name, context, class_context=False)
288-
yield from self._wrap_attr(attrs, context)
288+
iattrs = self._proxied.igetattr(name, context, class_context=False)
289+
yield from self._wrap_attr(iattrs, context)
289290
except AttributeInferenceError as error:
290291
raise InferenceError(**vars(error)) from error
291292

292293
def _wrap_attr(
293-
self, attrs: Iterable[InferenceResult], context: InferenceContext | None = None
294+
self, iattrs: Iterable[InferenceResult], context: InferenceContext | None = None
294295
) -> Iterator[InferenceResult]:
295296
"""Wrap bound methods of attrs in a InstanceMethod proxies."""
296-
for attr in attrs:
297+
for attr in iattrs:
297298
if isinstance(attr, UnboundMethod):
298299
if _is_property(attr):
299300
yield from attr.infer_call_result(self, context)

astroid/brain/brain_argparse.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,10 @@ def infer_namespace(node, context: InferenceContext | None = None):
2020
"Namespace",
2121
lineno=node.lineno,
2222
col_offset=node.col_offset,
23-
parent=nodes.Unknown(),
23+
parent=AstroidManager().adhoc_module, # this class is not real
2424
end_lineno=node.end_lineno,
2525
end_col_offset=node.end_col_offset,
2626
)
27-
# Set parent manually until ClassDef constructor fixed:
28-
# https://github.com/pylint-dev/astroid/issues/1490
29-
class_node.parent = node.parent
3027
for attr in set(callsite.keyword_arguments):
3128
fake_node = nodes.EmptyNode()
3229
fake_node.parent = class_node

astroid/brain/brain_builtin_inference.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
)
2424
from astroid.manager import AstroidManager
2525
from astroid.nodes import scoped_nodes
26+
from astroid.raw_building import build_module
2627
from astroid.typing import (
2728
ConstFactoryResult,
2829
InferenceResult,
@@ -163,6 +164,8 @@ def _extend_builtins(class_transforms):
163164

164165
def on_bootstrap():
165166
"""Called by astroid_bootstrapping()."""
167+
AstroidManager().cache_module(build_module("__astroid_adhoc"))
168+
166169
_extend_builtins(
167170
{
168171
"bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),

astroid/brain/brain_namedtuple_enum.py

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

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

369370

astroid/helpers.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ def safe_infer(
3737

3838

3939
def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
40-
proxy = raw_building.build_class(cls_name)
41-
proxy.parent = builtins
40+
proxy = raw_building.build_class(cls_name, builtins)
4241
return proxy
4342

4443

astroid/interpreter/objectmodel.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
mechanism.
2222
"""
2323

24+
# isort: off
25+
2426
from __future__ import annotations
2527

2628
import itertools
@@ -493,7 +495,7 @@ def __init__(self):
493495
super().__init__()
494496

495497
@property
496-
def attr___annotations__(self) -> node_classes.Unkown:
498+
def attr___annotations__(self) -> node_classes.Unknown:
497499
return node_classes.Unknown()
498500

499501
@property

astroid/manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def unregister_transform(self):
115115
def builtins_module(self) -> nodes.Module:
116116
return self.astroid_cache["builtins"]
117117

118+
@property
119+
def adhoc_module(self) -> nodes.Module:
120+
return self.astroid_cache["__astroid_adhoc"]
121+
118122
@property
119123
def prefer_stubs(self) -> bool:
120124
return AstroidManager.brain["prefer_stubs"]

astroid/nodes/scoped_nodes/scoped_nodes.py

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
Lambda, GeneratorExp, DictComp and SetComp to some extent).
99
"""
1010

11+
# isort: skip_file
12+
1113
from __future__ import annotations
1214

1315
import io
@@ -44,7 +46,6 @@
4446
Arguments,
4547
Const,
4648
NodeNG,
47-
Unknown,
4849
_base_nodes,
4950
const_factory,
5051
node_classes,
@@ -1166,9 +1167,11 @@ def __init__(
11661167
end_col_offset=end_col_offset,
11671168
parent=parent,
11681169
)
1169-
if parent and not isinstance(parent, Unknown):
1170+
if parent:
11701171
frame = parent.frame()
11711172
frame.set_local(name, self)
1173+
if frame is parent:
1174+
frame._append_node(self)
11721175

11731176
def postinit(
11741177
self,
@@ -1935,7 +1938,7 @@ def __init__(
19351938
end_col_offset=end_col_offset,
19361939
parent=parent,
19371940
)
1938-
if parent and not isinstance(parent, Unknown):
1941+
if parent:
19391942
parent.frame().set_local(name, self)
19401943

19411944
for local_name, node in self.implicit_locals():
@@ -2089,7 +2092,7 @@ def _infer_type_call(self, caller, context):
20892092
col_offset=0,
20902093
end_lineno=0,
20912094
end_col_offset=0,
2092-
parent=Unknown(),
2095+
parent=caller.parent,
20932096
)
20942097

20952098
# Get the bases of the class.
@@ -2123,7 +2126,6 @@ def _infer_type_call(self, caller, context):
21232126
if isinstance(attr, node_classes.Const) and isinstance(attr.value, str):
21242127
result.locals[attr.value] = [value]
21252128

2126-
result.parent = caller.parent
21272129
return result
21282130

21292131
def infer_call_result(
@@ -2486,14 +2488,11 @@ def igetattr(
24862488
24872489
:returns: The inferred possible values.
24882490
"""
2489-
from astroid import objects # pylint: disable=import-outside-toplevel
2490-
24912491
# set lookup name since this is necessary to infer on import nodes for
24922492
# instance
24932493
context = copy_context(context)
24942494
context.lookupname = name
24952495

2496-
metaclass = self.metaclass(context=context)
24972496
try:
24982497
attributes = self.getattr(name, context, class_context=class_context)
24992498
# If we have more than one attribute, make sure that those starting from
@@ -2516,44 +2515,7 @@ def igetattr(
25162515
for a in attributes
25172516
if a not in functions or a is last_function or bases._is_property(a)
25182517
]
2519-
2520-
for inferred in bases._infer_stmts(attributes, context, frame=self):
2521-
# yield Uninferable object instead of descriptors when necessary
2522-
if not isinstance(inferred, node_classes.Const) and isinstance(
2523-
inferred, bases.Instance
2524-
):
2525-
try:
2526-
inferred._proxied.getattr("__get__", context)
2527-
except AttributeInferenceError:
2528-
yield inferred
2529-
else:
2530-
yield util.Uninferable
2531-
elif isinstance(inferred, objects.Property):
2532-
function = inferred.function
2533-
if not class_context:
2534-
if not context.callcontext:
2535-
context.callcontext = CallContext(
2536-
args=function.args.arguments, callee=function
2537-
)
2538-
# Through an instance so we can solve the property
2539-
yield from function.infer_call_result(
2540-
caller=self, context=context
2541-
)
2542-
# If we're in a class context, we need to determine if the property
2543-
# was defined in the metaclass (a derived class must be a subclass of
2544-
# the metaclass of all its bases), in which case we can resolve the
2545-
# property. If not, i.e. the property is defined in some base class
2546-
# instead, then we return the property object
2547-
elif metaclass and function.parent.scope() is metaclass:
2548-
# Resolve a property as long as it is not accessed through
2549-
# the class itself.
2550-
yield from function.infer_call_result(
2551-
caller=self, context=context
2552-
)
2553-
else:
2554-
yield inferred
2555-
else:
2556-
yield function_to_method(inferred, self)
2518+
yield from self._infer_attrs(attributes, context, class_context)
25572519
except AttributeInferenceError as error:
25582520
if not name.startswith("__") and self.has_dynamic_getattr(context):
25592521
# class handle some dynamic attributes, return a Uninferable object
@@ -2563,6 +2525,49 @@ def igetattr(
25632525
str(error), target=self, attribute=name, context=context
25642526
) from error
25652527

2528+
def _infer_attrs(
2529+
self,
2530+
attributes: list[InferenceResult],
2531+
context: InferenceContext,
2532+
class_context: bool = True,
2533+
) -> Iterator[InferenceResult]:
2534+
from astroid import objects # pylint: disable=import-outside-toplevel
2535+
2536+
metaclass = self.metaclass(context=context)
2537+
for inferred in bases._infer_stmts(attributes, context, frame=self):
2538+
# yield Uninferable object instead of descriptors when necessary
2539+
if not isinstance(inferred, node_classes.Const) and isinstance(
2540+
inferred, bases.Instance
2541+
):
2542+
try:
2543+
inferred._proxied.getattr("__get__", context)
2544+
except AttributeInferenceError:
2545+
yield inferred
2546+
else:
2547+
yield util.Uninferable
2548+
elif isinstance(inferred, objects.Property):
2549+
function = inferred.function
2550+
if not class_context:
2551+
if not context.callcontext:
2552+
context.callcontext = CallContext(
2553+
args=function.args.arguments, callee=function
2554+
)
2555+
# Through an instance so we can solve the property
2556+
yield from function.infer_call_result(caller=self, context=context)
2557+
# If we're in a class context, we need to determine if the property
2558+
# was defined in the metaclass (a derived class must be a subclass of
2559+
# the metaclass of all its bases), in which case we can resolve the
2560+
# property. If not, i.e. the property is defined in some base class
2561+
# instead, then we return the property object
2562+
elif metaclass and function.parent.scope() is metaclass:
2563+
# Resolve a property as long as it is not accessed through
2564+
# the class itself.
2565+
yield from function.infer_call_result(caller=self, context=context)
2566+
else:
2567+
yield inferred
2568+
else:
2569+
yield function_to_method(inferred, self)
2570+
25662571
def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool:
25672572
"""Check if the class has a custom __getattr__ or __getattribute__.
25682573

astroid/objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def __init__(self, call, name=None, lineno=None, col_offset=None, parent=None):
282282
name,
283283
lineno=lineno,
284284
col_offset=col_offset,
285-
parent=node_classes.Unknown(),
285+
parent=AstroidManager().adhoc_module,
286286
end_col_offset=0,
287287
end_lineno=0,
288288
)

0 commit comments

Comments
 (0)