Skip to content

Commit cb8342f

Browse files
committed
simple fix
1 parent 6968641 commit cb8342f

File tree

2 files changed

+24
-171
lines changed

2 files changed

+24
-171
lines changed

astroid/brain/brain_pathlib.py

Lines changed: 3 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from collections.abc import Iterator
88

9-
from astroid import bases, context, nodes, util
9+
from astroid import bases, context, nodes
1010
from astroid.builder import _extract_single_node
1111
from astroid.const import PY313
1212
from astroid.exceptions import InferenceError, UseInferenceDefault
@@ -21,7 +21,8 @@
2121

2222
def _looks_like_parents_subscript(node: nodes.Subscript) -> bool:
2323
if not (
24-
isinstance(node.value, nodes.Attribute) and node.value.attrname == "parents"
24+
(isinstance(node.value, nodes.Attribute) and node.value.attrname == "parents")
25+
or isinstance(node.value, nodes.Name)
2526
):
2627
return False
2728

@@ -43,142 +44,13 @@ def infer_parents_subscript(
4344
if isinstance(subscript_node.slice, nodes.Const):
4445
path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
4546
return iter([path_cls.instantiate_class()])
46-
elif isinstance(subscript_node.slice, nodes.Slice):
47-
# For slices, return a tuple
48-
parents_tuple = nodes.Tuple()
49-
path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
50-
parents_tuple.elts = [path_cls.instantiate_class() for _ in range(2)] # Mock some parents
51-
return iter([parents_tuple])
5247

5348
raise UseInferenceDefault
5449

5550

56-
def _looks_like_parents_name(node: nodes.Name) -> bool:
57-
"""Check if a Name node was assigned from a Path.parents attribute."""
58-
# Only apply to direct Name nodes, not to Name nodes in subscripts
59-
if isinstance(node.parent, nodes.Subscript):
60-
return False
61-
62-
# Only apply to Name nodes that are direct expressions, not in subscripts
63-
if not isinstance(node.parent, nodes.Expr):
64-
return False
65-
66-
# Look for the assignment in the current scope
67-
try:
68-
frame, stmts = node.lookup(node.name)
69-
if not stmts:
70-
return False
71-
72-
# Check each assignment statement
73-
for stmt in stmts:
74-
if isinstance(stmt, nodes.AssignName):
75-
# Get the parent Assign node
76-
assign_node = stmt.parent
77-
if isinstance(assign_node, nodes.Assign):
78-
# Check if the value is an Attribute access to .parents
79-
if (isinstance(assign_node.value, nodes.Attribute)
80-
and assign_node.value.attrname == "parents"):
81-
try:
82-
# Check if the attribute is from a Path object
83-
value = next(assign_node.value.expr.infer())
84-
if (isinstance(value, bases.Instance)
85-
and isinstance(value._proxied, nodes.ClassDef)
86-
and value.qname() in ("pathlib.Path", "pathlib._local.Path")):
87-
return True
88-
except (InferenceError, StopIteration):
89-
pass
90-
except (InferenceError, StopIteration):
91-
pass
92-
return False
93-
94-
95-
def infer_parents_name(
96-
name_node: nodes.Name, ctx: context.InferenceContext | None = None
97-
) -> Iterator[bases.Instance]:
98-
"""Infer a Name node that was assigned from Path.parents."""
99-
if PY313:
100-
# For Python 3.13+, parents is a tuple
101-
from astroid import nodes
102-
# Create a tuple that behaves like Path.parents
103-
parents_tuple = nodes.Tuple()
104-
# Add some mock Path elements to make indexing work
105-
path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
106-
parents_tuple.elts = [path_cls.instantiate_class() for _ in range(3)] # Mock some parents
107-
return iter([parents_tuple])
108-
else:
109-
# For older versions, it's a _PathParents object
110-
# We need to create a mock _PathParents instance that behaves correctly
111-
parents_cls = _extract_single_node("""
112-
class _PathParents:
113-
def __getitem__(self, key):
114-
from pathlib import Path
115-
if isinstance(key, slice):
116-
# Return a tuple for slices
117-
return (Path(), Path())
118-
# For indexing, return a Path object
119-
return Path()
120-
""")
121-
return iter([parents_cls.instantiate_class()])
122-
123-
124-
def _looks_like_parents_attribute(node: nodes.Attribute) -> bool:
125-
"""Check if an Attribute node is accessing Path.parents."""
126-
if node.attrname != "parents":
127-
return False
128-
129-
# Check if the expression is a Path object
130-
try:
131-
expr_inferred = list(node.expr.infer())
132-
if expr_inferred and not isinstance(expr_inferred[0], util.UninferableBase):
133-
expr_value = expr_inferred[0]
134-
if (isinstance(expr_value, bases.Instance)
135-
and isinstance(expr_value._proxied, nodes.ClassDef)
136-
and expr_value.qname() in ("pathlib.Path", "pathlib._local.Path")):
137-
return True
138-
except (InferenceError, StopIteration):
139-
pass
140-
141-
return False
142-
143-
144-
def infer_parents_attribute(
145-
attr_node: nodes.Attribute, ctx: context.InferenceContext | None = None
146-
) -> Iterator[bases.Instance]:
147-
"""Infer Path.parents attribute access."""
148-
if PY313:
149-
# For Python 3.13+, parents is a tuple
150-
parents_tuple = nodes.Tuple()
151-
path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
152-
parents_tuple.elts = [path_cls.instantiate_class() for _ in range(3)] # Mock some parents
153-
return iter([parents_tuple])
154-
else:
155-
# For older versions, it's a _PathParents object
156-
parents_cls = _extract_single_node("""
157-
class _PathParents:
158-
def __getitem__(self, key):
159-
from pathlib import Path
160-
if isinstance(key, slice):
161-
# Return a tuple for slices
162-
return (Path(), Path())
163-
# For indexing, return a Path object
164-
return Path()
165-
""")
166-
return iter([parents_cls.instantiate_class()])
167-
168-
16951
def register(manager: AstroidManager) -> None:
17052
manager.register_transform(
17153
nodes.Subscript,
17254
inference_tip(infer_parents_subscript),
17355
_looks_like_parents_subscript,
17456
)
175-
manager.register_transform(
176-
nodes.Name,
177-
inference_tip(infer_parents_name),
178-
_looks_like_parents_name,
179-
)
180-
manager.register_transform(
181-
nodes.Attribute,
182-
inference_tip(infer_parents_attribute),
183-
_looks_like_parents_attribute,
184-
)

tests/brain/test_pathlib.py

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ def test_inference_parents_subscript_index() -> None:
4949
assert inferred[0].qname() == "pathlib.Path"
5050

5151

52+
def test_inference_parents_assigned_to_variable() -> None:
53+
"""Test inference of ``pathlib.Path.parents`` when assigned to a variable."""
54+
name_node = astroid.extract_node(
55+
"""
56+
from pathlib import Path
57+
58+
cwd = Path.cwd()
59+
parents = cwd.parents
60+
parents[0] #@
61+
"""
62+
)
63+
64+
inferred = name_node.inferred()
65+
assert len(inferred) == 1
66+
assert isinstance(inferred[0], bases.Instance)
67+
if PY313:
68+
assert inferred[0].qname() == "pathlib._local.Path"
69+
else:
70+
assert inferred[0].qname() == "pathlib.Path"
71+
72+
5273
def test_inference_parents_subscript_slice() -> None:
5374
"""Test inference of ``pathlib.Path.parents``, accessed by slice."""
5475
name_node = astroid.extract_node(
@@ -82,43 +103,3 @@ class A:
82103
assert len(inferred) == 1
83104
assert inferred[0] is Uninferable
84105

85-
86-
def test_inference_parents_assigned_to_variable() -> None:
87-
"""Test inference of ``pathlib.Path.parents`` when assigned to a variable."""
88-
name_node = astroid.extract_node(
89-
"""
90-
from pathlib import Path
91-
92-
cwd = Path.cwd()
93-
parents = cwd.parents
94-
parents[0] #@
95-
"""
96-
)
97-
98-
inferred = name_node.inferred()
99-
assert len(inferred) == 1
100-
assert isinstance(inferred[0], bases.Instance)
101-
if PY313:
102-
# For Python 3.13+, variable assignment returns a Path object
103-
assert inferred[0].qname() == "pathlib._local.Path"
104-
else:
105-
# For Python < 3.13, variable assignment returns a tuple
106-
assert inferred[0].qname() == "builtins.tuple"
107-
108-
109-
def test_inference_parents_assigned_to_variable_slice() -> None:
110-
"""Test inference of ``pathlib.Path.parents`` when assigned to a variable and sliced."""
111-
name_node = astroid.extract_node(
112-
"""
113-
from pathlib import Path
114-
115-
cwd = Path.cwd()
116-
parents = cwd.parents
117-
parents[:2] #@
118-
"""
119-
)
120-
121-
inferred = name_node.inferred()
122-
assert len(inferred) == 1
123-
assert isinstance(inferred[0], bases.Instance)
124-
assert inferred[0].qname() == "builtins.tuple"

0 commit comments

Comments
 (0)