Skip to content

Commit

Permalink
Scope the inference to the current bound node when inferring instance…
Browse files Browse the repository at this point in the history
…s of classes

When inferring instances of classes from arguments, such as ``self``
in a bound method, we could use as a hint the context's ``boundnode``,
which indicates the instance from which the inference originated.
As an example, a subclass that uses a parent's method which returns
``self``, will override the ``self`` to point to it instead of pointing
to the parent class.

Close pylint-dev/pylint#3157
  • Loading branch information
PCManticore committed Nov 25, 2019
1 parent 05a9106 commit 1304064
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 0 deletions.
11 changes: 11 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ Release Date: TBA

* Allow inferring positional only arguments.

* Scope the inference to the current bound node when inferring instances of classes

When inferring instances of classes from arguments, such as ``self``
in a bound method, we could use as a hint the context's ``boundnode``,
which indicates the instance from which the inference originated.
As an example, a subclass that uses a parent's method which returns
``self``, will override the ``self`` to point to it instead of pointing
to the parent class.

Close PyCQA/pylint#3157

* Add support for inferring exception instances in all contexts

We were able to infer exception instances as ``ExceptionInstance``
Expand Down
2 changes: 2 additions & 0 deletions astroid/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ def _arguments_infer_argname(self, name, context):
is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass"
# If this is a metaclass, then the first argument will always
# be the class, not an instance.
if context.boundnode and isinstance(context.boundnode, bases.Instance):
cls = context.boundnode._proxied
if is_metaclass or functype == "classmethod":
yield cls
return
Expand Down
52 changes: 52 additions & 0 deletions tests/unittest_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -5370,5 +5370,57 @@ def __init__(self, index):
assert isinstance(index[0], nodes.AssignAttr)


@pytest.mark.parametrize(
"code,instance_name",
[
(
"""
class A:
def __enter__(self):
return self
def __exit__(self, err_type, err, traceback):
return
class B(A):
pass
with B() as b:
b #@
""",
"B",
),
(
"""
class A:
def __enter__(self):
return A()
def __exit__(self, err_type, err, traceback):
return
class B(A):
pass
with B() as b:
b #@
""",
"A",
),
(
"""
class A:
def test(self):
return A()
class B(A):
def test(self):
return A.test(self)
B().test()
""",
"A",
),
],
)
def test_inference_is_limited_to_the_boundnode(code, instance_name):
node = extract_node(code)
inferred = next(node.infer())
assert isinstance(inferred, Instance)
assert inferred.name == instance_name


if __name__ == "__main__":
unittest.main()

0 comments on commit 1304064

Please sign in to comment.