python / typing_extensions

Backported and experimental type hints for Python
Other
445 stars 109 forks source link

Nested use of `Unpack` in alias from TypeAliasType causes TypeError on Python 3.11 #474

Closed Daraan closed 1 month ago

Daraan commented 1 month ago

I've written some tests to test nesting of Unpack with TypeAliasTypes, which causes TypeError: Substitution of bare TypeVarTuple is not supported during the recursive substitution.

This is currently only relevant for 3.11 as for lower versions Issue https://github.com/python/typing_extensions/issues/103 needs to be addressed first.

    @skipUnless(TYPING_3_11_0, "Needs Issue #103 first")
    def test_nested_unpack(self):
        T = TypeVar('T')
        Ts = TypeVarTuple("Ts")

        Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,))
        Variadic[int, Tuple[str, int]]  # this is ok

        TupleAliasTs = Variadic[Tuple[Unpack[Ts], int]]

# -------------------------------------------------

        # Tuple[int, Tuple[str, int]]
        # all below tests also fail so abort here early.
        TupleAliasTs[str]  # <--- TypeError: Substitution of bare TypeVarTuple is not supported

# -------------------------------------------------

        TupleAliasTsT = Variadic[Tuple[Unpack[Ts], T]]
        with self.subTest("Single parameter for tuple alias"):
            # Tuple[int, Tuple[str, object]]
            nested_tuple_A = TupleAliasTsT[str, object]
            nested_tuple_A_unpack = TupleAliasTsT[Unpack[Tuple[str]], object]
            self.assertEqual(nested_tuple_A, nested_tuple_A_unpack)

        with self.subTest("Test invalid args", args=([str, int], object)):
            # TypeError on some versions as types should be passed
            invalid_nested_tuple = TupleAliasTsT[[str, int], object]  # invalid form
        with self.subTest("With Callable Ts"):
            # Tuple[int, (str, int) -> object]
            CallableAliasTsT = Variadic[Callable[[Unpack[Ts]], T]]
            CallableAliasTsT[[str, int], object]  # valid nested tuple

        # Equivalent Forms
        with self.subTest("Equivalence of variadic arguments"):
            nested_tuple_bare = TupleAliasTsT[str, int, object]
            nested_tuple_B_1xUnpack = TupleAliasTsT[Unpack[Tuple[str, int]], object]
            nested_tuple_B_2xUnpack = TupleAliasTsT[Unpack[Tuple[str]], Unpack[Tuple[int]], object]
            self.assertEqual(nested_tuple_B_1xUnpack, nested_tuple_bare)
            self.assertEqual(nested_tuple_B_1xUnpack, nested_tuple_B_2xUnpack)
            self.assertNotEqual(invalid_nested_tuple, nested_tuple_B_1xUnpack)

Traceback:

TupleAliasTs[str]
~~~~~~~~~~~~^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 379, in inner
return func(*args, **kwds)
        ^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1431, in __getitem__
new_args = self._determine_new_args(args)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1462, in _determine_new_args
return tuple(self._make_substitution(self.__args__, new_arg_by_param))
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1486, in _make_substitution
new_arg = old_arg[tuple(subargs)]
            ~~~~~~~^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 379, in inner
return func(*args, **kwds)
        ^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1431, in __getitem__
new_args = self._determine_new_args(args)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1462, in _determine_new_args
return tuple(self._make_substitution(self.__args__, new_arg_by_param))
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1474, in _make_substitution
new_arg = substfunc(new_arg_by_param[old_arg])
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/test/envs/python3.11/lib/python3.11/typing.py", line 1103, in __typing_subst__
raise TypeError("Substitution of bare TypeVarTuple is not supported")
TypeError: Substitution of bare TypeVarTuple is not supported
JelleZijlstra commented 1 month ago

Thanks, that does seem like a bug we should address.

Daraan commented 1 month ago

I've traced this back to a minimal variant that happens during the substitution causing the same error:

Unpack[Ts][str]  # should equal str

I've found a fix and wrap it in a PR in the next hours.