diff --git a/astroid/brain/brain_typing.py b/astroid/brain/brain_typing.py index bcaeb0aeaf..a59a333df0 100644 --- a/astroid/brain/brain_typing.py +++ b/astroid/brain/brain_typing.py @@ -10,6 +10,7 @@ # Copyright (c) 2021 hippo91 """Astroid hooks for typing.py support.""" +import itertools import typing from functools import partial @@ -365,8 +366,12 @@ def infer_typing_cast( if func.qname() != "typing.cast" or len(node.args) != 2: raise UseInferenceDefault - type_node = next(node.args[0].infer(context=ctx)) - return iter([Instance(type_node)]) + peek_value_node, value_node = itertools.tee(node.args[1].infer(context=ctx)) + if next(peek_value_node) is Uninferable: + type_node = next(node.args[0].infer(context=ctx)) + return iter([Instance(type_node)]) + else: + return value_node AstroidManager().register_transform( diff --git a/tests/unittest_brain.py b/tests/unittest_brain.py index d2e6102ce9..f0bbefd388 100644 --- a/tests/unittest_brain.py +++ b/tests/unittest_brain.py @@ -1801,7 +1801,8 @@ def test_typing_object_builtin_subscriptable(self): self.assertIsInstance(inferred, nodes.ClassDef) self.assertIsInstance(inferred.getattr("__iter__")[0], nodes.FunctionDef) - def test_typing_cast(self): + def test_typing_cast_uninferable(self): + """cast will yield instance of casted-to type if not otherwise inferable""" node = builder.extract_node( """ from typing import cast @@ -1817,6 +1818,23 @@ class A: assert isinstance(inferred, bases.Instance) assert inferred.name == "A" + def test_typing_cast_inferable(self): + """cast leaves type unchanged if it has been inferred""" + node = builder.extract_node( + """ + from typing import cast + class A: + pass + + b = list() + a = cast(A, b) + a + """ + ) + inferred = next(node.infer()) + assert isinstance(inferred, bases.Instance) + assert inferred.name == "list" + def test_typing_cast_attribute(self): node = builder.extract_node( """