mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-37046: PEP 586: Add Literal to typing module (#13572)
The implementation is straightforward and essentially is just copied from `typing_extensions`.
This commit is contained in:
parent
f367242d10
commit
b891c465bb
4 changed files with 113 additions and 1 deletions
|
@ -1103,6 +1103,28 @@ The module defines the following classes, functions and decorators:
|
||||||
``Callable[..., Any]``, and in turn to
|
``Callable[..., Any]``, and in turn to
|
||||||
:class:`collections.abc.Callable`.
|
:class:`collections.abc.Callable`.
|
||||||
|
|
||||||
|
.. data:: Literal
|
||||||
|
|
||||||
|
A type that can be used to indicate to type checkers that the
|
||||||
|
corresponding variable or function parameter has a value equivalent to
|
||||||
|
the provided literal (or one of several literals). For example::
|
||||||
|
|
||||||
|
def validate_simple(data: Any) -> Literal[True]: # always returns True
|
||||||
|
...
|
||||||
|
|
||||||
|
MODE = Literal['r', 'rb', 'w', 'wb']
|
||||||
|
def open_helper(file: str, mode: MODE) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
open_helper('/some/path', 'r') # Passes type check
|
||||||
|
open_helper('/other/path', 'typo') # Error in type checker
|
||||||
|
|
||||||
|
``Literal[...]`` cannot be subclassed. At runtime, an arbitrary value
|
||||||
|
is allowed as type argument to ``Literal[...]``, but type checkers may
|
||||||
|
impose restrictions. See :pep:`586` for more details about literal types.
|
||||||
|
|
||||||
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
.. data:: ClassVar
|
.. data:: ClassVar
|
||||||
|
|
||||||
Special type construct to mark class variables.
|
Special type construct to mark class variables.
|
||||||
|
|
|
@ -9,7 +9,7 @@ from copy import copy, deepcopy
|
||||||
from typing import Any, NoReturn
|
from typing import Any, NoReturn
|
||||||
from typing import TypeVar, AnyStr
|
from typing import TypeVar, AnyStr
|
||||||
from typing import T, KT, VT # Not in __all__.
|
from typing import T, KT, VT # Not in __all__.
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional, Literal
|
||||||
from typing import Tuple, List, MutableMapping
|
from typing import Tuple, List, MutableMapping
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Generic, ClassVar, Final, final
|
from typing import Generic, ClassVar, Final, final
|
||||||
|
@ -489,6 +489,68 @@ class CallableTests(BaseTestCase):
|
||||||
typing.List[Callable[..., str]]
|
typing.List[Callable[..., str]]
|
||||||
|
|
||||||
|
|
||||||
|
class LiteralTests(BaseTestCase):
|
||||||
|
def test_basics(self):
|
||||||
|
# All of these are allowed.
|
||||||
|
Literal[1]
|
||||||
|
Literal[1, 2, 3]
|
||||||
|
Literal["x", "y", "z"]
|
||||||
|
Literal[None]
|
||||||
|
Literal[True]
|
||||||
|
Literal[1, "2", False]
|
||||||
|
Literal[Literal[1, 2], Literal[4, 5]]
|
||||||
|
Literal[b"foo", u"bar"]
|
||||||
|
|
||||||
|
def test_illegal_parameters_do_not_raise_runtime_errors(self):
|
||||||
|
# Type checkers should reject these types, but we do not
|
||||||
|
# raise errors at runtime to maintain maximium flexibility.
|
||||||
|
Literal[int]
|
||||||
|
Literal[3j + 2, ..., ()]
|
||||||
|
Literal[{"foo": 3, "bar": 4}]
|
||||||
|
Literal[T]
|
||||||
|
|
||||||
|
def test_literals_inside_other_types(self):
|
||||||
|
List[Literal[1, 2, 3]]
|
||||||
|
List[Literal[("foo", "bar", "baz")]]
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
self.assertEqual(repr(Literal[1]), "typing.Literal[1]")
|
||||||
|
self.assertEqual(repr(Literal[1, True, "foo"]), "typing.Literal[1, True, 'foo']")
|
||||||
|
self.assertEqual(repr(Literal[int]), "typing.Literal[int]")
|
||||||
|
self.assertEqual(repr(Literal), "typing.Literal")
|
||||||
|
self.assertEqual(repr(Literal[None]), "typing.Literal[None]")
|
||||||
|
|
||||||
|
def test_cannot_init(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Literal()
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Literal[1]()
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
type(Literal)()
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
type(Literal[1])()
|
||||||
|
|
||||||
|
def test_no_isinstance_or_issubclass(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
isinstance(1, Literal[1])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
isinstance(int, Literal[1])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
issubclass(1, Literal[1])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
issubclass(int, Literal[1])
|
||||||
|
|
||||||
|
def test_no_subclassing(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class Foo(Literal[1]): pass
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class Bar(Literal): pass
|
||||||
|
|
||||||
|
def test_no_multiple_subscripts(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Literal[1][1]
|
||||||
|
|
||||||
|
|
||||||
XK = TypeVar('XK', str, bytes)
|
XK = TypeVar('XK', str, bytes)
|
||||||
XV = TypeVar('XV')
|
XV = TypeVar('XV')
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ __all__ = [
|
||||||
'ClassVar',
|
'ClassVar',
|
||||||
'Final',
|
'Final',
|
||||||
'Generic',
|
'Generic',
|
||||||
|
'Literal',
|
||||||
'Optional',
|
'Optional',
|
||||||
'Tuple',
|
'Tuple',
|
||||||
'Type',
|
'Type',
|
||||||
|
@ -355,6 +356,10 @@ class _SpecialForm(_Final, _Immutable, _root=True):
|
||||||
if self._name == 'Optional':
|
if self._name == 'Optional':
|
||||||
arg = _type_check(parameters, "Optional[t] requires a single type.")
|
arg = _type_check(parameters, "Optional[t] requires a single type.")
|
||||||
return Union[arg, type(None)]
|
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")
|
raise TypeError(f"{self} is not subscriptable")
|
||||||
|
|
||||||
|
|
||||||
|
@ -451,6 +456,28 @@ Optional = _SpecialForm('Optional', doc=
|
||||||
Optional[X] is equivalent to Union[X, None].
|
Optional[X] is equivalent to Union[X, None].
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
Literal = _SpecialForm('Literal', doc=
|
||||||
|
"""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
|
||||||
|
variable or function parameter has a value equivalent to the provided
|
||||||
|
literal (or one of several literals):
|
||||||
|
|
||||||
|
def validate_simple(data: Any) -> Literal[True]: # always returns True
|
||||||
|
...
|
||||||
|
|
||||||
|
MODE = Literal['r', 'rb', 'w', 'wb']
|
||||||
|
def open_helper(file: str, mode: MODE) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
open_helper('/some/path', 'r') # Passes type check
|
||||||
|
open_helper('/other/path', 'typo') # Error in type checker
|
||||||
|
|
||||||
|
Literal[...] cannot be subclassed. At runtime, an arbitrary value
|
||||||
|
is allowed as type argument to Literal[...], but type checkers may
|
||||||
|
impose restrictions.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
class ForwardRef(_Final, _root=True):
|
class ForwardRef(_Final, _root=True):
|
||||||
"""Internal wrapper to hold a forward reference."""
|
"""Internal wrapper to hold a forward reference."""
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
PEP 586: Add ``Literal`` to the ``typing`` module.
|
Loading…
Add table
Add a link
Reference in a new issue