PEP 661: Proposed typing specification changes (#4065)

This is an attempt to specify the behavior that I suggested on Discuss
and that has been endorsed by the Typing Council.

Co-authored-by: Tal Einat <532281+taleinat@users.noreply.github.com>
This commit is contained in:
Jelle Zijlstra 2024-10-19 07:29:26 -07:00 committed by GitHub
parent 8ae21fff71
commit 1c213ece4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 45 additions and 6 deletions

View File

@ -177,12 +177,6 @@ with the same name in different modules will be distinct from each other.
Creating a copy of a sentinel object, such as by using ``copy.copy()`` or by
pickling and unpickling, will return the same object.
Type annotations for sentinel values should use ``Literal[<sentinel_object>]``.
For example::
def foo(value: int | Literal[MISSING] = MISSING) -> int:
...
The ``module_name`` optional argument should normally not need to be supplied,
as ``Sentinel()`` will usually be able to recognize the module in which it was
called. ``module_name`` should be supplied only in unusual cases when this
@ -197,6 +191,51 @@ add minimal complexity.
Ordering comparisons are undefined for sentinel objects.
Typing
------
To make usage of sentinels clear and simple in typed Python code, we propose to
amend the type system with a special case for sentinel objects.
Sentinel objects may be used in
:term:`type expressions <typing:type expression>`, representing themselves.
This is similar to how ``None`` is handled in the existing type system. For
example::
from sentinels import Sentinel
MISSING = Sentinel('MISSING')
def foo(value: int | MISSING = MISSING) -> int:
...
More formally, type checkers should recognize sentinel creations of the form
``NAME = Sentinel('NAME')`` as creating a new sentinel object. If the name
passed to the ``Sentinel`` constructor does not match the name the object is
assigned to, type checkers should emit an error.
Sentinels defined using this syntax may be used in
:term:`type expressions <typing:type expression>`. They
represent a :term:`fully static type <typing:fully static type>` that has a
single member, the sentinel object itself.
Type checkers should support narrowing union types involving sentinels
using the ``is`` and ``is not`` operators::
from sentinels import Sentinel
from typing import assert_type
MISSING = Sentinel('MISSING')
def foo(value: int | MISSING) -> None:
if value is MISSING:
assert_type(value, MISSING)
else:
assert_type(value, int)
To support usage in type expressions, the runtime implementation
of the ``Sentinel`` class should have the ``__or__`` and ``__ror__``
methods, returning :py:class:`typing.Union` objects.
Backwards Compatibility
=======================