-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
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-105499: Merge typing.Union and types.UnionType #105511
base: main
Are you sure you want to change the base?
Conversation
Would we be able to get rid of |
Yes |
Instead of deleting from operator import or_
from functools import reduce
@_SpecialForm
def Union(self, parameters):
return reduce(or_, parameters) That would avoid the issue of "Should we rename But I know your plan is to deal with handling forward refs in So perhaps you could instead expose a @_SpecialForm
def Union(self, parameters):
return types.UnionType._secret_undocumented_constructor(parameters) Or we could just expose the constructor of |
Also you can now get rid of this function as part of this PR; prior callers of the function can now just use Lines 842 to 844 in 6a8b862
|
I'd rather not keep them as distinct objects, though; that means we still have two objects that to a user look like the same thing. Plus, we'd have |
I guess one of my reservations here is that >>> from types import UnionType
>>> UnionType(int, str)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot create 'types.UnionType' instances ...Except wait, you can, we now have a "secret >>> UnionType[int, str, bytes]
int | str | bytes It's very unusual for Maybe that means we should just expose the constructor as well as adding
Good point, that would indeed be confusing. |
I can make the constructor work. Should it take *args and union them all together? |
That makes sense to me! |
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.
This looks great, in my opinion. From a design perspective, the only weirdnesses I can see are that both of these become valid at runtime:
from types import UnionType
from typing import Union
UnionType[int, str]
Union(int, str)
I can live with that, though, and hopefully linters can flag those uses. (Type checkers almost certainly will, anyway.) On our side, we can just not document that you can do either of those things.
Coming back to this PR now, I still think it's best to combine the two objects completely. This creates a less confusing situation in the long term as we no longer have two nearly-equivalent objects. @AlexWaygood do you still have concerns about this? |
Removed the
|
@AlexWaygood any further thoughts? I just looked over the PR again and continue to think it's a good simplification. |
This is now failing tests due to changes from #112283 that allow unhashable entries in unions. I'll need some new C code to make that work. |
Finally got around to fixing hashability, and I'm pretty happy with the result (less code than before!). At the sprint I finagled @AlexWaygood into maybe taking another look. I still think this is a good idea and we should move forward with it. |
@@ -709,10 +709,6 @@ def test_or_types_operator(self): | |||
y = int | bool | |||
with self.assertRaises(TypeError): | |||
x < y | |||
# Check that we don't crash if typing.Union does not have a tuple in __args__ | |||
y = typing.Union[str, int] | |||
y.__args__ = [str, 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.
.__args__
is no longer writable.
@@ -1015,9 +1005,14 @@ def __eq__(self, other): | |||
return 1 / 0 | |||
|
|||
bt = BadType('bt', (), {}) | |||
bt2 = BadType('bt2', (), {}) | |||
# Comparison should fail and errors should propagate out for bad types. |
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.
With the new code there are fewer code paths that trigger the equality comparison.
typing.Union
andtypes.UnionType
#105499📚 Documentation preview 📚: https://cpython-previews--105511.org.readthedocs.build/