Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b891c46

Browse files
authored
bpo-37046: PEP 586: Add Literal to typing module (#13572)
The implementation is straightforward and essentially is just copied from `typing_extensions`.
1 parent f367242 commit b891c46

4 files changed

Lines changed: 113 additions & 1 deletion

File tree

Doc/library/typing.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,28 @@ The module defines the following classes, functions and decorators:
11031103
``Callable[..., Any]``, and in turn to
11041104
:class:`collections.abc.Callable`.
11051105

1106+
.. data:: Literal
1107+
1108+
A type that can be used to indicate to type checkers that the
1109+
corresponding variable or function parameter has a value equivalent to
1110+
the provided literal (or one of several literals). For example::
1111+
1112+
def validate_simple(data: Any) -> Literal[True]: # always returns True
1113+
...
1114+
1115+
MODE = Literal['r', 'rb', 'w', 'wb']
1116+
def open_helper(file: str, mode: MODE) -> str:
1117+
...
1118+
1119+
open_helper('/some/path', 'r') # Passes type check
1120+
open_helper('/other/path', 'typo') # Error in type checker
1121+
1122+
``Literal[...]`` cannot be subclassed. At runtime, an arbitrary value
1123+
is allowed as type argument to ``Literal[...]``, but type checkers may
1124+
impose restrictions. See :pep:`586` for more details about literal types.
1125+
1126+
.. versionadded:: 3.8
1127+
11061128
.. data:: ClassVar
11071129

11081130
Special type construct to mark class variables.

Lib/test/test_typing.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import Any, NoReturn
1010
from typing import TypeVar, AnyStr
1111
from typing import T, KT, VT # Not in __all__.
12-
from typing import Union, Optional
12+
from typing import Union, Optional, Literal
1313
from typing import Tuple, List, MutableMapping
1414
from typing import Callable
1515
from typing import Generic, ClassVar, Final, final
@@ -489,6 +489,68 @@ def test_ellipsis_in_generic(self):
489489
typing.List[Callable[..., str]]
490490

491491

492+
class LiteralTests(BaseTestCase):
493+
def test_basics(self):
494+
# All of these are allowed.
495+
Literal[1]
496+
Literal[1, 2, 3]
497+
Literal["x", "y", "z"]
498+
Literal[None]
499+
Literal[True]
500+
Literal[1, "2", False]
501+
Literal[Literal[1, 2], Literal[4, 5]]
502+
Literal[b"foo", u"bar"]
503+
504+
def test_illegal_parameters_do_not_raise_runtime_errors(self):
505+
# Type checkers should reject these types, but we do not
506+
# raise errors at runtime to maintain maximium flexibility.
507+
Literal[int]
508+
Literal[3j + 2, ..., ()]
509+
Literal[{"foo": 3, "bar": 4}]
510+
Literal[T]
511+
512+
def test_literals_inside_other_types(self):
513+
List[Literal[1, 2, 3]]
514+
List[Literal[("foo", "bar", "baz")]]
515+
516+
def test_repr(self):
517+
self.assertEqual(repr(Literal[1]), "typing.Literal[1]")
518+
self.assertEqual(repr(Literal[1, True, "foo"]), "typing.Literal[1, True, 'foo']")
519+
self.assertEqual(repr(Literal[int]), "typing.Literal[int]")
520+
self.assertEqual(repr(Literal), "typing.Literal")
521+
self.assertEqual(repr(Literal[None]), "typing.Literal[None]")
522+
523+
def test_cannot_init(self):
524+
with self.assertRaises(TypeError):
525+
Literal()
526+
with self.assertRaises(TypeError):
527+
Literal[1]()
528+
with self.assertRaises(TypeError):
529+
type(Literal)()
530+
with self.assertRaises(TypeError):
531+
type(Literal[1])()
532+
533+
def test_no_isinstance_or_issubclass(self):
534+
with self.assertRaises(TypeError):
535+
isinstance(1, Literal[1])
536+
with self.assertRaises(TypeError):
537+
isinstance(int, Literal[1])
538+
with self.assertRaises(TypeError):
539+
issubclass(1, Literal[1])
540+
with self.assertRaises(TypeError):
541+
issubclass(int, Literal[1])
542+
543+
def test_no_subclassing(self):
544+
with self.assertRaises(TypeError):
545+
class Foo(Literal[1]): pass
546+
with self.assertRaises(TypeError):
547+
class Bar(Literal): pass
548+
549+
def test_no_multiple_subscripts(self):
550+
with self.assertRaises(TypeError):
551+
Literal[1][1]
552+
553+
492554
XK = TypeVar('XK', str, bytes)
493555
XV = TypeVar('XV')
494556

Lib/typing.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
'ClassVar',
3838
'Final',
3939
'Generic',
40+
'Literal',
4041
'Optional',
4142
'Tuple',
4243
'Type',
@@ -355,6 +356,10 @@ def __getitem__(self, parameters):
355356
if self._name == 'Optional':
356357
arg = _type_check(parameters, "Optional[t] requires a single type.")
357358
return Union[arg, type(None)]
359+
if self._name == 'Literal':
360+
# There is no '_type_check' call because arguments to Literal[...] are
361+
# values, not types.
362+
return _GenericAlias(self, parameters)
358363
raise TypeError(f"{self} is not subscriptable")
359364

360365

@@ -451,6 +456,28 @@ class FastConnector(Connection):
451456
Optional[X] is equivalent to Union[X, None].
452457
""")
453458

459+
Literal = _SpecialForm('Literal', doc=
460+
"""Special typing form to define literal types (a.k.a. value types).
461+
462+
This form can be used to indicate to type checkers that the corresponding
463+
variable or function parameter has a value equivalent to the provided
464+
literal (or one of several literals):
465+
466+
def validate_simple(data: Any) -> Literal[True]: # always returns True
467+
...
468+
469+
MODE = Literal['r', 'rb', 'w', 'wb']
470+
def open_helper(file: str, mode: MODE) -> str:
471+
...
472+
473+
open_helper('/some/path', 'r') # Passes type check
474+
open_helper('/other/path', 'typo') # Error in type checker
475+
476+
Literal[...] cannot be subclassed. At runtime, an arbitrary value
477+
is allowed as type argument to Literal[...], but type checkers may
478+
impose restrictions.
479+
""")
480+
454481

455482
class ForwardRef(_Final, _root=True):
456483
"""Internal wrapper to hold a forward reference."""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PEP 586: Add ``Literal`` to the ``typing`` module.

0 commit comments

Comments
 (0)