Skip to content

Commit

Permalink
WIP: fix overzealous filtering of FunctionDef nodes in ClassDef.igetattr
Browse files Browse the repository at this point in the history
Ref pylint-dev#1015. When there are multiple statements defining some attribute
ClassDef.igetattr filters out later definitions if they are not in the
same scope as the first (to support cases like "self.a = 1; self.a = 2"
or "self.items = []; self.items += 1"). However, it checks the scope of
the first attribute against the *parent scope* of the later attributes.
For mundane statements this makes no difference, but for
scope-introducing statements such as FunctionDef these are not the same.
  • Loading branch information
nelfin committed Sep 15, 2021
1 parent 9a7878a commit 9640bf2
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 2 deletions.
2 changes: 1 addition & 1 deletion astroid/nodes/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2612,7 +2612,7 @@ def igetattr(self, name, context=None, class_context=True):
# to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists)
if len(attributes) > 1:
first_attr, attributes = attributes[0], attributes[1:]
first_scope = first_attr.scope()
first_scope = first_attr.parent.scope()
attributes = [first_attr] + [
attr
for attr in attributes
Expand Down
34 changes: 33 additions & 1 deletion tests/unittest_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from astroid import decorators as decoratorsmod
from astroid import helpers, nodes, objects, test_utils, util
from astroid.arguments import CallSite
from astroid.bases import BoundMethod, Instance, UnboundMethod
from astroid.bases import BoundMethod, Generator, Instance, UnboundMethod
from astroid.builder import AstroidBuilder, extract_node, parse
from astroid.const import PY38_PLUS, PY39_PLUS
from astroid.context import InferenceContext
Expand Down Expand Up @@ -4005,6 +4005,38 @@ class Test(Outer.Inner):
assert isinstance(inferred, nodes.Const)
assert inferred.value == 123

def test_infer_method_empty_body(self) -> None:
# https://github.com/PyCQA/astroid/issues/1015
node = extract_node(
"""
class A:
def foo(self): ...
A().foo() #@
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
assert inferred.value is None

def test_infer_method_overload(self) -> None:
# https://github.com/PyCQA/astroid/issues/1015
node = extract_node(
"""
class A:
def foo(self): ...
def foo(self):
yield
A().foo() #@
"""
)
inferred = list(node.infer())
assert len(inferred) == 2
assert isinstance(inferred[0], nodes.Const)
assert isinstance(inferred[1], Generator)

def test_delayed_attributes_without_slots(self) -> None:
ast_node = extract_node(
"""
Expand Down

0 comments on commit 9640bf2

Please sign in to comment.