From ee09374405ba8303b43b1ae35496d2dca440e52d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 07:40:04 -0700 Subject: [PATCH 1/7] gh-149995: Update typing.py docstrings Some of these docstrings read as if they were written when typing.py was first written, and things have evolved since then. A few motivations: - Call protocols protocols instead of ABCs. They are also ABCs, but the fact they are protocols is more relevant to typing. - Avoid recommending direct use of .__annotations__ and steer users to annotationlib instead. - For TypedDict, mention NotRequired before total=False since it is more general and probably more frequently useful. - For overloads, mention runtime use first instead of stub use. I think early on there was talk of allowing overload only in stubs, but it is now heavily used at runtime too and that's more likely to be relevant to users. --- Lib/typing.py | 106 +++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 130e09be4b9127..a933326ab064bd 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -5,7 +5,7 @@ * Generic, Protocol, and internal machinery to support generic aliases. All subscripted types like X[int], Union[int, str] are generic aliases. * Various "special forms" that have unique meanings in type annotations: - NoReturn, Never, ClassVar, Self, Concatenate, Unpack, and others. + Any, Never, ClassVar, Self, Concatenate, Unpack, and others. * Classes whose instances can be type arguments to generic classes and functions: TypeVar, ParamSpec, TypeVarTuple. * Public helper functions: get_type_hints, overload, cast, final, and others. @@ -591,12 +591,12 @@ def __repr__(self): class Any(metaclass=_AnyMeta): """Special type indicating an unconstrained type. - - Any is compatible with every type. - - Any assumed to have all methods. - - All values assumed to be instances of Any. + - Any is assignable to every type. + - Any assumed to have all methods and attributes. + - All values are assignable to Any. Note that all the above statements are true from the point of view of - static type checkers. At runtime, Any should not be used with instance + static type checkers. At runtime, Any cannot be used with instance checks. """ @@ -715,7 +715,7 @@ class Starship: ClassVar accepts only types and cannot be further subscribed. - Note that ClassVar is not a class itself, and should not + Note that ClassVar is not a class itself, and cannot be used with isinstance() or issubclass(). """ item = _type_check(parameters, f'{self} accepts only single type.', allow_special_forms=True) @@ -745,7 +745,7 @@ class FastConnector(Connection): @_SpecialForm def Optional(self, parameters): - """Optional[X] is equivalent to Union[X, None].""" + """Optional[X] is equivalent to X | None.""" arg = _type_check(parameters, f"{self} requires a single type.") return Union[arg, type(None)] @@ -788,7 +788,7 @@ def open_helper(file: str, mode: MODE) -> str: def TypeAlias(self, parameters): """Special form for marking type aliases. - Use TypeAlias to indicate that an assignment should + TypeAlias can be used to indicate that an assignment should be recognized as a proper type alias definition by type checkers. @@ -1796,7 +1796,7 @@ class Movie(TypedDict): def foo(**kwargs: Unpack[Movie]): ... Note that there is only some runtime checking of this operator. Not - everything the runtime allows may be accepted by static type checkers. + everything the runtime allows is accepted by static type checkers. For more information, see PEPs 646 and 692. """ @@ -2307,7 +2307,7 @@ def runtime_checkable(cls): Such protocol can be used with isinstance() and issubclass(). Raise TypeError if applied to a non-protocol class. This allows a simple-minded structural check very similar to - one trick ponies in collections.abc such as Iterable. + one-trick ponies in collections.abc such as Iterable. For example:: @@ -2377,8 +2377,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, *, format=None): """Return type hints for an object. - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals and recursively replaces all + This is often the same as obj.__annotations__ and annotationlib.get_annotations(obj), + but it handles forward references encoded as string literals and recursively replaces all 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). The argument may be a module, class, method, or function. The annotations @@ -2590,7 +2590,7 @@ def get_args(tp): def is_typeddict(tp): - """Check if an annotation is a TypedDict class. + """Check if an object is a TypedDict class. For example:: @@ -2688,9 +2688,9 @@ def overload(func): """Decorator for overloaded functions/methods. In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. - - For example:: + function in a row, each decorated with @overload, followed + with an implementation. The implementation should *not* + be decorated with @overload:: @overload def utf8(value: None) -> None: ... @@ -2698,10 +2698,11 @@ def utf8(value: None) -> None: ... def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... + def utf8(value): + ... # implementation goes here - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload:: + In a stub file or in a Protocol definition, the implementation + should be omitted:: @overload def utf8(value: None) -> None: ... @@ -2709,8 +2710,7 @@ def utf8(value: None) -> None: ... def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... - def utf8(value): - ... # implementation goes here + The overloads for a function can be retrieved at runtime using the get_overloads() function. @@ -2746,7 +2746,7 @@ def final(f): """Decorator to indicate final methods and final classes. Use this decorator to indicate to type checkers that the decorated - method cannot be overridden, and decorated class cannot be subclassed. + method cannot be overridden, and the decorated class cannot be subclassed. For example:: @@ -2899,7 +2899,7 @@ class TeamUser(User): ... And a function that takes a class argument that's a subclass of User and returns an instance of the corresponding class:: - def new_user[U](user_class: Type[U]) -> U: + def new_user[U](user_class: type[U]) -> U: user = user_class() # (Here we could write the user object to a database) return user @@ -2912,7 +2912,7 @@ def new_user[U](user_class: Type[U]) -> U: @runtime_checkable class SupportsInt(Protocol): - """An ABC with one abstract method __int__.""" + """A protocol with one abstract method __int__.""" __slots__ = () @@ -2923,7 +2923,7 @@ def __int__(self) -> int: @runtime_checkable class SupportsFloat(Protocol): - """An ABC with one abstract method __float__.""" + """A protocol with one abstract method __float__.""" __slots__ = () @@ -2934,7 +2934,7 @@ def __float__(self) -> float: @runtime_checkable class SupportsComplex(Protocol): - """An ABC with one abstract method __complex__.""" + """A protocol with one abstract method __complex__.""" __slots__ = () @@ -2945,7 +2945,7 @@ def __complex__(self) -> complex: @runtime_checkable class SupportsBytes(Protocol): - """An ABC with one abstract method __bytes__.""" + """A protocol with one abstract method __bytes__.""" __slots__ = () @@ -2956,7 +2956,7 @@ def __bytes__(self) -> bytes: @runtime_checkable class SupportsIndex(Protocol): - """An ABC with one abstract method __index__.""" + """A protocol with one abstract method __index__.""" __slots__ = () @@ -2967,7 +2967,7 @@ def __index__(self) -> int: @runtime_checkable class SupportsAbs[T](Protocol): - """An ABC with one abstract method __abs__ that is covariant in its return type.""" + """A protocol with one abstract method __abs__ that is covariant in its return type.""" __slots__ = () @@ -2978,7 +2978,7 @@ def __abs__(self) -> T: @runtime_checkable class SupportsRound[T](Protocol): - """An ABC with one abstract method __round__ that is covariant in its return type.""" + """A protocol with one abstract method __round__ that is covariant in its return type.""" __slots__ = () @@ -3095,7 +3095,7 @@ def annotate(format): def NamedTuple(typename, fields, /): - """Typed version of namedtuple. + """Typed version of collections.namedtuple. Usage:: @@ -3107,8 +3107,8 @@ class Employee(NamedTuple): Employee = collections.namedtuple('Employee', ['name', 'id']) - The resulting class has an extra __annotations__ attribute, giving a - dict that maps field names to types. (The field names are also in + The types for each field name can be retrieved by calling + annotationlib.get_annotations(Employee). (The field names are also in the _fields attribute, which is part of the namedtuple API.) An alternative equivalent functional syntax is also accepted:: @@ -3161,7 +3161,7 @@ def __new__(cls, name, bases, ns, total=True, closed=None, This method is called when TypedDict is subclassed, or when TypedDict is instantiated. This way - TypedDict supports all three syntax forms described in its docstring. + TypedDict classes can be created through both class-based and functional syntax. Subclasses and instances of TypedDict return actual dictionaries. """ for base in bases: @@ -3315,14 +3315,22 @@ def TypedDict(typename, fields, /, *, total=True, closed=None, >>> Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') True - The type info can be accessed via the Point2D.__annotations__ dict, and - the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + The type info can be accessed by calling annotationlib.get_annotations(Point2D), and + via the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. TypedDict supports an additional equivalent form:: Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) By default, all keys must be present in a TypedDict. It is possible - to override this by specifying totality:: + to override this by using the NotRequired and Required special forms:: + + class Point2D(TypedDict): + x: int # the "x" key must always be present (Required is the default) + y: NotRequired[int] # the "y" key can be omitted + + This means that a Point2D TypedDict can have the "y" key omitted, but the "x" key must be present. + Items are required by default, so the Required special form is not necessary in this example. + In addition, the total argument to the TypedDict function can be used to make all items not required:: class Point2D(TypedDict, total=False): x: int @@ -3331,16 +3339,8 @@ class Point2D(TypedDict, total=False): This means that a Point2D TypedDict can have any of the keys omitted. A type checker is only expected to support a literal False or True as the value of the total argument. True is the default, and makes all items defined in the - class body be required. - - The Required and NotRequired special forms can also be used to mark - individual keys as being required or not required:: - - class Point2D(TypedDict): - x: int # the "x" key must always be present (Required is the default) - y: NotRequired[int] # the "y" key can be omitted - - See PEP 655 for more details on Required and NotRequired. + class body be required. The Required special form can be used to mark individual + keys as required in a total=False TypedDict. The ReadOnly special form can be used to mark individual keys as immutable for type checkers:: @@ -3374,7 +3374,7 @@ class Point3D(Point2D): by default, and it may not be used with the closed argument at the same time. - See PEP 728 for more information about closed and extra_items. + See PEPs 589, 655, 705, and 728 for more information. """ ns = {'__annotations__': dict(fields)} module = _caller() @@ -3533,8 +3533,8 @@ class IO(Generic[AnyStr]): classes (text vs. binary, read vs. write vs. read/write, append-only, unbuffered). The TextIO and BinaryIO subclasses below capture the distinctions between text vs. binary, which is - pervasive in the interface; however we currently do not offer a - way to track the other distinctions in the type system. + pervasive in the interface. For more precise types, define a custom + Protocol. """ __slots__ = () @@ -3624,7 +3624,7 @@ def __exit__(self, type, value, traceback, /) -> None: class BinaryIO(IO[bytes]): - """Typed version of the return of open() in binary mode.""" + """Typed approximation of the return of open() in binary mode.""" __slots__ = () @@ -3638,7 +3638,7 @@ def __enter__(self) -> BinaryIO: class TextIO(IO[str]): - """Typed version of the return of open() in text mode.""" + """Typed approximation of the return of open() in text mode.""" __slots__ = () @@ -3705,7 +3705,7 @@ def dataclass_transform( field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = (), **kwargs: Any, ) -> _IdentityCallable: - """Decorator to mark an object as providing dataclass-like behaviour. + """Decorator to mark an object as providing dataclass-like behavior. The decorator can be applied to a function, class, or metaclass. From 0bd534520c8e60aba9719fc0550890762541f088 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 07:44:49 -0700 Subject: [PATCH 2/7] blur --- .../next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst b/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst new file mode 100644 index 00000000000000..a8e412b578da37 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst @@ -0,0 +1 @@ +Update various docstrings in :mod:`typing`. From 90edd321f0b4c16d3bf3b408ba29e62397f67a61 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 08:22:00 -0700 Subject: [PATCH 3/7] docs changes too --- Doc/library/typing.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 71b395c80166cc..3f26839093e774 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -719,8 +719,8 @@ The :data:`Any` type ==================== A special kind of type is :data:`Any`. A static type checker will treat -every type as being compatible with :data:`Any` and :data:`Any` as being -compatible with every type. +every type as assignable to :data:`Any` and :data:`Any` as assignable to +every type. This means that it is possible to perform any operation or method call on a value of type :data:`Any` and assign it to any variable:: @@ -785,7 +785,7 @@ it as a return value) of a more specialized type is a type error. For example:: hash_a(42) hash_a("foo") - # Passes type checking, since Any is compatible with all types + # Passes type checking, since Any is assignable to all types hash_b(42) hash_b("foo") @@ -851,8 +851,8 @@ using ``[]``. Special type indicating an unconstrained type. - * Every type is compatible with :data:`Any`. - * :data:`Any` is compatible with every type. + * Every type is assignable to :data:`Any`. + * :data:`Any` is assignable to every type. .. versionchanged:: 3.11 :data:`Any` can now be used as a base class. This can be useful for @@ -1292,7 +1292,7 @@ These can be used as types in annotations. They all support subscription using :data:`ClassVar` accepts only types and cannot be further subscribed. - :data:`ClassVar` is not a class itself, and should not + :data:`ClassVar` is not a class itself, and cannot be used with :func:`isinstance` or :func:`issubclass`. :data:`ClassVar` does not change Python runtime behavior, but it can be used by third-party type checkers. For example, a type checker @@ -2472,9 +2472,9 @@ types. Fields with a default value must come after any fields without a default. - The resulting class has an extra attribute ``__annotations__`` giving a - dict that maps the field names to the field types. (The field names are in - the ``_fields`` attribute and the default values are in the + The types for each field name can be retrieved by calling + :func:`annotationlib.get_annotations` on the resulting class. (The field + names are in the ``_fields`` attribute and the default values are in the ``_field_defaults`` attribute, both of which are part of the :func:`~collections.namedtuple` API.) @@ -2616,7 +2616,7 @@ types. Mark a protocol class as a runtime protocol. Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. - This allows a simple-minded structural check, very similar to "one trick ponies" + This allows a simple-minded structural check, very similar to "one-trick ponies" in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable @@ -2855,7 +2855,7 @@ types. key: T group: list[T] - A ``TypedDict`` can be introspected via annotations dicts + A ``TypedDict`` can be introspected via :func:`annotationlib.get_annotations` (see :ref:`annotations-howto` for more information on annotations best practices) and the following attributes: @@ -2982,34 +2982,34 @@ with :deco:`runtime_checkable`. .. class:: SupportsAbs - An ABC with one abstract method ``__abs__`` that is covariant + A protocol with one abstract method ``__abs__`` that is covariant in its return type. .. class:: SupportsBytes - An ABC with one abstract method ``__bytes__``. + A protocol with one abstract method ``__bytes__``. .. class:: SupportsComplex - An ABC with one abstract method ``__complex__``. + A protocol with one abstract method ``__complex__``. .. class:: SupportsFloat - An ABC with one abstract method ``__float__``. + A protocol with one abstract method ``__float__``. .. class:: SupportsIndex - An ABC with one abstract method ``__index__``. + A protocol with one abstract method ``__index__``. .. versionadded:: 3.8 .. class:: SupportsInt - An ABC with one abstract method ``__int__``. + A protocol with one abstract method ``__int__``. .. class:: SupportsRound - An ABC with one abstract method ``__round__`` + A protocol with one abstract method ``__round__`` that is covariant in its return type. .. _typing-io: From 086c6a68c71ff6a06e20cf39284b1be2b1c684e6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 08:22:42 -0700 Subject: [PATCH 4/7] Alex feedback --- Lib/typing.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index a933326ab064bd..1dd5cd3a4190ae 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2687,7 +2687,7 @@ def _overload_dummy(*args, **kwds): def overload(func): """Decorator for overloaded functions/methods. - In a stub file, place two or more stub definitions for the same + In a non-stub file, place two or more stub definitions for the same function in a row, each decorated with @overload, followed with an implementation. The implementation should *not* be decorated with @overload:: @@ -2701,8 +2701,8 @@ def utf8(value: str) -> bytes: ... def utf8(value): ... # implementation goes here - In a stub file or in a Protocol definition, the implementation - should be omitted:: + In a stub file or in an abstract method (for example, in a Protocol definition), + the implementation may be omitted:: @overload def utf8(value: None) -> None: ... @@ -2711,7 +2711,6 @@ def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... - The overloads for a function can be retrieved at runtime using the get_overloads() function. """ From 6d5b09c4199f141c99808ce09266919111c722f5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 08:34:55 -0700 Subject: [PATCH 5/7] more tweaks --- Doc/library/typing.rst | 10 +++++----- Lib/typing.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3f26839093e774..b2167cbc63a1ff 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1295,7 +1295,7 @@ These can be used as types in annotations. They all support subscription using :data:`ClassVar` is not a class itself, and cannot be used with :func:`isinstance` or :func:`issubclass`. :data:`ClassVar` does not change Python runtime behavior, but - it can be used by third-party type checkers. For example, a type checker + it can be used by static type checkers. For example, a type checker might flag the following code as an error:: enterprise_d = Starship(3000) @@ -1365,7 +1365,7 @@ These can be used as types in annotations. They all support subscription using def mutate_movie(m: Movie) -> None: m["year"] = 1999 # allowed - m["title"] = "The Matrix" # typechecker error + m["title"] = "The Matrix" # type checker error There is no runtime checking for this property. @@ -2535,7 +2535,7 @@ types. Helper class to create low-overhead :ref:`distinct types `. - A ``NewType`` is considered a distinct type by a typechecker. At runtime, + A ``NewType`` is considered a distinct type by a type checker. At runtime, however, calling a ``NewType`` returns its argument unchanged. Usage:: @@ -2898,7 +2898,7 @@ types. For backwards compatibility with Python 3.10 and below, it is also possible to use inheritance to declare both required and - non-required keys in the same ``TypedDict`` . This is done by declaring a + non-required keys in the same ``TypedDict``. This is done by declaring a ``TypedDict`` with one value for the ``total`` argument and then inheriting from it in another ``TypedDict`` with a different value for ``total``: @@ -3763,7 +3763,7 @@ Constant .. data:: TYPE_CHECKING - A special constant that is assumed to be ``True`` by 3rd party static + A special constant that is assumed to be ``True`` by static type checkers. It's ``False`` at runtime. A module which is expensive to import, and which only contain types diff --git a/Lib/typing.py b/Lib/typing.py index 1dd5cd3a4190ae..2a15cc02125566 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2810,7 +2810,7 @@ class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. -# Internal type variable used for Type[]. +# Internal type bound to class object types. CT_co = TypeVar('CT_co', covariant=True, bound=type) @@ -3403,7 +3403,7 @@ class Movie(TypedDict, total=False): year: int m = Movie( - title='The Matrix', # typechecker error if key is omitted + title='The Matrix', # type checker error if key is omitted year=1999, ) @@ -3425,7 +3425,7 @@ class Movie(TypedDict): year: NotRequired[int] m = Movie( - title='The Matrix', # typechecker error if key is omitted + title='The Matrix', # type checker error if key is omitted year=1999, ) """ @@ -3445,7 +3445,7 @@ class Movie(TypedDict): def mutate_movie(m: Movie) -> None: m["year"] = 1992 # allowed - m["title"] = "The Matrix" # typechecker error + m["title"] = "The Matrix" # type checker error There is no runtime checking for this property. """ From d712cb38ba922965b34e884c67e085f13b6f5591 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 08:38:54 -0700 Subject: [PATCH 6/7] Update Lib/typing.py Co-authored-by: Alex Waygood --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 2a15cc02125566..0831c6d366e74c 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2689,7 +2689,7 @@ def overload(func): In a non-stub file, place two or more stub definitions for the same function in a row, each decorated with @overload, followed - with an implementation. The implementation should *not* + by an implementation. The implementation should *not* be decorated with @overload:: @overload From 270a6bf2e8f58ec20a1e01c0e573d490b37ae3b7 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 18 May 2026 08:39:01 -0700 Subject: [PATCH 7/7] Update Lib/typing.py Co-authored-by: Alex Waygood --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 0831c6d366e74c..715d08e0e1603e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2377,7 +2377,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, *, format=None): """Return type hints for an object. - This is often the same as obj.__annotations__ and annotationlib.get_annotations(obj), + This is often the same as annotationlib.get_annotations(obj) or obj.__annotations__, but it handles forward references encoded as string literals and recursively replaces all 'Annotated[T, ...]' with 'T' (unless 'include_extras=True').