mirror of
https://github.com/python/cpython.git
synced 2025-11-01 14:11:41 +00:00
gh-105499: Merge typing.Union and types.UnionType (#105511)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Ken Jin <kenjin@python.org> Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
parent
e091520fdb
commit
dc6d66f44c
20 changed files with 562 additions and 327 deletions
144
Lib/typing.py
144
Lib/typing.py
|
|
@ -29,7 +29,13 @@
|
|||
import operator
|
||||
import sys
|
||||
import types
|
||||
from types import GenericAlias
|
||||
from types import (
|
||||
WrapperDescriptorType,
|
||||
MethodWrapperType,
|
||||
MethodDescriptorType,
|
||||
GenericAlias,
|
||||
)
|
||||
import warnings
|
||||
|
||||
from _typing import (
|
||||
_idfunc,
|
||||
|
|
@ -40,6 +46,7 @@
|
|||
ParamSpecKwargs,
|
||||
TypeAliasType,
|
||||
Generic,
|
||||
Union,
|
||||
NoDefault,
|
||||
)
|
||||
|
||||
|
|
@ -367,21 +374,6 @@ def _compare_args_orderless(first_args, second_args):
|
|||
return False
|
||||
return not t
|
||||
|
||||
def _remove_dups_flatten(parameters):
|
||||
"""Internal helper for Union creation and substitution.
|
||||
|
||||
Flatten Unions among parameters, then remove duplicates.
|
||||
"""
|
||||
# Flatten out Union[Union[...], ...].
|
||||
params = []
|
||||
for p in parameters:
|
||||
if isinstance(p, (_UnionGenericAlias, types.UnionType)):
|
||||
params.extend(p.__args__)
|
||||
else:
|
||||
params.append(p)
|
||||
|
||||
return tuple(_deduplicate(params, unhashable_fallback=True))
|
||||
|
||||
|
||||
def _flatten_literal_params(parameters):
|
||||
"""Internal helper for Literal creation: flatten Literals among parameters."""
|
||||
|
|
@ -470,7 +462,7 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
|
|||
return evaluate_forward_ref(t, globals=globalns, locals=localns,
|
||||
type_params=type_params, owner=owner,
|
||||
_recursive_guard=recursive_guard, format=format)
|
||||
if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)):
|
||||
if isinstance(t, (_GenericAlias, GenericAlias, Union)):
|
||||
if isinstance(t, GenericAlias):
|
||||
args = tuple(
|
||||
_make_forward_ref(arg) if isinstance(arg, str) else arg
|
||||
|
|
@ -495,7 +487,7 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
|
|||
return t
|
||||
if isinstance(t, GenericAlias):
|
||||
return GenericAlias(t.__origin__, ev_args)
|
||||
if isinstance(t, types.UnionType):
|
||||
if isinstance(t, Union):
|
||||
return functools.reduce(operator.or_, ev_args)
|
||||
else:
|
||||
return t.copy_with(ev_args)
|
||||
|
|
@ -749,59 +741,6 @@ class FastConnector(Connection):
|
|||
item = _type_check(parameters, f'{self} accepts only single type.', allow_special_forms=True)
|
||||
return _GenericAlias(self, (item,))
|
||||
|
||||
@_SpecialForm
|
||||
def Union(self, parameters):
|
||||
"""Union type; Union[X, Y] means either X or Y.
|
||||
|
||||
On Python 3.10 and higher, the | operator
|
||||
can also be used to denote unions;
|
||||
X | Y means the same thing to the type checker as Union[X, Y].
|
||||
|
||||
To define a union, use e.g. Union[int, str]. Details:
|
||||
- The arguments must be types and there must be at least one.
|
||||
- None as an argument is a special case and is replaced by
|
||||
type(None).
|
||||
- Unions of unions are flattened, e.g.::
|
||||
|
||||
assert Union[Union[int, str], float] == Union[int, str, float]
|
||||
|
||||
- Unions of a single argument vanish, e.g.::
|
||||
|
||||
assert Union[int] == int # The constructor actually returns int
|
||||
|
||||
- Redundant arguments are skipped, e.g.::
|
||||
|
||||
assert Union[int, str, int] == Union[int, str]
|
||||
|
||||
- When comparing unions, the argument order is ignored, e.g.::
|
||||
|
||||
assert Union[int, str] == Union[str, int]
|
||||
|
||||
- You cannot subclass or instantiate a union.
|
||||
- You can use Optional[X] as a shorthand for Union[X, None].
|
||||
"""
|
||||
if parameters == ():
|
||||
raise TypeError("Cannot take a Union of no types.")
|
||||
if not isinstance(parameters, tuple):
|
||||
parameters = (parameters,)
|
||||
msg = "Union[arg, ...]: each arg must be a type."
|
||||
parameters = tuple(_type_check(p, msg) for p in parameters)
|
||||
parameters = _remove_dups_flatten(parameters)
|
||||
if len(parameters) == 1:
|
||||
return parameters[0]
|
||||
if len(parameters) == 2 and type(None) in parameters:
|
||||
return _UnionGenericAlias(self, parameters, name="Optional")
|
||||
return _UnionGenericAlias(self, parameters)
|
||||
|
||||
def _make_union(left, right):
|
||||
"""Used from the C implementation of TypeVar.
|
||||
|
||||
TypeVar.__or__ calls this instead of returning types.UnionType
|
||||
because we want to allow unions between TypeVars and strings
|
||||
(forward references).
|
||||
"""
|
||||
return Union[left, right]
|
||||
|
||||
@_SpecialForm
|
||||
def Optional(self, parameters):
|
||||
"""Optional[X] is equivalent to Union[X, None]."""
|
||||
|
|
@ -1708,45 +1647,34 @@ def __getitem__(self, params):
|
|||
return self.copy_with(params)
|
||||
|
||||
|
||||
class _UnionGenericAlias(_NotIterable, _GenericAlias, _root=True):
|
||||
def copy_with(self, params):
|
||||
return Union[params]
|
||||
class _UnionGenericAliasMeta(type):
|
||||
def __instancecheck__(self, inst: object) -> bool:
|
||||
warnings._deprecated("_UnionGenericAlias", remove=(3, 17))
|
||||
return isinstance(inst, Union)
|
||||
|
||||
def __subclasscheck__(self, inst: type) -> bool:
|
||||
warnings._deprecated("_UnionGenericAlias", remove=(3, 17))
|
||||
return issubclass(inst, Union)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, (_UnionGenericAlias, types.UnionType)):
|
||||
return NotImplemented
|
||||
try: # fast path
|
||||
return set(self.__args__) == set(other.__args__)
|
||||
except TypeError: # not hashable, slow path
|
||||
return _compare_args_orderless(self.__args__, other.__args__)
|
||||
warnings._deprecated("_UnionGenericAlias", remove=(3, 17))
|
||||
if other is _UnionGenericAlias or other is Union:
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
return hash(frozenset(self.__args__))
|
||||
|
||||
def __repr__(self):
|
||||
args = self.__args__
|
||||
if len(args) == 2:
|
||||
if args[0] is type(None):
|
||||
return f'typing.Optional[{_type_repr(args[1])}]'
|
||||
elif args[1] is type(None):
|
||||
return f'typing.Optional[{_type_repr(args[0])}]'
|
||||
return super().__repr__()
|
||||
class _UnionGenericAlias(metaclass=_UnionGenericAliasMeta):
|
||||
"""Compatibility hack.
|
||||
|
||||
def __instancecheck__(self, obj):
|
||||
for arg in self.__args__:
|
||||
if isinstance(obj, arg):
|
||||
return True
|
||||
return False
|
||||
A class named _UnionGenericAlias used to be used to implement
|
||||
typing.Union. This class exists to serve as a shim to preserve
|
||||
the meaning of some code that used to use _UnionGenericAlias
|
||||
directly.
|
||||
|
||||
def __subclasscheck__(self, cls):
|
||||
for arg in self.__args__:
|
||||
if issubclass(cls, arg):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __reduce__(self):
|
||||
func, (origin, args) = super().__reduce__()
|
||||
return func, (Union, args)
|
||||
"""
|
||||
def __new__(cls, self_cls, parameters, /, *, name=None):
|
||||
warnings._deprecated("_UnionGenericAlias", remove=(3, 17))
|
||||
return Union[parameters]
|
||||
|
||||
|
||||
def _value_and_type_iter(parameters):
|
||||
|
|
@ -2472,7 +2400,7 @@ def _strip_annotations(t):
|
|||
if stripped_args == t.__args__:
|
||||
return t
|
||||
return GenericAlias(t.__origin__, stripped_args)
|
||||
if isinstance(t, types.UnionType):
|
||||
if isinstance(t, Union):
|
||||
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
||||
if stripped_args == t.__args__:
|
||||
return t
|
||||
|
|
@ -2506,8 +2434,8 @@ def get_origin(tp):
|
|||
return tp.__origin__
|
||||
if tp is Generic:
|
||||
return Generic
|
||||
if isinstance(tp, types.UnionType):
|
||||
return types.UnionType
|
||||
if isinstance(tp, Union):
|
||||
return Union
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -2532,7 +2460,7 @@ def get_args(tp):
|
|||
if _should_unflatten_callable_args(tp, res):
|
||||
res = (list(res[:-1]), res[-1])
|
||||
return res
|
||||
if isinstance(tp, types.UnionType):
|
||||
if isinstance(tp, Union):
|
||||
return tp.__args__
|
||||
return ()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue