diff --git a/pep-0589.rst b/pep-0589.rst index daf5c6914..24fa45344 100644 --- a/pep-0589.rst +++ b/pep-0589.rst @@ -372,9 +372,9 @@ Discussion: f(b) # Type check error: 'B' not compatible with 'A' b['x'] + 1 # Runtime error: None + 1 -* A TypedDict type with required keys is not consistent with a - TypedDict type with non-required keys, since the latter allows keys - to be deleted. Example where this is relevant:: +* A TypedDict type with a required key is not consistent with a + TypedDict type where the same key is a non-required key, since the + latter allows keys to be deleted. Example where this is relevant:: class A(TypedDict, total=False): x: int @@ -479,8 +479,9 @@ the most important type safety violations to prevent: 3. A key that is not defined in the TypedDict type is added. A key that is not a literal should generally be rejected, since its -value is unknown during type checking, and thus can cause some of -the above violations. +value is unknown during type checking, and thus can cause some of the +above violations. (`Use of Final Values and Literal Types`_ +generalizes this to cover final names and literal types.) The use of a key that is not known to exist should be reported as an error, even if this wouldn't necessarily generate a runtime type @@ -503,9 +504,15 @@ Type checkers should reject the following operations on TypedDict objects as unsafe, even though they are valid for normal dictionaries: * Operations with arbitrary ``str`` keys (instead of string literals - or other expressions with known string values) should be rejected. - This involves both destructive operations such as setting an item - and read-only operations such as subscription expressions. + or other expressions with known string values) should generally be + rejected. This involves both destructive operations such as setting + an item and read-only operations such as subscription expressions. + As an exception to the above rule, ``d.get(e)`` and ``e in d`` + should be allowed for TypedDict objects, for an arbitrary expression + ``e`` with type ``str``. The motivation is that these are safe and + can be useful for introspecting TypedDict objects. The static type + of ``d.get(e)`` should be ``object`` if the string value of ``e`` + cannot be determined statically. * ``clear()`` is not safe since it could remove required keys, some of which may not be directly visible because of structural @@ -527,6 +534,33 @@ In some cases potentially unsafe operations may be accepted if the alternative is to generate false positive errors for idiomatic code. +Use of Final Values and Literal Types +------------------------------------- + +Type checkers should allow final names (PEP 591 [#PEP-591]_) with +string values to be used instead of string literals in operations on +TypedDict objects. For example, this is valid:: + + YEAR: Final = 'year' + + m: Movie = {'name': 'Alien', 'year': 1979} + years_since_epoch = m[YEAR] - 1970 + +Similarly, an expression with a suitable literal type +(PEP 586 [#PEP-586]_) can be used instead of a literal value:: + + def get_value(movie: Movie, + key: Literal['year', 'name']) -> Union[int, str]: + return movie[key] + +Type checkers are only expected to support actual string literals, not +final names or literal types, for specifying keys in a TypedDict type +definition. Also, only a boolean literal can be used to specify +totality in a TypedDict definition. The motivation for this is to +make type declarations self-contained, and to simplify the +implementation of type checkers. + + Backwards Compatibility ======================= @@ -544,7 +578,9 @@ Reference Implementation The mypy [#mypy]_ type checker supports TypedDict types. A reference implementation of the runtime component is provided in the -``mypy_extensions`` [#mypy_extensions]_ module. +``typing_extensions`` [#typing_extensions]_ module. The original +implementation was in the ``mypy_extensions`` [#mypy_extensions]_ +module. Rejected Alternatives @@ -576,7 +612,7 @@ this proposal: * TypedDict types can't be used in ``isinstance()`` or ``issubclass()`` checks. The reasoning is similar to why runtime type checks aren't - supported in general. + supported in general with many type hints. These features were left out from this PEP, but they are potential extensions to be added in the future: @@ -588,7 +624,8 @@ extensions to be added in the future: dictionary type. * There is no way to individually specify whether each key is required - or not. No proposed syntax was clear enough. + or not. No proposed syntax was clear enough, and we expect that + there is limited need for this. * TypedDict can't be used for specifying the type of a ``**kwargs`` argument. This would allow restricting the allowed keyword @@ -624,8 +661,17 @@ References .. [#PEP-483] PEP 483, The Theory of Type Hints, van Rossum, Levkivskyi (http://www.python.org/dev/peps/pep-0483) +.. [#PEP-591] PEP 591, Adding a final qualifier to typing, Sullivan, + Levkivskyi (http://www.python.org/dev/peps/pep-0591) + +.. [#PEP-586] PEP 586, Literal Types, Lee, Levkivskyi, Lehtosalo + (http://www.python.org/dev/peps/pep-0586) + .. [#mypy] http://www.mypy-lang.org/ +.. [#typing_extensions] + https://github.com/python/typing/tree/master/typing_extensions + .. [#mypy_extensions] https://github.com/python/mypy_extensions .. [#typing_inspect] https://github.com/ilevkivskyi/typing_inspect