From 01a0a7ac2e070ee165e960bb9f2456dbbc7411ab Mon Sep 17 00:00:00 2001 From: Stanislav Levin Date: Sun, 22 Dec 2019 23:08:47 +0300 Subject: [PATCH] Make use of cache while transform builtin containers As of now, the transformation of builtin containers which members, in turn, are containers relies on `safe_infer` helper. `safe_infer` tries to infer the node to get its values. Doing this without a cache containing a previously inferred nodes lead to infinite recursion, for example, on resolving a class attribute. Note: this doesn't help to infer a class attribute by itself is such a case, but prevents crash. Closes: https://github.com/PyCQA/pylint/issues/3245 Signed-off-by: Stanislav Levin --- astroid/brain/brain_builtin_inference.py | 9 ++++++--- tests/unittest_inference.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py index 7cc6095fd1..3cbe48f715 100644 --- a/astroid/brain/brain_builtin_inference.py +++ b/astroid/brain/brain_builtin_inference.py @@ -154,7 +154,7 @@ def _container_generic_inference(node, context, node_type, transform): if len(node.args) > 1: raise UseInferenceDefault() - arg, = args + (arg,) = args transformed = transform(arg) if not transformed: try: @@ -169,7 +169,7 @@ def _container_generic_inference(node, context, node_type, transform): return transformed -def _container_generic_transform(arg, klass, iterables, build_elts): +def _container_generic_transform(arg, context, klass, iterables, build_elts): if isinstance(arg, klass): return arg elif isinstance(arg, iterables): @@ -177,7 +177,9 @@ def _container_generic_transform(arg, klass, iterables, build_elts): elts = [elt.value for elt in arg.elts] else: # TODO: Does not handle deduplication for sets. - elts = filter(None, map(helpers.safe_infer, arg.elts)) + elts = filter( + None, map(partial(helpers.safe_infer, context=context), arg.elts) + ) elif isinstance(arg, nodes.Dict): # Dicts need to have consts as strings already. if not all(isinstance(elt[0], nodes.Const) for elt in arg.items): @@ -197,6 +199,7 @@ def _infer_builtin_container( ): transform_func = partial( _container_generic_transform, + context=context, klass=klass, iterables=iterables, build_elts=build_elts, diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py index a54c4ee159..a5ed96b1da 100644 --- a/tests/unittest_inference.py +++ b/tests/unittest_inference.py @@ -5502,5 +5502,19 @@ class A: assert inferred.value == 42 +def test_recursion_error_inferring_builtin_containers(): + node = extract_node( + """ + class Foo: + a = "foo" + inst = Foo() + + b = tuple([inst.a]) #@ + inst.a = b + """ + ) + helpers.safe_infer(node.targets[0]) + + if __name__ == "__main__": unittest.main()