diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b1414dc82bae0f..f153bc3c1b426e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3298,6 +3298,24 @@ def test_top_level_class_var(self): ): get_type_hints(ann_module6) + def test_hints_with_literal_cache(self): + # https://bugs.python.org/issue45679 + Literal[True] # to trigger cache + def func(arg: Literal[1]): + pass + + hints = get_type_hints(func) + # We use such complex way of comparing things, because `1 == True` + self.assertEqual(str(hints['arg']), "typing.Literal[1]") + + # Two args case, which was causing an issue: + Literal[1, 'a'] + def func(arg: Literal[True, 'a']): + pass + + hints = get_type_hints(func) + self.assertEqual(str(hints['arg']), "typing.Literal[True, 'a']") + class GetUtilitiesTestCase(TestCase): def test_get_origin(self): diff --git a/Lib/typing.py b/Lib/typing.py index 78d973d2bba056..6db6f41551f99c 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -312,10 +312,14 @@ def decorator(func): @functools.wraps(func) def inner(*args, **kwds): - try: - return cached(*args, **kwds) - except TypeError: - pass # All real errors (not unhashable args) are raised below. + # We call `cached`, only if we don't care about types, + # or when we have just a single argument. Otherwise we can confuse + # `Literal[1, 'a']` with `Literal[True, 'a']`, see: bpo-45679 + if not typed or len(args) <= 1: + try: + return cached(*args, **kwds) + except TypeError: + pass # All real errors (not unhashable args) are raised below. return func(*args, **kwds) return inner diff --git a/Misc/NEWS.d/next/Library/2021-10-30-20-27-59.bpo-45679.gwXa77.rst b/Misc/NEWS.d/next/Library/2021-10-30-20-27-59.bpo-45679.gwXa77.rst new file mode 100644 index 00000000000000..abe48e029365de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-10-30-20-27-59.bpo-45679.gwXa77.rst @@ -0,0 +1,2 @@ +Do not cache :class:`typing.Literal` when it has more than one type +argument.