8000 gh-91162: Support splitting of unpacked arbitrary-length tuple over TypeVar and TypeVarTuple parameters (alt) by serhiy-storchaka · Pull Request #93412 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-91162: Support splitting of unpacked arbitrary-length tuple over TypeVar and TypeVarTuple parameters (alt) #93412 < 8000 /h1>
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-91162: Support substitution of TypeVar with an unpacked variable-s…
…ize tuple

For example:

  A[T, *Ts][*tuple[int, ...]] -> A[int, *tuple[int, ...]]
  A[*Ts, T][*tuple[int, ...]] -> A[*tuple[int, ...], int]
  • Loading branch information
serhiy-storchaka committed May 29, 2022
commit cfb43b27befa05d17e465cd2bb7419cfe7ab20f5
11 changes: 8 additions & 3 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,11 +756,12 @@ class C(Generic[*Ts]): pass
('generic[*Ts]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'),
('generic[*Ts]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'),
('generic[*Ts]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'),
('generic[*Ts]', '[str, *tuple_type[int, ...], bool]', 'generic[str, *tuple_type[int, ...], bool]'),

# Technically, multiple unpackings are forbidden by PEP 646, but we
# choose to be less restrictive at runtime, to allow folks room
# to experiment. So all three of these should be valid.
('generic[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...] 8000 ]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'),
#('generic[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'),

('generic[*Ts]', '[*Ts]', 'generic[*Ts]'),
('generic[*Ts]', '[T, *Ts]', 'generic[T, *Ts]'),
Expand All @@ -769,11 +770,15 @@ class C(Generic[*Ts]): pass
('generic[T, *Ts]', '[int, str]', 'generic[int, str]'),
('generic[T, *Ts]', '[int, str, bool]', 'generic[int, str, bool]'),

('generic[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be generic[int, *tuple[int, ...]]
#('generic[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be generic[int, *tuple[int, ...]]
('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[int, *tuple_type[int, ...]]'),
('C[*Ts, T]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...], int]'),
('C[T1, *Ts, T2]', '[*tuple_type[int, ...]]', 'C[int, *tuple_type[int, ...], int]'),


('generic[*Ts, T]', '[int]', 'generic[int]'),
('generic[*Ts, T]', '[int, str]', 'generic[int, str]'),
('generic[*Ts, T]', '[int, str, bool]', 'generic[int, str, bool]'),
('generic[*Ts, T]', '[int, str, bool]', 'generic[int, str, bool]'),

('generic[T, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'),
('generic[T1, T2, *tuple_type[int, ...]]', '[str, bool]', 'generic[str, bool, *tuple_type[int, ...]]'),
Expand Down
41 changes: 37 additions & 4 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,14 @@ def _is_unpacked_typevartuple(x: Any) -> bool:
return ((not isinstance(x, type)) and
getattr(x, '__typing_is_unpacked_typevartuple__', False))

def _is_unpacked_var_tuple(x: Any) -> bool:
if isinstance(x, type) and not isinstance(x, GenericAlias):
return False
args = getattr(x, '__typing_unpacked_tuple_args__', None)
if args and args[-1] is ...:
return True
return False


def _is_typevar_like(x: Any) -> bool:
return isinstance(x, (TypeVar, ParamSpec)) or _is_unpacked_typevartuple(x)
Expand Down Expand Up @@ -1422,13 +1430,28 @@ def _determine_new_args(self, args):
plen = len(params)
if typevartuple_index is not None:
i = typevartuple_index
j = alen - (plen - i - 1)
if j < i:
j = plen - typevartuple_index - 1
var_tuple_index = None
for k, arg in enumerate(args):
if _is_unpacked_var_tuple(arg):
if var_tuple_index is not None:
raise TypeError("More than one unpacked variable-size tuple argument")
var_tuple_index = k
fillarg = args[var_tuple_index].__typing_unpacked_tuple_args__[0]
if var_tuple_index is not None:
i = min(i, var_tuple_index)
j = min(j, alen - var_tuple_index - 1)
elif i + j > alen:
raise TypeError(f"Too few arguments for {self};"
f" actual {alen}, expected at least {plen-1}")

new_arg_by_param.update(zip(params[:i], args[:i]))
new_arg_by_param[params[i]] = tuple(args[i: j])
new_arg_by_param.update(zip(params[i + 1:], args[j:]))
for k in range(i, typevartuple_index):
new_arg_by_param[params[k]] = fillarg
new_arg_by_param[params[typevartuple_index]] = tuple(args[i: alen - j])
for k in range(typevartuple_index + 1, plen - j):
new_arg_by_param[params[k]] = fillarg
new_arg_by_param.update(zip(params[plen - j:], args[alen - j:]))
else:
if alen != plen:
raise TypeError(f"Too {'many' if alen > plen else 'few'} arguments for {self};"
Expand Down Expand Up @@ -1760,6 +1783,16 @@ def __typing_is_unpacked_typevartuple__(self):
assert len(self.__args__) == 1
return isinstance(self.__args__[0], TypeVarTuple)

@property
def __typing_is_unpacked_var_tuple__(self):
assert self.__origin__ is Unpack
assert len(self.__args__) == 1
arg, = self.__args__
if isinstance(arg, _GenericAlias):
assert arg.__origin__ is tuple
return len(arg.__args__) >= 2 and arg.__args__[-1] is ...
return False


class Generic:
"""Abstract base class for generic types.
Expand Down
0