-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
gh-91162: Fix substitution of unpacked tuples in generic aliases #92335
Changes from 3 commits
bdb1c53
0d717f0
0b78566
1397f0a
0067dc8
f0ba8cd
e8c26d9
7e784a9
8e4e43d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -603,22 +603,16 @@ class C(Generic[T]): pass | |
('generic[T]', '[int]', 'generic[int]'), | ||
('generic[T]', '[int, str]', 'TypeError'), | ||
('generic[T]', '[tuple_type[int, ...]]', 'generic[tuple_type[int, ...]]'), | ||
('generic[T]', '[*tuple_type[int]]', 'generic[int]'), | ||
# Should raise TypeError: a) according to the tentative spec, | ||
# unpacked types cannot be used as arguments to aliases that expect | ||
# a fixed number of arguments; b) it's equivalent to generic[()]. | ||
('generic[T]', '[*tuple[()]]', 'generic[*tuple[()]]'), | ||
('generic[T]', '[*Tuple[()]]', 'TypeError'), | ||
('generic[T]', '[*tuple_type[()]]', 'TypeError'), | ||
('generic[T]', '[*tuple_type[int, str]]', 'TypeError'), | ||
# Should raise TypeError according to the tentative spec: unpacked | ||
# types cannot be used as arguments to aliases that expect a fixed | ||
# number of arguments. | ||
('generic[T]', '[*tuple[int]]', 'generic[*tuple[int]]'), | ||
('generic[T]', '[*Tuple[int]]', 'TypeError'), | ||
# Ditto. | ||
('generic[T]', '[*tuple[int, str]]', 'generic[*tuple[int, str]]'), | ||
('generic[T]', '[*Tuple[int, str]]', 'TypeError'), | ||
# Ditto. | ||
('generic[T]', '[*tuple[int, ...]]', 'generic[*tuple[int, ...]]'), | ||
('generic[T]', '[*Tuple[int, ...]]', 'TypeError'), | ||
('generic[T]', '[*tuple_type[int, ...]]', 'TypeError'), | ||
('generic[T]', '[*Ts]', 'TypeError'), | ||
('generic[T]', '[T, *Ts]', 'TypeError'), | ||
('generic[T]', '[*Ts, T]', 'TypeError'), | ||
|
@@ -664,23 +658,29 @@ class C(Generic[T1, T2]): pass | |
('generic[T1, T2]', '[int, str]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[int, str, bool]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, str]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, str]]', 'generic[int, str]'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on the tentative spec we'd decided on at #91162 (comment), even though this is valid and easy to evaluate, we should also disallow it so that the rule stays simple: "If the generic accepts a fixed number of arguments, it can't take any unpacked type arguments." Ditto various cases below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the spec should be changed. There is no problem to unpack a tuple of fixed length into the corresponding number of type variables. I think that it is simpler to allow this than add a special exception. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pradeep90 Do you want to argue the case here? I agreed with you in #32341 (comment) tbh mainly because it didn't seem a huge deal and I didn't want to drag out the discussion there, but I actually agree with Serhiy here that it seems simpler to just allow it. Is there context that Serhiy and I are missing here? As a starting point, I think the rule here could be something like: "You can only use an unpacked arbitrary-length type (such as an unpacked TypeVarTuple or an unpacked arbitrary-length tuple) in the arguments list if the generic accepts an arbitrary number of type parameters". That seems a simple enough rule to me - it disallows There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I disagree that it's a simple enough rule. Given an already-complex PEP, this seems like yet another arcane rule for users to keep in mind. The only benefit it gives us is that we allow the fairly useless example of I don't want to block runtime behavior on this. The runtime can always be more flexible and accept arbitrary cases. Type checkers can forbid this during type checking to keep things simple for users. In short, for the runtime implementation, I'm ok with whatever is simpler for you and Serhiy. I disagree with changing the spec we agreed on for the sake of this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, yeah, to be clear - this conversation is totally just about the spec for what we allow at runtime. If you're happy with the runtime being flexible on this, then so am I. You can go ahead and resolve this, Serhiy. |
||
('generic[T1, T2]', '[*tuple_type[int, str, bool]]', 'TypeError'), | ||
|
||
# Should raise TypeError according to the tentative spec: unpacked | ||
# types cannot be used as arguments to aliases that expect a fixed | ||
# number of arguments. | ||
('generic[T1, T2]', '[*tuple[int, str], *tuple[float, bool]]', 'generic[*tuple[int, str], *tuple[float, bool]]'), | ||
('generic[T1, T2]', '[*Tuple[int, str], *Tuple[float, bool]]', 'TypeError'), | ||
('generic[T1, T2]', '[int, *tuple_type[str]]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[*tuple_type[int], str]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[*tuple_type[int], *tuple_type[str]]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[()]]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[*tuple_type[()], *tuple_type[int, str]]', 'generic[int, str]'), | ||
('generic[T1, T2]', '[*tuple_type[int], *tuple_type[()]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[()], *tuple_type[int]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int], *tuple_type[str, float]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, str], *tuple_type[float, bool]]', 'TypeError'), | ||
|
||
('generic[T1, T2]', '[tuple_type[int, ...]]', 'TypeError'), | ||
('generic[T1, T2]', '[tuple_type[int, ...], tuple_type[str, ...]]', 'generic[tuple_type[int, ...], tuple_type[str, ...]]'), | ||
# Should raise TypeError according to the tentative spec: unpacked | ||
# types cannot be used as arguments to aliases that expect a fixed | ||
# number of arguments. | ||
('generic[T1, T2]', '[*tuple_type[int, ...]]', 'TypeError'), | ||
|
||
# Ditto. | ||
('generic[T1, T2]', '[*tuple[int, ...], *tuple[str, ...]]', 'generic[*tuple[int, ...], *tuple[str, ...]]'), | ||
('generic[T1, T2]', '[*Tuple[int, ...], *Tuple[str, ...]]', 'TypeError'), | ||
|
||
('generic[T1, T2]', '[int, *tuple_type[str, ...]]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, ...], str]', 'TypeError'), | ||
('generic[T1, T2]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'TypeError'), | ||
('generic[T1, T2]', '[*Ts]', 'TypeError'), | ||
('generic[T1, T2]', '[T, *Ts]', 'TypeError'), | ||
('generic[T1, T2]', '[*Ts, T]', 'TypeError'), | ||
|
@@ -720,7 +720,7 @@ class C(Generic[T1, T2, T3]): pass | |
tests = [ | ||
# Alias # Args # Expected result | ||
('generic[T1, bool, T2]', '[int, str]', 'generic[int, bool, str]'), | ||
('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'TypeError'), | ||
('generic[T1, bool, T2]', '[*tuple_type[int, str]]', 'generic[int, bool, str]'), | ||
] | ||
|
||
for alias_template, args_template, expected_template in tests: | ||
|
@@ -765,17 +765,17 @@ class C(Generic[*Ts]): pass | |
('tuple[*Ts]', '[int, str]', 'tuple[int, str]'), | ||
('Tuple[*Ts]', '[int, str]', 'Tuple[int, str]'), | ||
|
||
('C[*Ts]', '[*tuple_type[int]]', 'C[*tuple_type[int]]'), # Should be C[int] | ||
('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[*tuple_type[int]]'), # Should be tuple[int] | ||
('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[*tuple_type[int]]'), # Should be Tuple[int] | ||
('C[*Ts]', '[*tuple_type[int]]', 'C[int]'), | ||
serhiy-storchaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
('tuple[*Ts]', '[*tuple_type[int]]', 'tuple[int]'), | ||
('Tuple[*Ts]', '[*tuple_type[int]]', 'Tuple[int]'), | ||
|
||
('C[*Ts]', '[*tuple_type[*Ts]]', 'C[*tuple_type[*Ts]]'), # Should be C[*Ts] | ||
('tuple[*Ts]', '[*tuple_type[*Ts]]', 'tuple[*tuple_type[*Ts]]'), # Should be tuple[*Ts] | ||
('Tuple[*Ts]', '[*tuple_type[*Ts]]', 'Tuple[*tuple_type[*Ts]]'), # Should be Tuple[*Ts] | ||
('C[*Ts]', '[*tuple_type[*Ts]]', 'C[*Ts]'), | ||
('tuple[*Ts]', '[*tuple_type[*Ts]]', 'tuple[*Ts]'), | ||
('Tuple[*Ts]', '[*tuple_type[*Ts]]', 'Tuple[*Ts]'), | ||
|
||
('C[*Ts]', '[*tuple_type[int, str]]', 'C[*tuple_type[int, str]]'), # Should be C[int, str] | ||
('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[*tuple_type[int, str]]'), # Should be tuple[int, str] | ||
('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[*tuple_type[int, str]]'), # Should be Tuple[int, str] | ||
('C[*Ts]', '[*tuple_type[int, str]]', 'C[int, str]'), | ||
('tuple[*Ts]', '[*tuple_type[int, str]]', 'tuple[int, str]'), | ||
('Tuple[*Ts]', '[*tuple_type[int, str]]', 'Tuple[int, str]'), | ||
|
||
('C[*Ts]', '[tuple_type[int, ...]]', 'C[tuple_type[int, ...]]'), | ||
('tuple[*Ts]', '[tuple_type[int, ...]]', 'tuple[tuple_type[int, ...]]'), | ||
|
@@ -820,11 +820,11 @@ class C(Generic[*Ts]): pass | |
('tuple[T, *Ts]', '[int, str, bool]', 'tuple[int, str, bool]'), | ||
('Tuple[T, *Ts]', '[int, str, bool]', 'Tuple[int, str, bool]'), | ||
|
||
('C[T, *Ts]', '[*tuple[int, ...]]', 'C[*tuple[int, ...]]'), # Should be C[int, *tuple[int, ...]] | ||
('C[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be C[int, *tuple[int, ...]] | ||
('C[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), # Ditto | ||
('tuple[T, *Ts]', '[*tuple[int, ...]]', 'tuple[*tuple[int, ...]]'), # Should be tuple[int, *tuple[int, ...]] | ||
('tuple[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be tuple[int, *tuple[int, ...]] | ||
('tuple[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), # Should be tuple[int, *Tuple[int, ...]] | ||
('Tuple[T, *Ts]', '[*tuple[int, ...]]', 'Tuple[*tuple[int, ...]]'), # Should be Tuple[int, *tuple[int, ...]] | ||
('Tuple[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be Tuple[int, *tuple[int, ...]] | ||
('Tuple[T, *Ts]', '[*Tuple[int, ...]]', 'TypeError'), # Should be Tuple[int, *Tuple[int, ...]] | ||
|
||
('C[*Ts, T]', '[int]', 'C[int]'), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can get rid of the comment here and a few lines below now that these cases do indeed raise a TypeError.