From 9a379807cd966f69400939653633236e05530a7c Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Sat, 5 Jan 2019 20:38:14 -0800 Subject: [PATCH 1/5] Add docs for Literal types This pull request adds some documentation on using Literal types. It focuses mostly on how they can be parameterized, and different ways of declaring variables to be Literal. These docs deliberately do not mention the "intelligent indexing" feature: we don't yet support intelligently indexing using variables that are declared to be Final, so I think we should leave this feature undocumented for now. --- docs/source/final_attrs.rst | 2 + docs/source/more_types.rst | 170 +++++++++++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 2 deletions(-) diff --git a/docs/source/final_attrs.rst b/docs/source/final_attrs.rst index 3f8e900d555a..3e15bbb2b8a5 100644 --- a/docs/source/final_attrs.rst +++ b/docs/source/final_attrs.rst @@ -1,3 +1,5 @@ +.. _final_attrs: + Final names, methods and classes ================================ diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 8c4a4a362ecd..9651e4e6cd98 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -2,8 +2,8 @@ More types ========== This section introduces a few additional kinds of types, including ``NoReturn``, -``NewType``, ``TypedDict``, and types for async code. It also discusses how to -give functions more precise types using overloads. All of these are only +``NewType``, ``TypedDict``, ``Literal``, and types for async code. It also discusses +how to give functions more precise types using overloads. All of these are only situationally useful, so feel free to skip this section and come back when you have a need for some of them. @@ -23,6 +23,10 @@ Here's a quick summary of what's covered here: * ``TypedDict`` lets you give precise types for dictionaries that represent objects with a fixed schema, such as ``{'id': 1, 'items': ['x']}``. +* ``Literal`` lets you indicate that a given expression has a specific value. + For example, if you do ``x: Literal["foo"]``, mypy will understand that + ``x`` is not only a string, but is equal to exactly the string ``"foo"``. + * Async types let you type check programs using ``async`` and ``await``. .. _noreturn: @@ -985,3 +989,165 @@ and non-required keys, such as ``Movie`` above, will only be compatible with another TypedDict if all required keys in the other TypedDict are required keys in the first TypedDict, and all non-required keys of the other TypedDict are also non-required keys in the first TypedDict. + +.. _literal: + +Literal +******* + +.. note:: + + Literal is an officially supported feature, but is highly experimental + and should be considered to be in alpha stage. It is very likely that future + releases of mypy will modify the behavior of Literal types, either by adding + new features or by tuning or removing problematic ones. + +Literal types let you indicate that an expression is equal to some specific +primitive value. For example, if we annotate a variable with type ``Literal["foo"]``, +mypy will understand that variable is not only of type ``str``, but is also +equal to specifically the string ``"foo"``. + +This feature is primarily useful when annotating functions that behave +differently based on the exact value the caller provides. For example, +suppose we have a function ``fetch_data(...)`` that returns bytes if the +first argument is True, and Text if it's False. We can construct a precise +type signature for this function using Literal and overloads: + +.. code-block:: python + + from typing import overload, Text, Union + from typing_extensions import Literal + + # The first two overloads use Literal so we can + # have precise return types: + + @overload + def fetch_data(raw: Literal[True]) -> bytes: ... + @overload + def fetch_data(raw: Literal[False]) -> Text: ... + + # The last overload is a fallback in case the caller + # provides a regular bool: + + @overload + def fetch_data(raw: bool) -> Union[bytes, Text]: ... + + def fetch_data(raw: bool) -> Union[bytes, Text]: + # (Implementation is omitted) + pass + + variable_1: Literal[True] = True + + reveal_type(fetch_data(True)) # Revealed type is 'bytes' + reveal_type(fetch_data(False)) # Revealed type is 'str' + reveal_type(fetch_data(variable_1)) # Revealed type is 'bytes' + + # Variables declared without annotations will continue to have an + # inferred type of 'bool'. + + variable_2 = True + reveal_type(fetch_data(variable_2)) # Revealed type is 'Union[bytes, str]' + +Parameterizing Literals +----------------------- + +Literal types may contain one or more literal bools, ints, strings, and byte +strings. However, Literal types **cannot** contain arbitrary expressions: +types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]`` +are all illegal. + +Literals containing two or more values are equivalent to the union of those values. +So, ``Literal[-3, b"foo", True]`` is equivalent to +``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This can help make writing +more complex types involving Literals a little more convenient. + +Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being +exactly equivalent to just ``None``. This means that ``Literal[4, None]``, +``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all exactly equivalent. + +Literals may also contain aliases of Literal types. For example, the following program +is legal: + +.. code-block:: python + + PrimaryColors = Literal["red", "blue", "yellow"] + SecondaryColors = Literal["purple", "green", "orange"] + AllowedColors = Literal[PrimaryColors, SecondaryColors] + + def paint(color: AllowedColors) -> None: pass + + paint("red") # Type checks! + paint("turquoise") # Does not type check + +Literals may not contain any other kind of type or expression. This means doing +``Literal[my_instance]``, ``Literal[Any]``, ``Literal[3.14]``, or +``Literal[{"foo": 2, "bar": 5}]`` are all illegal. + +Future versions of mypy may relax some of these restrictions. For example, we +plan on adding support for using enum values inside Literals in an upcoming release. + +Declaring Literal variables +--------------------------- + +You must explicitly add an annotation to a variable to declare that it is +a Literal type: + +.. code-block:: python + + a: Literal[19] = 19 + reveal_type(a) # Revealed type is 'Literal[19]' + +In order to preserve backwards-compatibility, variables without this annotation +are **not** assumed to be Literals: + +.. code-block:: python + + b = 19 + reveal_type(b) # Revealed type is 'int' + +If you find repeating the value of the variable in the type hint to be tedious, +you can instead declare the variable to be :ref:`Final `: + +.. code-block:: python + + from typing_extensions import Final, Literal + + def expects_literal(x: Literal[19]) -> None: pass + + c: Final = 19 + + reveal_type(c) # Revealed type is 'int' + expects_literal(c) # ...but type checks! + +If we do not provide an explicit type in the Final, the type of ``c`` becomes +context-sensitive: mypy will basically try "substituting" the original assigned +value whenever it's used before performing type checking. So, mypy will type-check +the above program as if it were written like so: + +.. code-block:: python + + from typing_extensions import Final, Literal + + def expects_literal(x: Literal[19]) -> None: pass + + reveal_type(19) + expects_literal(19) + +This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)`` +reports ``int``. + +So while declaring a variable to be final is not quite the same thing as adding +an explicit Literal annotation, it often leads to the same effect in practice. + +Limitations +----------- + +Mypy will not understand expressions that use Literal variables on a deep level. +For example, if you have a variable ``a`` of type ``Literal[3]`` +and another variable ``b`` of type ``Literal[5]``, mypy will infer that +``a + b`` has type ``int``, **not** type ``Literal[8]``. + +The basic rule is that Literal types are treated as just regular subtypes of +whatever type the parameter has. For example, ``Literal[3]`` is as a subtype of +``int`` and ``Literal["foo"]`` is treated as a subtype of ``str``. + From 0adfa647f8cd463472c471682d5641a263ca385c Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Sun, 6 Jan 2019 12:06:46 -0800 Subject: [PATCH 2/5] Respond to code review --- docs/source/more_types.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 9651e4e6cd98..dec680e5d850 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -1051,14 +1051,14 @@ type signature for this function using Literal and overloads: Parameterizing Literals ----------------------- -Literal types may contain one or more literal bools, ints, strings, and byte +Literal types may contain one or more literal bools, ints, strs, and byte strings. However, Literal types **cannot** contain arbitrary expressions: types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]`` are all illegal. Literals containing two or more values are equivalent to the union of those values. So, ``Literal[-3, b"foo", True]`` is equivalent to -``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This can help make writing +``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This makes writing more complex types involving Literals a little more convenient. Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being @@ -1089,7 +1089,7 @@ plan on adding support for using enum values inside Literals in an upcoming rele Declaring Literal variables --------------------------- -You must explicitly add an annotation to a variable to declare that it is +You must explicitly add an annotation to a variable to declare that it has a Literal type: .. code-block:: python @@ -1137,7 +1137,7 @@ This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_t reports ``int``. So while declaring a variable to be final is not quite the same thing as adding -an explicit Literal annotation, it often leads to the same effect in practice. +an explicit Literal annotation, it often leads to the same effect in practice. Limitations ----------- From dc978fc0ba060b81ec9b48e0787bfc40f6827b88 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Sun, 6 Jan 2019 12:22:21 -0800 Subject: [PATCH 3/5] Refine examples and 'limitation' section; more tweaks --- docs/source/more_types.rst | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index dec680e5d850..ff8574e207e9 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -1010,12 +1010,12 @@ equal to specifically the string ``"foo"``. This feature is primarily useful when annotating functions that behave differently based on the exact value the caller provides. For example, suppose we have a function ``fetch_data(...)`` that returns bytes if the -first argument is True, and Text if it's False. We can construct a precise +first argument is True, and str if it's False. We can construct a precise type signature for this function using Literal and overloads: .. code-block:: python - from typing import overload, Text, Union + from typing import overload, Union from typing_extensions import Literal # The first two overloads use Literal so we can @@ -1024,15 +1024,15 @@ type signature for this function using Literal and overloads: @overload def fetch_data(raw: Literal[True]) -> bytes: ... @overload - def fetch_data(raw: Literal[False]) -> Text: ... + def fetch_data(raw: Literal[False]) -> str: ... # The last overload is a fallback in case the caller # provides a regular bool: @overload - def fetch_data(raw: bool) -> Union[bytes, Text]: ... + def fetch_data(raw: bool) -> Union[bytes, str]: ... - def fetch_data(raw: bool) -> Union[bytes, Text]: + def fetch_data(raw: bool) -> Union[bytes, str]: # (Implementation is omitted) pass @@ -1062,8 +1062,8 @@ So, ``Literal[-3, b"foo", True]`` is equivalent to more complex types involving Literals a little more convenient. Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being -exactly equivalent to just ``None``. This means that ``Literal[4, None]``, -``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all exactly equivalent. +equivalent to just ``None``. This means that ``Literal[4, None]``, +``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent. Literals may also contain aliases of Literal types. For example, the following program is legal: @@ -1117,12 +1117,12 @@ you can instead declare the variable to be :ref:`Final `: c: Final = 19 reveal_type(c) # Revealed type is 'int' - expects_literal(c) # ...but type checks! + expects_literal(c) # ...but this type checks! If we do not provide an explicit type in the Final, the type of ``c`` becomes context-sensitive: mypy will basically try "substituting" the original assigned value whenever it's used before performing type checking. So, mypy will type-check -the above program as if it were written like so: +the above program almost as if it were written like so: .. code-block:: python @@ -1136,7 +1136,7 @@ the above program as if it were written like so: This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)`` reports ``int``. -So while declaring a variable to be final is not quite the same thing as adding +So while declaring a variable to be Final is not quite the same thing as adding an explicit Literal annotation, it often leads to the same effect in practice. Limitations @@ -1148,6 +1148,7 @@ and another variable ``b`` of type ``Literal[5]``, mypy will infer that ``a + b`` has type ``int``, **not** type ``Literal[8]``. The basic rule is that Literal types are treated as just regular subtypes of -whatever type the parameter has. For example, ``Literal[3]`` is as a subtype of -``int`` and ``Literal["foo"]`` is treated as a subtype of ``str``. - +whatever type the parameter has. For example, ``Literal[3]`` is treated as a +subtype of ``int`` and so will inherit all of ``int``'s methods directly. This +means that ``Literal[3].__add__`` accepts the same arguments and has the same +return type as ``int.__add__``. From 6e95ca148818c89be3cfa0106cc412d977305424 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Mon, 7 Jan 2019 08:56:49 -0800 Subject: [PATCH 4/5] Respond to Ivan's code review --- docs/source/more_types.rst | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index ff8574e207e9..a3c2f125eefe 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -1009,9 +1009,9 @@ equal to specifically the string ``"foo"``. This feature is primarily useful when annotating functions that behave differently based on the exact value the caller provides. For example, -suppose we have a function ``fetch_data(...)`` that returns bytes if the -first argument is True, and str if it's False. We can construct a precise -type signature for this function using Literal and overloads: +suppose we have a function ``fetch_data(...)`` that returns ``bytes`` if the +first argument is ``True``, and ``str`` if it's ``False``. We can construct a +precise type signature for this function using Literal and overloads: .. code-block:: python @@ -1033,26 +1033,23 @@ type signature for this function using Literal and overloads: def fetch_data(raw: bool) -> Union[bytes, str]: ... def fetch_data(raw: bool) -> Union[bytes, str]: - # (Implementation is omitted) - pass - - variable_1: Literal[True] = True + # Implementation is omitted + ... reveal_type(fetch_data(True)) # Revealed type is 'bytes' reveal_type(fetch_data(False)) # Revealed type is 'str' - reveal_type(fetch_data(variable_1)) # Revealed type is 'bytes' # Variables declared without annotations will continue to have an # inferred type of 'bool'. - variable_2 = True - reveal_type(fetch_data(variable_2)) # Revealed type is 'Union[bytes, str]' + variable = True + reveal_type(fetch_data(variable)) # Revealed type is 'Union[bytes, str]' Parameterizing Literals ----------------------- -Literal types may contain one or more literal bools, ints, strs, and byte -strings. However, Literal types **cannot** contain arbitrary expressions: +Literal types may contain one or more literal bools, ints, strs, and bytes. +However, Literal types **cannot** contain arbitrary expressions: types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]`` are all illegal. @@ -1074,7 +1071,7 @@ is legal: SecondaryColors = Literal["purple", "green", "orange"] AllowedColors = Literal[PrimaryColors, SecondaryColors] - def paint(color: AllowedColors) -> None: pass + def paint(color: AllowedColors) -> None: ... paint("red") # Type checks! paint("turquoise") # Does not type check @@ -1106,7 +1103,7 @@ are **not** assumed to be Literals: reveal_type(b) # Revealed type is 'int' If you find repeating the value of the variable in the type hint to be tedious, -you can instead declare the variable to be :ref:`Final `: +you can instead change the variable to be :ref:`Final `: .. code-block:: python @@ -1119,7 +1116,7 @@ you can instead declare the variable to be :ref:`Final `: reveal_type(c) # Revealed type is 'int' expects_literal(c) # ...but this type checks! -If we do not provide an explicit type in the Final, the type of ``c`` becomes +If you do not provide an explicit type in the Final, the type of ``c`` becomes context-sensitive: mypy will basically try "substituting" the original assigned value whenever it's used before performing type checking. So, mypy will type-check the above program almost as if it were written like so: @@ -1136,8 +1133,8 @@ the above program almost as if it were written like so: This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)`` reports ``int``. -So while declaring a variable to be Final is not quite the same thing as adding -an explicit Literal annotation, it often leads to the same effect in practice. +So while changing a variable to be Final is not quite the same thing as adding +an explicit ``Literal[...]`` annotation, it often leads to the same effect in practice. Limitations ----------- From 5b6041b03509d4c35bb98094f404c8b2d919b558 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Thu, 10 Jan 2019 07:37:11 -0800 Subject: [PATCH 5/5] Respond to final wave of code review --- docs/source/index.rst | 1 + docs/source/literal_types.rst | 159 ++++++++++++++++++++++++++++++++ docs/source/more_types.rst | 166 +--------------------------------- 3 files changed, 161 insertions(+), 165 deletions(-) create mode 100644 docs/source/literal_types.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 05a6cce610fd..78f401d2d438 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -39,6 +39,7 @@ Mypy is a static type checker for Python 3 and Python 2.7. stubs generics more_types + literal_types final_attrs metaclasses diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst new file mode 100644 index 000000000000..da851fc8f03c --- /dev/null +++ b/docs/source/literal_types.rst @@ -0,0 +1,159 @@ +.. _literal_types: + +Literal types +============= + +.. note:: + + Literal is an officially supported feature, but is highly experimental + and should be considered to be in alpha stage. It is very likely that future + releases of mypy will modify the behavior of literal types, either by adding + new features or by tuning or removing problematic ones. + +Literal types let you indicate that an expression is equal to some specific +primitive value. For example, if we annotate a variable with type ``Literal["foo"]``, +mypy will understand that variable is not only of type ``str``, but is also +equal to specifically the string ``"foo"``. + +This feature is primarily useful when annotating functions that behave +differently based on the exact value the caller provides. For example, +suppose we have a function ``fetch_data(...)`` that returns ``bytes`` if the +first argument is ``True``, and ``str`` if it's ``False``. We can construct a +precise type signature for this function using ``Literal[...]`` and overloads: + +.. code-block:: python + + from typing import overload, Union + from typing_extensions import Literal + + # The first two overloads use Literal[...] so we can + # have precise return types: + + @overload + def fetch_data(raw: Literal[True]) -> bytes: ... + @overload + def fetch_data(raw: Literal[False]) -> str: ... + + # The last overload is a fallback in case the caller + # provides a regular bool: + + @overload + def fetch_data(raw: bool) -> Union[bytes, str]: ... + + def fetch_data(raw: bool) -> Union[bytes, str]: + # Implementation is omitted + ... + + reveal_type(fetch_data(True)) # Revealed type is 'bytes' + reveal_type(fetch_data(False)) # Revealed type is 'str' + + # Variables declared without annotations will continue to have an + # inferred type of 'bool'. + + variable = True + reveal_type(fetch_data(variable)) # Revealed type is 'Union[bytes, str]' + +Parameterizing Literals +*********************** + +Literal types may contain one or more literal bools, ints, strs, and bytes. +However, literal types **cannot** contain arbitrary expressions: +types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]`` +are all illegal. + +Literals containing two or more values are equivalent to the union of those values. +So, ``Literal[-3, b"foo", True]`` is equivalent to +``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This makes writing +more complex types involving literals a little more convenient. + +Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being +equivalent to just ``None``. This means that ``Literal[4, None]``, +``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent. + +Literals may also contain aliases to other literal types. For example, the +following program is legal: + +.. code-block:: python + + PrimaryColors = Literal["red", "blue", "yellow"] + SecondaryColors = Literal["purple", "green", "orange"] + AllowedColors = Literal[PrimaryColors, SecondaryColors] + + def paint(color: AllowedColors) -> None: ... + + paint("red") # Type checks! + paint("turquoise") # Does not type check + +Literals may not contain any other kind of type or expression. This means doing +``Literal[my_instance]``, ``Literal[Any]``, ``Literal[3.14]``, or +``Literal[{"foo": 2, "bar": 5}]`` are all illegal. + +Future versions of mypy may relax some of these restrictions. For example, we +plan on adding support for using enum values inside ``Literal[...]`` in an upcoming release. + +Declaring literal variables +*************************** + +You must explicitly add an annotation to a variable to declare that it has +a literal type: + +.. code-block:: python + + a: Literal[19] = 19 + reveal_type(a) # Revealed type is 'Literal[19]' + +In order to preserve backwards-compatibility, variables without this annotation +are **not** assumed to be literals: + +.. code-block:: python + + b = 19 + reveal_type(b) # Revealed type is 'int' + +If you find repeating the value of the variable in the type hint to be tedious, +you can instead change the variable to be :ref:`Final `: + +.. code-block:: python + + from typing_extensions import Final, Literal + + def expects_literal(x: Literal[19]) -> None: pass + + c: Final = 19 + + reveal_type(c) # Revealed type is 'int' + expects_literal(c) # ...but this type checks! + +If you do not provide an explicit type in the Final, the type of ``c`` becomes +context-sensitive: mypy will basically try "substituting" the original assigned +value whenever it's used before performing type checking. So, mypy will type-check +the above program almost as if it were written like so: + +.. code-block:: python + + from typing_extensions import Final, Literal + + def expects_literal(x: Literal[19]) -> None: pass + + reveal_type(19) + expects_literal(19) + +This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)`` +reports ``int``. + +So while changing a variable to be Final is not quite the same thing as adding +an explicit ``Literal[...]`` annotation, it often leads to the same effect in practice. + +Limitations +*********** + +Mypy will not understand expressions that use variables of type ``Literal[..]`` +on a deep level. For example, if you have a variable ``a`` of type ``Literal[3]`` +and another variable ``b`` of type ``Literal[5]``, mypy will infer that +``a + b`` has type ``int``, **not** type ``Literal[8]``. + +The basic rule is that literal types are treated as just regular subtypes of +whatever type the parameter has. For example, ``Literal[3]`` is treated as a +subtype of ``int`` and so will inherit all of ``int``'s methods directly. This +means that ``Literal[3].__add__`` accepts the same arguments and has the same +return type as ``int.__add__``. diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index a3c2f125eefe..72138a0fd553 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -2,7 +2,7 @@ More types ========== This section introduces a few additional kinds of types, including ``NoReturn``, -``NewType``, ``TypedDict``, ``Literal``, and types for async code. It also discusses +``NewType``, ``TypedDict``, and types for async code. It also discusses how to give functions more precise types using overloads. All of these are only situationally useful, so feel free to skip this section and come back when you have a need for some of them. @@ -23,10 +23,6 @@ Here's a quick summary of what's covered here: * ``TypedDict`` lets you give precise types for dictionaries that represent objects with a fixed schema, such as ``{'id': 1, 'items': ['x']}``. -* ``Literal`` lets you indicate that a given expression has a specific value. - For example, if you do ``x: Literal["foo"]``, mypy will understand that - ``x`` is not only a string, but is equal to exactly the string ``"foo"``. - * Async types let you type check programs using ``async`` and ``await``. .. _noreturn: @@ -989,163 +985,3 @@ and non-required keys, such as ``Movie`` above, will only be compatible with another TypedDict if all required keys in the other TypedDict are required keys in the first TypedDict, and all non-required keys of the other TypedDict are also non-required keys in the first TypedDict. - -.. _literal: - -Literal -******* - -.. note:: - - Literal is an officially supported feature, but is highly experimental - and should be considered to be in alpha stage. It is very likely that future - releases of mypy will modify the behavior of Literal types, either by adding - new features or by tuning or removing problematic ones. - -Literal types let you indicate that an expression is equal to some specific -primitive value. For example, if we annotate a variable with type ``Literal["foo"]``, -mypy will understand that variable is not only of type ``str``, but is also -equal to specifically the string ``"foo"``. - -This feature is primarily useful when annotating functions that behave -differently based on the exact value the caller provides. For example, -suppose we have a function ``fetch_data(...)`` that returns ``bytes`` if the -first argument is ``True``, and ``str`` if it's ``False``. We can construct a -precise type signature for this function using Literal and overloads: - -.. code-block:: python - - from typing import overload, Union - from typing_extensions import Literal - - # The first two overloads use Literal so we can - # have precise return types: - - @overload - def fetch_data(raw: Literal[True]) -> bytes: ... - @overload - def fetch_data(raw: Literal[False]) -> str: ... - - # The last overload is a fallback in case the caller - # provides a regular bool: - - @overload - def fetch_data(raw: bool) -> Union[bytes, str]: ... - - def fetch_data(raw: bool) -> Union[bytes, str]: - # Implementation is omitted - ... - - reveal_type(fetch_data(True)) # Revealed type is 'bytes' - reveal_type(fetch_data(False)) # Revealed type is 'str' - - # Variables declared without annotations will continue to have an - # inferred type of 'bool'. - - variable = True - reveal_type(fetch_data(variable)) # Revealed type is 'Union[bytes, str]' - -Parameterizing Literals ------------------------ - -Literal types may contain one or more literal bools, ints, strs, and bytes. -However, Literal types **cannot** contain arbitrary expressions: -types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]`` -are all illegal. - -Literals containing two or more values are equivalent to the union of those values. -So, ``Literal[-3, b"foo", True]`` is equivalent to -``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This makes writing -more complex types involving Literals a little more convenient. - -Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being -equivalent to just ``None``. This means that ``Literal[4, None]``, -``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent. - -Literals may also contain aliases of Literal types. For example, the following program -is legal: - -.. code-block:: python - - PrimaryColors = Literal["red", "blue", "yellow"] - SecondaryColors = Literal["purple", "green", "orange"] - AllowedColors = Literal[PrimaryColors, SecondaryColors] - - def paint(color: AllowedColors) -> None: ... - - paint("red") # Type checks! - paint("turquoise") # Does not type check - -Literals may not contain any other kind of type or expression. This means doing -``Literal[my_instance]``, ``Literal[Any]``, ``Literal[3.14]``, or -``Literal[{"foo": 2, "bar": 5}]`` are all illegal. - -Future versions of mypy may relax some of these restrictions. For example, we -plan on adding support for using enum values inside Literals in an upcoming release. - -Declaring Literal variables ---------------------------- - -You must explicitly add an annotation to a variable to declare that it has -a Literal type: - -.. code-block:: python - - a: Literal[19] = 19 - reveal_type(a) # Revealed type is 'Literal[19]' - -In order to preserve backwards-compatibility, variables without this annotation -are **not** assumed to be Literals: - -.. code-block:: python - - b = 19 - reveal_type(b) # Revealed type is 'int' - -If you find repeating the value of the variable in the type hint to be tedious, -you can instead change the variable to be :ref:`Final `: - -.. code-block:: python - - from typing_extensions import Final, Literal - - def expects_literal(x: Literal[19]) -> None: pass - - c: Final = 19 - - reveal_type(c) # Revealed type is 'int' - expects_literal(c) # ...but this type checks! - -If you do not provide an explicit type in the Final, the type of ``c`` becomes -context-sensitive: mypy will basically try "substituting" the original assigned -value whenever it's used before performing type checking. So, mypy will type-check -the above program almost as if it were written like so: - -.. code-block:: python - - from typing_extensions import Final, Literal - - def expects_literal(x: Literal[19]) -> None: pass - - reveal_type(19) - expects_literal(19) - -This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)`` -reports ``int``. - -So while changing a variable to be Final is not quite the same thing as adding -an explicit ``Literal[...]`` annotation, it often leads to the same effect in practice. - -Limitations ------------ - -Mypy will not understand expressions that use Literal variables on a deep level. -For example, if you have a variable ``a`` of type ``Literal[3]`` -and another variable ``b`` of type ``Literal[5]``, mypy will infer that -``a + b`` has type ``int``, **not** type ``Literal[8]``. - -The basic rule is that Literal types are treated as just regular subtypes of -whatever type the parameter has. For example, ``Literal[3]`` is treated as a -subtype of ``int`` and so will inherit all of ``int``'s methods directly. This -means that ``Literal[3].__add__`` accepts the same arguments and has the same -return type as ``int.__add__``.