Commit b891c465 authored by Ivan Levkivskyi's avatar Ivan Levkivskyi Committed by GitHub

bpo-37046: PEP 586: Add Literal to typing module (#13572)

The implementation is straightforward and essentially is just copied from `typing_extensions`.
parent f367242d
...@@ -1103,6 +1103,28 @@ The module defines the following classes, functions and decorators: ...@@ -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 ...@@ -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): ...@@ -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__ = [ ...@@ -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): ...@@ -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= ...@@ -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."""
......
PEP 586: Add ``Literal`` to the ``typing`` module.
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment