Skip to content

Commit

Permalink
Prevent a recursion error for self reference variables and type() c…
Browse files Browse the repository at this point in the history
…alls.

The recursion error was caused by inferring the bases of the generated
`B` class multiple times. The solution uses the new `EvaluatedObject`
class to stop the inference by inferring the base classes in the
`type` inference function.

Close #199
  • Loading branch information
PCManticore committed Mar 10, 2020
1 parent 77e205d commit 8b80e9b
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 1 deletion.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ Release Date: TBA

Close PyCQA/pylint#2991

* Prevent a recursion error for self reference variables and `type()` calls.

Close #199

* Do not infer the first argument of a staticmethod in a metaclass as the class itself

Close PyCQA/pylint#3032
Expand Down
9 changes: 8 additions & 1 deletion astroid/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2126,7 +2126,14 @@ def _infer_type_call(self, caller, context):
# Get the bases of the class.
class_bases = next(caller.args[1].infer(context))
if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
result.bases = class_bases.itered()
bases = []
for base in class_bases.itered():
inferred = next(base.infer(context=context))
if inferred:
bases.append(
node_classes.EvaluatedObject(original=base, value=inferred)
)
result.bases = bases
else:
# There is currently no AST node that can represent an 'unknown'
# node (Uninferable is not an AST node), therefore we simply return Uninferable here
Expand Down
20 changes: 20 additions & 0 deletions tests/unittest_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -5779,5 +5779,25 @@ def test_recursion_error_metaclass_monkeypatching():
assert cls.declared_metaclass() is None


@pytest.mark.xfail(reason="Cannot fully infer all the base classes properly.")
def test_recursion_error_self_reference_type_call():
# Fix for https://github.com/PyCQA/astroid/issues/199
code = """
class A(object):
pass
class SomeClass(object):
route_class = A
def __init__(self):
self.route_class = type('B', (self.route_class, ), {})
self.route_class() #@
"""
node = extract_node(code)
inferred = next(node.infer())
assert isinstance(inferred, Instance)
assert inferred.name == "B"
# TODO: Cannot infer [B, A, object] but at least the recursion error is gone.
assert [cls.name for cls in inferred.mro()] == ["B", "A", "object"]


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

0 comments on commit 8b80e9b

Please sign in to comment.