bpo-40336: Refactor typing._SpecialForm (GH-19620)
This commit is contained in:
parent
d663d34685
commit
40ded947f8
134
Lib/typing.py
134
Lib/typing.py
|
@ -141,8 +141,9 @@ def _type_check(arg, msg, is_argument=True):
|
||||||
if (isinstance(arg, _GenericAlias) and
|
if (isinstance(arg, _GenericAlias) and
|
||||||
arg.__origin__ in invalid_generic_forms):
|
arg.__origin__ in invalid_generic_forms):
|
||||||
raise TypeError(f"{arg} is not valid as type argument")
|
raise TypeError(f"{arg} is not valid as type argument")
|
||||||
if (isinstance(arg, _SpecialForm) and arg not in (Any, NoReturn) or
|
if arg in (Any, NoReturn):
|
||||||
arg in (Generic, Protocol)):
|
return arg
|
||||||
|
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
|
||||||
raise TypeError(f"Plain {arg} is not valid as type argument")
|
raise TypeError(f"Plain {arg} is not valid as type argument")
|
||||||
if isinstance(arg, (type, TypeVar, ForwardRef)):
|
if isinstance(arg, (type, TypeVar, ForwardRef)):
|
||||||
return arg
|
return arg
|
||||||
|
@ -299,41 +300,18 @@ class _Immutable:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class _SpecialForm(_Final, _Immutable, _root=True):
|
# Internal indicator of special typing constructs.
|
||||||
"""Internal indicator of special typing constructs.
|
# See __doc__ instance attribute for specific docs.
|
||||||
See _doc instance attribute for specific docs.
|
class _SpecialForm(_Final, _root=True):
|
||||||
"""
|
__slots__ = ('_name', '__doc__', '_getitem')
|
||||||
|
|
||||||
__slots__ = ('_name', '_doc')
|
def __init__(self, getitem):
|
||||||
|
self._getitem = getitem
|
||||||
|
self._name = getitem.__name__
|
||||||
|
self.__doc__ = getitem.__doc__
|
||||||
|
|
||||||
def __new__(cls, *args, **kwds):
|
def __mro_entries__(self, bases):
|
||||||
"""Constructor.
|
raise TypeError(f"Cannot subclass {self!r}")
|
||||||
|
|
||||||
This only exists to give a better error message in case
|
|
||||||
someone tries to subclass a special typing object (not a good idea).
|
|
||||||
"""
|
|
||||||
if (len(args) == 3 and
|
|
||||||
isinstance(args[0], str) and
|
|
||||||
isinstance(args[1], tuple)):
|
|
||||||
# Close enough.
|
|
||||||
raise TypeError(f"Cannot subclass {cls!r}")
|
|
||||||
return super().__new__(cls)
|
|
||||||
|
|
||||||
def __init__(self, name, doc):
|
|
||||||
self._name = name
|
|
||||||
self._doc = doc
|
|
||||||
|
|
||||||
@property
|
|
||||||
def __doc__(self):
|
|
||||||
return self._doc
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if not isinstance(other, _SpecialForm):
|
|
||||||
return NotImplemented
|
|
||||||
return self._name == other._name
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash((self._name,))
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'typing.' + self._name
|
return 'typing.' + self._name
|
||||||
|
@ -352,31 +330,10 @@ class _SpecialForm(_Final, _Immutable, _root=True):
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem__(self, parameters):
|
def __getitem__(self, parameters):
|
||||||
if self._name in ('ClassVar', 'Final'):
|
return self._getitem(self, parameters)
|
||||||
item = _type_check(parameters, f'{self._name} accepts only single type.')
|
|
||||||
return _GenericAlias(self, (item,))
|
|
||||||
if self._name == 'Union':
|
|
||||||
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]
|
|
||||||
return _GenericAlias(self, parameters)
|
|
||||||
if self._name == 'Optional':
|
|
||||||
arg = _type_check(parameters, "Optional[t] requires a single type.")
|
|
||||||
return Union[arg, type(None)]
|
|
||||||
if self._name == 'Literal':
|
|
||||||
# There is no '_type_check' call because arguments to Literal[...] are
|
|
||||||
# values, not types.
|
|
||||||
return _GenericAlias(self, parameters)
|
|
||||||
raise TypeError(f"{self} is not subscriptable")
|
|
||||||
|
|
||||||
|
@_SpecialForm
|
||||||
Any = _SpecialForm('Any', doc=
|
def Any(self, parameters):
|
||||||
"""Special type indicating an unconstrained type.
|
"""Special type indicating an unconstrained type.
|
||||||
|
|
||||||
- Any is compatible with every type.
|
- Any is compatible with every type.
|
||||||
|
@ -386,9 +343,11 @@ Any = _SpecialForm('Any', doc=
|
||||||
Note that all the above statements are true from the point of view of
|
Note that all the above statements are true from the point of view of
|
||||||
static type checkers. At runtime, Any should not be used with instance
|
static type checkers. At runtime, Any should not be used with instance
|
||||||
or class checks.
|
or class checks.
|
||||||
""")
|
"""
|
||||||
|
raise TypeError(f"{self} is not subscriptable")
|
||||||
|
|
||||||
NoReturn = _SpecialForm('NoReturn', doc=
|
@_SpecialForm
|
||||||
|
def NoReturn(self, parameters):
|
||||||
"""Special type indicating functions that never return.
|
"""Special type indicating functions that never return.
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
@ -399,9 +358,11 @@ NoReturn = _SpecialForm('NoReturn', doc=
|
||||||
|
|
||||||
This type is invalid in other positions, e.g., ``List[NoReturn]``
|
This type is invalid in other positions, e.g., ``List[NoReturn]``
|
||||||
will fail in static type checkers.
|
will fail in static type checkers.
|
||||||
""")
|
"""
|
||||||
|
raise TypeError(f"{self} is not subscriptable")
|
||||||
|
|
||||||
ClassVar = _SpecialForm('ClassVar', doc=
|
@_SpecialForm
|
||||||
|
def ClassVar(self, parameters):
|
||||||
"""Special type construct to mark class variables.
|
"""Special type construct to mark class variables.
|
||||||
|
|
||||||
An annotation wrapped in ClassVar indicates that a given
|
An annotation wrapped in ClassVar indicates that a given
|
||||||
|
@ -416,9 +377,12 @@ ClassVar = _SpecialForm('ClassVar', doc=
|
||||||
|
|
||||||
Note that ClassVar is not a class itself, and should not
|
Note that ClassVar is not a class itself, and should not
|
||||||
be used with isinstance() or issubclass().
|
be used with isinstance() or issubclass().
|
||||||
""")
|
"""
|
||||||
|
item = _type_check(parameters, f'{self} accepts only single type.')
|
||||||
|
return _GenericAlias(self, (item,))
|
||||||
|
|
||||||
Final = _SpecialForm('Final', doc=
|
@_SpecialForm
|
||||||
|
def Final(self, parameters):
|
||||||
"""Special typing construct to indicate final names to type checkers.
|
"""Special typing construct to indicate final names to type checkers.
|
||||||
|
|
||||||
A final name cannot be re-assigned or overridden in a subclass.
|
A final name cannot be re-assigned or overridden in a subclass.
|
||||||
|
@ -434,9 +398,12 @@ Final = _SpecialForm('Final', doc=
|
||||||
TIMEOUT = 1 # Error reported by type checker
|
TIMEOUT = 1 # Error reported by type checker
|
||||||
|
|
||||||
There is no runtime checking of these properties.
|
There is no runtime checking of these properties.
|
||||||
""")
|
"""
|
||||||
|
item = _type_check(parameters, f'{self} accepts only single type.')
|
||||||
|
return _GenericAlias(self, (item,))
|
||||||
|
|
||||||
Union = _SpecialForm('Union', doc=
|
@_SpecialForm
|
||||||
|
def Union(self, parameters):
|
||||||
"""Union type; Union[X, Y] means either X or Y.
|
"""Union type; Union[X, Y] means either X or Y.
|
||||||
|
|
||||||
To define a union, use e.g. Union[int, str]. Details:
|
To define a union, use e.g. Union[int, str]. Details:
|
||||||
|
@ -461,15 +428,29 @@ Union = _SpecialForm('Union', doc=
|
||||||
|
|
||||||
- You cannot subclass or instantiate a union.
|
- You cannot subclass or instantiate a union.
|
||||||
- You can use Optional[X] as a shorthand for Union[X, None].
|
- 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]
|
||||||
|
return _GenericAlias(self, parameters)
|
||||||
|
|
||||||
Optional = _SpecialForm('Optional', doc=
|
@_SpecialForm
|
||||||
|
def Optional(self, parameters):
|
||||||
"""Optional type.
|
"""Optional type.
|
||||||
|
|
||||||
Optional[X] is equivalent to Union[X, None].
|
Optional[X] is equivalent to Union[X, None].
|
||||||
""")
|
"""
|
||||||
|
arg = _type_check(parameters, f"{self} requires a single type.")
|
||||||
|
return Union[arg, type(None)]
|
||||||
|
|
||||||
Literal = _SpecialForm('Literal', doc=
|
@_SpecialForm
|
||||||
|
def Literal(self, parameters):
|
||||||
"""Special typing form to define literal types (a.k.a. value types).
|
"""Special typing form to define literal types (a.k.a. value types).
|
||||||
|
|
||||||
This form can be used to indicate to type checkers that the corresponding
|
This form can be used to indicate to type checkers that the corresponding
|
||||||
|
@ -486,10 +467,13 @@ Literal = _SpecialForm('Literal', doc=
|
||||||
open_helper('/some/path', 'r') # Passes type check
|
open_helper('/some/path', 'r') # Passes type check
|
||||||
open_helper('/other/path', 'typo') # Error in type checker
|
open_helper('/other/path', 'typo') # Error in type checker
|
||||||
|
|
||||||
Literal[...] cannot be subclassed. At runtime, an arbitrary value
|
Literal[...] cannot be subclassed. At runtime, an arbitrary value
|
||||||
is allowed as type argument to Literal[...], but type checkers may
|
is allowed as type argument to Literal[...], but type checkers may
|
||||||
impose restrictions.
|
impose restrictions.
|
||||||
""")
|
"""
|
||||||
|
# There is no '_type_check' call because arguments to Literal[...] are
|
||||||
|
# values, not types.
|
||||||
|
return _GenericAlias(self, parameters)
|
||||||
|
|
||||||
|
|
||||||
class ForwardRef(_Final, _root=True):
|
class ForwardRef(_Final, _root=True):
|
||||||
|
|
Loading…
Reference in New Issue