Skip to content

Type substitution in generic aliases does not work if ParamSpec is followed by TypeVarTuple #99379

Closed
@serhiy-storchaka

Description

@serhiy-storchaka

The following code:

from typing import *
P = ParamSpec('P')
Ts = TypeVarTuple('Ts')
C = Callable[P, Tuple[*Ts]]
C[[int], str, bytes]

gives an error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 363, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1393, in __getitem__
    new_args = self._determine_new_args(args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1417, in _determine_new_args
    args = prepare(self, args)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1229, in __typing_prepare_subst__
    return _prepare_paramspec_params(alias, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 297, in _prepare_paramspec_params
    _check_generic(cls, params, len(cls.__parameters__))
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 274, in _check_generic
    raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
TypeError: Too many arguments for typing.Callable[~P, typing.Tuple[*Ts]]; actual 3, expected 2

The same for the C implementation (types.GenericAlias):

import collections.abc
C = collections.abc.Callable[P, Tuple[*Ts]]
C[[int], str, bytes]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.11/Lib/_collections_abc.py", line 468, in __getitem__
    new_args = super().__getitem__(item).__args__
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1229, in __typing_prepare_subst__
    return _prepare_paramspec_params(alias, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 297, in _prepare_paramspec_params
    _check_generic(cls, params, len(cls.__parameters__))
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 274, in _check_generic
    raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
TypeError: Too many arguments for collections.abc.Callable[~P, typing.Tuple[*Ts]]; actual 3, expected 2

And for user generics:

T = TypeVar('T')
class A(Generic[P, T]):
    pass

B = A[P, Tuple[*Ts]]
B[[int], str, bytes]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 363, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1393, in __getitem__
    new_args = self._determine_new_args(args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1417, in _determine_new_args
    args = prepare(self, args)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 1229, in __typing_prepare_subst__
    return _prepare_paramspec_params(alias, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 297, in _prepare_paramspec_params
    _check_generic(cls, params, len(cls.__parameters__))
  File "/home/serhiy/py/cpython3.11/Lib/typing.py", line 274, in _check_generic
    raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
TypeError: Too many arguments for __main__.A[~P, typing.Tuple[*Ts]]; actual 3, expected 2

But it works if ParamSpec and TypeVarTuple are in different order:

class A(Generic[T, P]):
    pass

B = A[Tuple[*Ts], P]
B[str, bytes, [int]]

It is a different bug than #99344 because it happens when you substitute in a generic alias instead of a user generic.

Metadata

Metadata

Labels

3.11only security fixes3.12only security fixespendingThe issue will be closed if no feedback is providedtopic-typingtype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions