Skip to content

Commit 8b80e9b

Browse files
committed
Prevent a recursion error for self reference variables and type() calls.
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
1 parent 77e205d commit 8b80e9b

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Release Date: TBA
1818

1919
Close PyCQA/pylint#2991
2020

21+
* Prevent a recursion error for self reference variables and `type()` calls.
22+
23+
Close #199
24+
2125
* Do not infer the first argument of a staticmethod in a metaclass as the class itself
2226

2327
Close PyCQA/pylint#3032

astroid/scoped_nodes.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2126,7 +2126,14 @@ def _infer_type_call(self, caller, context):
21262126
# Get the bases of the class.
21272127
class_bases = next(caller.args[1].infer(context))
21282128
if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
2129-
result.bases = class_bases.itered()
2129+
bases = []
2130+
for base in class_bases.itered():
2131+
inferred = next(base.infer(context=context))
2132+
if inferred:
2133+
bases.append(
2134+
node_classes.EvaluatedObject(original=base, value=inferred)
2135+
)
2136+
result.bases = bases
21302137
else:
21312138
# There is currently no AST node that can represent an 'unknown'
21322139
# node (Uninferable is not an AST node), therefore we simply return Uninferable here

tests/unittest_inference.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5779,5 +5779,25 @@ def test_recursion_error_metaclass_monkeypatching():
57795779
assert cls.declared_metaclass() is None
57805780

57815781

5782+
@pytest.mark.xfail(reason="Cannot fully infer all the base classes properly.")
5783+
def test_recursion_error_self_reference_type_call():
5784+
# Fix for https://github.com/PyCQA/astroid/issues/199
5785+
code = """
5786+
class A(object):
5787+
pass
5788+
class SomeClass(object):
5789+
route_class = A
5790+
def __init__(self):
5791+
self.route_class = type('B', (self.route_class, ), {})
5792+
self.route_class() #@
5793+
"""
5794+
node = extract_node(code)
5795+
inferred = next(node.infer())
5796+
assert isinstance(inferred, Instance)
5797+
assert inferred.name == "B"
5798+
# TODO: Cannot infer [B, A, object] but at least the recursion error is gone.
5799+
assert [cls.name for cls in inferred.mro()] == ["B", "A", "object"]
5800+
5801+
57825802
if __name__ == "__main__":
57835803
unittest.main()

0 commit comments

Comments
 (0)