Skip to content

Commit

Permalink
Drop a superfluous and wrong callcontext when inferring the result of…
Browse files Browse the repository at this point in the history
… a context manager

The callcontext was containing the instance of the class (self) as the sole argument,
but this was tripping the inference of unknown arguments coming down into an
instance, such as the one from the commit's example.
By dropping the superfluous callcontext, the inference can no longer assume
that the first argument is the instance of the class, leading to wrongly
infer `self.client` as the first argument of the instantiation call.

Close pylint-dev/pylint#2859
  • Loading branch information
PCManticore committed Apr 9, 2019
1 parent 55076ca commit e0a298d
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -6,6 +6,10 @@ What's New in astroid 2.3.0?
============================
Release Date: TBA

* Drop a superfluous and wrong callcontext when inferring the result of a context manager

Close PyCQA/pylint#2859

* ``igetattr`` raises ``InferenceError`` on re-inference of the same object

This prevents ``StopIteration`` from leaking when we encounter the same
Expand Down
2 changes: 0 additions & 2 deletions astroid/protocols.py
Expand Up @@ -494,8 +494,6 @@ def _infer_context_manager(self, mgr, context):
raise exceptions.InferenceError(node=inferred)
if not isinstance(enter, bases.BoundMethod):
raise exceptions.InferenceError(node=enter)
if not context.callcontext:
context.callcontext = contextmod.CallContext(args=[inferred])
yield from enter.infer_call_result(self, context)
else:
raise exceptions.InferenceError(node=mgr)
Expand Down
33 changes: 33 additions & 0 deletions astroid/tests/unittest_inference.py
Expand Up @@ -5213,5 +5213,38 @@ def __call__(self):
assert next(node.infer()) is util.Uninferable


def test_infer_context_manager_with_unknown_args():
code = """
class client_log(object):
def __init__(self, client):
self.client = client
def __enter__(self):
return self.client
def __exit__(self, exc_type, exc_value, traceback):
pass
with client_log(None) as c:
c #@
"""
node = extract_node(code)
assert next(node.infer()) is util.Uninferable

# But if we know the argument, then it is easy
code = """
class client_log(object):
def __init__(self, client=24):
self.client = client
def __enter__(self):
return self.client
def __exit__(self, exc_type, exc_value, traceback):
pass
with client_log(None) as c:
c #@
"""
node = extract_node(code)
assert isinstance(next(node.infer()), nodes.Const)


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

0 comments on commit e0a298d

Please sign in to comment.