From 9731d545153849aeb2f51d628ae3f7837452076a Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Tue, 7 Apr 2026 10:30:46 +0530 Subject: [PATCH 01/15] docs: consistently use PEP 695 syntax in generics reference --- docs/reference/generics.rst | 245 ++++++++++++++++++------------------ 1 file changed, 124 insertions(+), 121 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index c39d2bc11..99ff841dc 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -19,11 +19,7 @@ Here is a very simple generic class that represents a stack: .. code-block:: python - from typing import TypeVar, Generic - - T = TypeVar('T') - - class Stack(Generic[T]): + class Stack[T]: def __init__(self) -> None: # Create an empty list with items of type T self.items: list[T] = [] @@ -37,6 +33,23 @@ Here is a very simple generic class that represents a stack: def empty(self) -> bool: return not self.items +.. note:: + + The type parameter syntax (e.g., ``class Foo[T]:``) is available in Python 3.12 and newer. + For earlier Python versions, generic classes need to be defined using + ``TypeVar`` and ``Generic``, as shown below. + +For compatibility with older Python versions, the same class may be written as: + +.. code-block:: python + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Stack(Generic[T]): + ... + The ``Stack`` class can be used to represent a stack of any type: ``Stack[int]``, ``Stack[tuple[int, str]]``, etc. @@ -56,7 +69,7 @@ construction of the instance will be type checked correspondingly. .. code-block:: python - class Box(Generic[T]): + class Box[T]: def __init__(self, content: T) -> None: self.content = content @@ -70,17 +83,14 @@ Defining subclasses of generic classes ************************************** User-defined generic classes and generic classes defined in :py:mod:`typing` -can be used as a base class for another class (generic or non-generic). For example: +can be used as base classes for other classes (generic or non-generic). For example: .. code-block:: python - from typing import Generic, TypeVar, Mapping, Iterator - - KT = TypeVar('KT') - VT = TypeVar('VT') + from typing import Mapping, Iterator # This is a generic subclass of Mapping - class MyMap(Mapping[KT, VT]): + class MyMap[KT, VT](Mapping[KT, VT]): def __getitem__(self, k: KT) -> VT: ... def __iter__(self) -> Iterator[KT]: ... def __len__(self) -> int: ... @@ -96,12 +106,12 @@ can be used as a base class for another class (generic or non-generic). For exam data: StrDict[int, int] # error: "StrDict" expects no type arguments, but 2 given data2: StrDict # OK - # This is a user-defined generic class - class Receiver(Generic[T]): - def accept(self, value: T) -> None: ... + # This is a user-defined generic class + class Receiver[T]: + def accept(self, value: T) -> None: ... - # This is a generic subclass of Receiver - class AdvancedReceiver(Receiver[T]): ... + # This is a generic subclass of Receiver + class AdvancedReceiver[T](Receiver[T]): ... .. note:: @@ -111,36 +121,21 @@ can be used as a base class for another class (generic or non-generic). For exam protocols like :py:class:`~typing.Iterable`, which use :ref:`structural subtyping `. -:py:class:`Generic ` can be omitted from bases if there are -other base classes that include type variables, such as ``Mapping[KT, VT]`` -in the above example. If you include ``Generic[...]`` in bases, then -it should list all type variables present in other bases (or more, -if needed). The order of type variables is defined by the following -rules: - -* If ``Generic[...]`` is present, then the order of variables is - always determined by their order in ``Generic[...]``. -* If there are no ``Generic[...]`` in bases, then all type variables - are collected in the lexicographic order (i.e. by first appearance). - -For example: +When using the new syntax, the order of type parameters is determined +by their order in the class definition's type parameter list. For example: .. code-block:: python - from typing import Generic, TypeVar, Any - - T = TypeVar('T') - S = TypeVar('S') - U = TypeVar('U') + from typing import Any - class One(Generic[T]): ... - class Another(Generic[T]): ... + class One[T]: ... + class Another[T]: ... - class First(One[T], Another[S]): ... - class Second(One[T], Another[S], Generic[S, U, T]): ... + class First[T, S](One[T], Another[S]): ... + class Second[S, U, T](One[T], Another[S]): ... x: First[int, str] # Here T is bound to int, S is bound to str - y: Second[int, str, Any] # Here T is Any, S is int, and U is str + y: Second[int, str, Any] # Here S is int, U is str, and T is Any .. _generic-functions: @@ -152,12 +147,10 @@ where the types of the arguments or return value have some relationship: .. code-block:: python - from typing import TypeVar, Sequence - - T = TypeVar('T') + from typing import Sequence # A generic function! - def first(seq: Sequence[T]) -> T: + def first[T](seq: Sequence[T]) -> T: return seq[0] As with generic classes, the type variable can be replaced with any @@ -181,17 +174,15 @@ functions do not share any typing relationship to each other: .. code-block:: python - from typing import TypeVar, Sequence - - T = TypeVar('T') + from typing import Sequence - def first(seq: Sequence[T]) -> T: + def first[T](seq: Sequence[T]) -> T: return seq[0] - def last(seq: Sequence[T]) -> T: + def last[T](seq: Sequence[T]) -> T: return seq[-1] -Variables should not have a type variable in their type unless the type variable +Variables should not have a type parameter in their type unless the type parameter is bound by a containing generic class, generic function or generic alias. .. _generic-methods-and-generic-self: @@ -205,16 +196,16 @@ the class definition. .. code-block:: python - # T is the type variable bound by this class - class PairedBox(Generic[T]): + # T is the type parameter bound by this class + class PairedBox[T]: def __init__(self, content: T) -> None: self.content = content - # S is a type variable bound only in this method - def first(self, x: list[S]) -> S: + # S is a type parameter bound only in this method + def first[S](self, x: list[S]) -> S: return x[0] - def pair_with_first(self, x: list[S]) -> tuple[S, T]: + def pair_with_first[S](self, x: list[S]) -> tuple[S, T]: return (x[0], self.content) box = PairedBox("asdf") @@ -228,12 +219,8 @@ methods: .. code-block:: python - from typing import TypeVar - - T = TypeVar('T', bound='Shape') - class Shape: - def set_scale(self: T, scale: float) -> T: + def set_scale[T: Shape](self: T, scale: float) -> T: self.scale = scale return self @@ -259,15 +246,13 @@ For class methods, you can also define generic ``cls``, using :py:class:`type`: .. code-block:: python - from typing import Optional, TypeVar, Type - - T = TypeVar('T', bound='Friend') + from typing import Optional class Friend: other: Optional["Friend"] = None @classmethod - def make_pair(cls: type[T]) -> tuple[T, T]: + def make_pair[T: Friend](cls: type[T]) -> tuple[T, T]: a, b = cls(), cls() a.other = b b.other = a @@ -352,10 +337,12 @@ Let us illustrate this by few simple examples: .. code-block:: python - # We'll use these classes in the examples below - class Shape: ... - class Triangle(Shape): ... - class Square(Shape): ... + from typing import Sequence, Union, Callable + + # We'll use these classes in the examples below + class Shape: ... + class Triangle(Shape): ... + class Square(Shape): ... * Most immutable containers, such as :py:class:`~typing.Sequence` and :py:class:`~typing.FrozenSet` are covariant. :py:data:`~typing.Union` is @@ -422,10 +409,13 @@ Let us illustrate this by few simple examples: Another example of an invariant type is :py:class:`~typing.Dict`. Most mutable containers are invariant. -By default, all user-defined generics are invariant. -To declare a given generic class as covariant or contravariant use -type variables defined with special keyword arguments ``covariant`` or -``contravariant``. For example: +The variance of user-defined generics depends on how type parameters +are used. When using Python 3.12 type parameter syntax, type checkers +can infer the variance automatically. + +Explicit variance annotations are mainly needed when using legacy +``TypeVar`` syntax with the ``covariant`` or ``contravariant`` +arguments. For example: .. code-block:: python @@ -458,25 +448,22 @@ It's therefore often useful to be able to limit the types that a type variable can take on, for instance, by restricting it to values that are subtypes of a specific type. -Such a type is called the upper bound of the type variable, and is specified -with the ``bound=...`` keyword argument to :py:class:`~typing.TypeVar`. +Such a type is called the upper bound of the type parameter. In modern Python +(3.12+), this can be expressed directly in the type parameter list using +``[T: Bound]`` syntax. .. code-block:: python - from typing import TypeVar, SupportsAbs + from typing import SupportsAbs - T = TypeVar('T', bound=SupportsAbs[float]) + def largest_in_absolute_value[T: SupportsAbs[float]](*xs: T) -> T: + return max(xs, key=abs) In the definition of a generic function that uses such a type variable ``T``, the type represented by ``T`` is assumed to be a subtype of its upper bound, so the function can use methods of the upper bound on values of type ``T``. -.. code-block:: python - - def largest_in_absolute_value(*xs: T) -> T: - return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. - In a call to such a function, the type ``T`` must be replaced by a type that is a subtype of its upper bound. Continuing the example above: @@ -501,6 +488,12 @@ be avoided if an upper bound can be made to work instead, as above. An example is a type variable that can only have values ``str`` and ``bytes``: +.. note:: + + Constrained type variables defined with :py:class:`~typing.TypeVar` have + slightly different semantics from bounded type parameters (see below), so + the legacy ``TypeVar`` syntax is still commonly used for this pattern. + .. code-block:: python from typing import TypeVar @@ -620,6 +613,7 @@ Here's how one could annotate the decorator: from typing import Any, Callable, TypeVar, cast + # Legacy approach using TypeVar; see ParamSpec below for a better alternative. F = TypeVar('F', bound=Callable[..., Any]) # A decorator that preserves the signature. @@ -651,12 +645,9 @@ for a more faithful type annotation: .. code-block:: python - from typing import Callable, ParamSpec, TypeVar - - P = ParamSpec('P') - T = TypeVar('T') + from typing import Callable - def printing_decorator(func: Callable[P, T]) -> Callable[P, T]: + def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]: def wrapper(*args: P.args, **kwds: P.kwargs) -> T: print("Calling", func) return func(*args, **kwds) @@ -672,13 +663,10 @@ alter the signature of the input function: .. code-block:: python - from typing import Callable, ParamSpec, TypeVar - - P = ParamSpec('P') - T = TypeVar('T') + from typing import Callable # We reuse 'P' in the return type, but replace 'T' with 'str' - def stringify(func: Callable[P, T]) -> Callable[P, str]: + def stringify[**P, T](func: Callable[P, T]) -> Callable[P, str]: def wrapper(*args: P.args, **kwds: P.kwargs) -> str: return str(func(*args, **kwds)) return wrapper @@ -695,12 +683,11 @@ Or insert an argument: .. code-block:: python - from typing import Callable, Concatenate, ParamSpec, TypeVar - - P = ParamSpec('P') - T = TypeVar('T') + from typing import Callable, Concatenate - def printing_decorator(func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: + def printing_decorator[**P, T]( + func: Callable[P, T], + ) -> Callable[Concatenate[str, P], T]: def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: print("Calling", func, "with", msg) return func(*args, **kwds) @@ -720,6 +707,12 @@ Decorator factories Functions that take arguments and return a decorator (also called second-order decorators), are similarly supported via generics: +.. note:: + + The following examples use legacy ``TypeVar`` syntax because + decorator factories using :py:func:`~typing.overload` do not yet + have a concise PEP 695 equivalent. + .. code-block:: python from typing import Any, Callable, TypeVar @@ -776,11 +769,9 @@ Example: .. code-block:: python - from typing import Protocol, TypeVar - - T = TypeVar('T') + from typing import Protocol - class Box(Protocol[T]): + class Box[T](Protocol): content: T def do_stuff(one: Box[str], other: Box[bytes]) -> None: @@ -818,7 +809,23 @@ return type, but the type variable is invariant: class ReadOnlyBox(Protocol[T]): # error: Invariant type variable "T" used in protocol where covariant one is expected def content(self) -> T: ... -This example correctly uses a covariant type variable: +With PEP 695 syntax, type checkers infer the variance automatically +based on how the type parameter is used. Since ``T`` only appears in +covariant position (return type), this is accepted: + +.. code-block:: python + + from typing import Protocol + + class ReadOnlyBox[T](Protocol): # OK -- T inferred as covariant + def content(self) -> T: ... + + ax: ReadOnlyBox[float] = ... + ay: ReadOnlyBox[int] = ... + ax = ay # OK -- ReadOnlyBox is covariant + +With the legacy ``TypeVar`` syntax, covariance must be declared +explicitly using ``covariant=True``: .. code-block:: python @@ -839,9 +846,9 @@ Generic protocols can also be recursive. Example: .. code-block:: python - T = TypeVar('T') + from typing import Protocol - class Linked(Protocol[T]): + class Linked[T](Protocol): val: T def next(self) -> 'Linked[T]': ... @@ -849,7 +856,7 @@ Generic protocols can also be recursive. Example: val: int def next(self) -> 'L': ... - def last(seq: Linked[T]) -> T: ... + def last[T](seq: Linked[T]) -> T: ... result = last(L()) reveal_type(result) # Revealed type is "builtins.int" @@ -868,28 +875,24 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases .. code-block:: python - from typing import TypeVar, Iterable, Union, Callable - - S = TypeVar('S') + from typing import Iterable, Union, Callable - TInt = tuple[int, S] - UInt = Union[S, int] - CBack = Callable[..., S] + type TInt[S] = tuple[int, S] + type UInt[S] = Union[S, int] + type CBack[S] = Callable[..., S] def response(query: str) -> UInt[str]: # Same as Union[str, int] ... - def activate(cb: CBack[S]) -> S: # Same as Callable[..., S] + def activate[S](cb: CBack[S]) -> S: # Same as Callable[..., S] ... table_entry: TInt # Same as tuple[int, Any] - T = TypeVar('T', int, float, complex) + type Vec[T: (int, float, complex)] = Iterable[tuple[T, T]] - Vec = Iterable[tuple[T, T]] - - def inproduct(v: Vec[T]) -> T: + def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: return sum(x*y for x, y in v) - def dilate(v: Vec[T], scale: T) -> Vec[T]: + def dilate[T: (int, float, complex)](v: Vec[T], scale: T) -> Vec[T]: return ((x * scale, y * scale) for x, y in v) v1: Vec[int] = [] # Same as Iterable[tuple[int, int]] @@ -903,7 +906,7 @@ defeating the purpose of using aliases. Example: .. code-block:: python - from typing import TypeVar, Generic, Optional + from typing import Optional from example1 import AliasType from example2 import Vec @@ -912,9 +915,7 @@ defeating the purpose of using aliases. Example: def fun() -> AliasType: ... - T = TypeVar('T') - - class NewVec(Vec[T]): + class NewVec[T](Vec[T]): ... for i, j in NewVec[int](): @@ -931,7 +932,9 @@ Generic class internals You may wonder what happens at runtime when you index a generic class. Indexing returns a *generic alias* to the original class that returns instances -of the original class on instantiation: +of the original class on instantiation. The following example uses the legacy +syntax for illustration, but the runtime behavior is identical for classes +defined with the new ``class Stack[T]:`` syntax: .. code-block:: python From 7564afc78bd7aae39a99ec38e0b8c8da88ffc44d Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Sun, 28 Jun 2026 17:38:45 +0530 Subject: [PATCH 02/15] docs: improve wording for legacy syntax note --- docs/reference/generics.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 99ff841dc..b00b6c9fc 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -35,11 +35,9 @@ Here is a very simple generic class that represents a stack: .. note:: - The type parameter syntax (e.g., ``class Foo[T]:``) is available in Python 3.12 and newer. - For earlier Python versions, generic classes need to be defined using - ``TypeVar`` and ``Generic``, as shown below. - -For compatibility with older Python versions, the same class may be written as: + The type parameter syntax (for example, ``class Foo[T]:``) is available in + Python 3.12 and newer. For earlier Python versions, use the legacy + ``TypeVar`` and ``Generic`` syntax shown below. .. code-block:: python @@ -183,7 +181,7 @@ functions do not share any typing relationship to each other: return seq[-1] Variables should not have a type parameter in their type unless the type parameter -is bound by a containing generic class, generic function or generic alias. +is bound by a containing generic class, generic function, or generic alias. .. _generic-methods-and-generic-self: From d551ced772e8aacfa85d3941b8879cfbad0c534c Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Thu, 29 Oct 2026 12:09:10 +0530 Subject: [PATCH 03/15] docs: addressed the review comments --- docs/reference/generics.rst | 76 +++++++++++-------------------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index b00b6c9fc..c92f85529 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -119,8 +119,8 @@ can be used as base classes for other classes (generic or non-generic). For exam protocols like :py:class:`~typing.Iterable`, which use :ref:`structural subtyping `. -When using the new syntax, the order of type parameters is determined -by their order in the class definition's type parameter list. For example: +When using the ``class Foo[T, S]`` type parameter list syntax, the order of +type parameters is determined by their order in the parameter list. For example: .. code-block:: python @@ -409,9 +409,10 @@ Let us illustrate this by few simple examples: The variance of user-defined generics depends on how type parameters are used. When using Python 3.12 type parameter syntax, type checkers -can infer the variance automatically. +infer the variance automatically based on how the type parameter is +used in the class body. -Explicit variance annotations are mainly needed when using legacy +Explicit variance annotations are only needed when using the legacy ``TypeVar`` syntax with the ``covariant`` or ``contravariant`` arguments. For example: @@ -455,7 +456,7 @@ Such a type is called the upper bound of the type parameter. In modern Python from typing import SupportsAbs def largest_in_absolute_value[T: SupportsAbs[float]](*xs: T) -> T: - return max(xs, key=abs) + return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. In the definition of a generic function that uses such a type variable ``T``, the type represented by ``T`` is assumed to be a subtype of @@ -484,34 +485,23 @@ In some cases, it can be useful to restrict the values that a type variable can exactly a specific set of types. This feature is a little complex and should be avoided if an upper bound can be made to work instead, as above. -An example is a type variable that can only have values ``str`` and ``bytes``: - -.. note:: - - Constrained type variables defined with :py:class:`~typing.TypeVar` have - slightly different semantics from bounded type parameters (see below), so - the legacy ``TypeVar`` syntax is still commonly used for this pattern. +An example is a type variable that can only have values ``str`` and ``bytes``. +With the Python 3.12 type parameter syntax, constraints are written as a tuple +after the ``:`` in the type parameter list: .. code-block:: python - from typing import TypeVar - - AnyStr = TypeVar('AnyStr', str, bytes) + def concat[AnyStr: (str, bytes)](x: AnyStr, y: AnyStr) -> AnyStr: + return x + y -This is actually such a common type variable that :py:data:`~typing.AnyStr` is -defined in :py:mod:`typing`. +:py:data:`~typing.AnyStr` is also available as a predefined type variable in +:py:mod:`typing`, defined using the legacy ``TypeVar`` syntax. -We can use :py:data:`~typing.AnyStr` to define a function that can concatenate -two strings or bytes objects, but it can't be called with other -argument types: +We can use this function to concatenate two strings or bytes objects, but it +can't be called with other argument types: .. code-block:: python - from typing import AnyStr - - def concat(x: AnyStr, y: AnyStr) -> AnyStr: - return x + y - concat('a', 'b') # Okay concat(b'a', b'b') # Okay concat(1, 2) # Error! @@ -609,13 +599,10 @@ Here's how one could annotate the decorator: .. code-block:: python - from typing import Any, Callable, TypeVar, cast - - # Legacy approach using TypeVar; see ParamSpec below for a better alternative. - F = TypeVar('F', bound=Callable[..., Any]) + from typing import Any, Callable, cast # A decorator that preserves the signature. - def printing_decorator(func: F) -> F: + def printing_decorator[F: Callable[..., Any]](func: F) -> F: def wrapper(*args, **kwds): print("Calling", func) return func(*args, **kwds) @@ -705,19 +692,11 @@ Decorator factories Functions that take arguments and return a decorator (also called second-order decorators), are similarly supported via generics: -.. note:: - - The following examples use legacy ``TypeVar`` syntax because - decorator factories using :py:func:`~typing.overload` do not yet - have a concise PEP 695 equivalent. - .. code-block:: python - from typing import Any, Callable, TypeVar - - F = TypeVar('F', bound=Callable[..., Any]) + from typing import Any, Callable - def route(url: str) -> Callable[[F], F]: + def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: ... @route(url='/') @@ -729,16 +708,14 @@ achieved by combining with :py:func:`@overload `: .. code-block:: python - from typing import Any, Callable, Optional, TypeVar, overload - - F = TypeVar('F', bound=Callable[..., Any]) + from typing import Any, Callable, Optional, overload # Bare decorator usage @overload - def atomic(__func: F) -> F: ... + def atomic[F: Callable[..., Any]](__func: F) -> F: ... # Decorator with arguments @overload - def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + def atomic[F: Callable[..., Any]](*, savepoint: bool = True) -> Callable[[F], F]: ... # Implementation def atomic(__func: Optional[Callable[..., Any]] = None, *, savepoint: bool = True): @@ -789,9 +766,6 @@ Example: y: Box[int] = ... x = y # Error -- Box is invariant -Note that ``class ClassName(Protocol[T])`` is allowed as a shorthand for -``class ClassName(Protocol, Generic[T])``, as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`, - The main difference between generic protocols and ordinary generic classes is that the declared variances of generic type variables in a protocol are checked against how they are used in the protocol definition. The protocol in this @@ -913,12 +887,6 @@ defeating the purpose of using aliases. Example: def fun() -> AliasType: ... - class NewVec[T](Vec[T]): - ... - - for i, j in NewVec[int](): - ... - OIntVec = Optional[Vec[int]] Using type variable bounds or values in generic aliases has the same effect From 9a6993f5d6d5f4764291558b0ed22cc6766e8e2f Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:19:10 +0530 Subject: [PATCH 04/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index c92f85529..405a83af4 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -878,7 +878,6 @@ defeating the purpose of using aliases. Example: .. code-block:: python - from typing import Optional from example1 import AliasType from example2 import Vec From 5f78bd1ede6572182dfddf1b2b70b958e4ff1ec8 Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:19:27 +0530 Subject: [PATCH 05/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 405a83af4..9dc64ac4b 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -718,7 +718,7 @@ achieved by combining with :py:func:`@overload `: def atomic[F: Callable[..., Any]](*, savepoint: bool = True) -> Callable[[F], F]: ... # Implementation - def atomic(__func: Optional[Callable[..., Any]] = None, *, savepoint: bool = True): + def atomic(__func: Callable[..., Any] | None = None, *, savepoint: bool = True): def decorator(func: Callable[..., Any]): ... # Code goes here if __func is not None: From 8d3149b123b055603021b10def1da0d2c4c4456b Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:19:40 +0530 Subject: [PATCH 06/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 9dc64ac4b..93ad02c45 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -886,7 +886,7 @@ defeating the purpose of using aliases. Example: def fun() -> AliasType: ... - OIntVec = Optional[Vec[int]] + type OIntVec = Vec[int] | None Using type variable bounds or values in generic aliases has the same effect as in generic classes/functions. From 19e637b12233f34b2e8ef9b125805dd867692123 Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:19:55 +0530 Subject: [PATCH 07/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 93ad02c45..d1fa93a1f 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -850,7 +850,7 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases from typing import Iterable, Union, Callable type TInt[S] = tuple[int, S] - type UInt[S] = Union[S, int] + type UInt[S] = S | int type CBack[S] = Callable[..., S] def response(query: str) -> UInt[str]: # Same as Union[str, int] From 4bc394dafad7c94beeff4b07c8a7023a928a89ed Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:20:12 +0530 Subject: [PATCH 08/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index d1fa93a1f..d70d37ad8 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -708,7 +708,8 @@ achieved by combining with :py:func:`@overload `: .. code-block:: python - from typing import Any, Callable, Optional, overload + from collections.abc import Callable + from typing import Any, overload # Bare decorator usage @overload From 94d3e5535308c2f0d2b409b0d5179248b7f8b997 Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:20:27 +0530 Subject: [PATCH 09/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index d70d37ad8..96e1ab8ec 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -694,7 +694,8 @@ similarly supported via generics: .. code-block:: python - from typing import Any, Callable + from collections.abc import Callable + from typing import Any def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: ... From 15db7cfad2955ccd5ce5d5d918204ebc08a54fab Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:20:48 +0530 Subject: [PATCH 10/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 96e1ab8ec..5801d127e 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -849,7 +849,7 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases .. code-block:: python - from typing import Iterable, Union, Callable + from collections.abc import Callable, Iterable type TInt[S] = tuple[int, S] type UInt[S] = S | int From 1b5e25f57f7a7dd77a22ebc2b5f766aec78ff3af Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:21:14 +0530 Subject: [PATCH 11/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 5801d127e..677d2a654 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -145,7 +145,7 @@ where the types of the arguments or return value have some relationship: .. code-block:: python - from typing import Sequence + from collection.abc import Sequence # A generic function! def first[T](seq: Sequence[T]) -> T: From 8fd00fe80317a48867eb4ce2e47f2cb2469b393d Mon Sep 17 00:00:00 2001 From: Bhuvansh Date: Fri, 30 Oct 2026 00:21:37 +0530 Subject: [PATCH 12/15] Update docs/reference/generics.rst Co-authored-by: Joren Hammudoglu --- docs/reference/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 677d2a654..9d855bafb 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -85,7 +85,7 @@ can be used as base classes for other classes (generic or non-generic). For exam .. code-block:: python - from typing import Mapping, Iterator + from collection.abc import Mapping, Iterator # This is a generic subclass of Mapping class MyMap[KT, VT](Mapping[KT, VT]): From 0dc2a52db26a68dd0cb26f0624d3c13351b890cb Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Fri, 30 Oct 2026 00:27:36 +0530 Subject: [PATCH 13/15] docs: addressed review comments --- docs/reference/generics.rst | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index 9d855bafb..a6adcfaf4 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -247,7 +247,7 @@ For class methods, you can also define generic ``cls``, using :py:class:`type`: from typing import Optional class Friend: - other: Optional["Friend"] = None + other: "Friend | None" = None @classmethod def make_pair[T: Friend](cls: type[T]) -> tuple[T, T]: @@ -412,9 +412,9 @@ are used. When using Python 3.12 type parameter syntax, type checkers infer the variance automatically based on how the type parameter is used in the class body. -Explicit variance annotations are only needed when using the legacy -``TypeVar`` syntax with the ``covariant`` or ``contravariant`` -arguments. For example: +When using the legacy ``TypeVar`` syntax, variance can be declared +explicitly using the ``covariant=True`` or ``contravariant=True`` +keyword arguments, or inferred using ``infer_variance=True``. For example: .. code-block:: python @@ -494,9 +494,6 @@ after the ``:`` in the type parameter list: def concat[AnyStr: (str, bytes)](x: AnyStr, y: AnyStr) -> AnyStr: return x + y -:py:data:`~typing.AnyStr` is also available as a predefined type variable in -:py:mod:`typing`, defined using the legacy ``TypeVar`` syntax. - We can use this function to concatenate two strings or bytes objects, but it can't be called with other argument types: @@ -546,11 +543,6 @@ instance of exactly ``str`` in the above example: >>> print(type(ss)) -You can also use a :py:class:`~typing.TypeVar` with a restricted set of possible -values when defining a generic class. For example, you can use the type -:py:class:`Pattern[AnyStr] ` for the return value of :py:func:`re.compile`, -since regular expressions can be based on a string or a bytes pattern. - A type variable may not have both a value restriction (see :ref:`type-variable-upper-bound`) and an upper bound. @@ -783,9 +775,10 @@ return type, but the type variable is invariant: class ReadOnlyBox(Protocol[T]): # error: Invariant type variable "T" used in protocol where covariant one is expected def content(self) -> T: ... -With PEP 695 syntax, type checkers infer the variance automatically -based on how the type parameter is used. Since ``T`` only appears in -covariant position (return type), this is accepted: +With the Python 3.12 type parameter syntax, type checkers infer the +variance automatically based on how the type parameter is used. +Since ``T`` only appears in covariant position (return type), this +is accepted: .. code-block:: python @@ -844,8 +837,7 @@ Type aliases can be generic. In this case they can be used in two ways: Subscripted aliases are equivalent to original types with substituted type variables, so the number of type arguments must match the number of free type variables in the generic type alias. Unsubscripted aliases are treated as original types with free -variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases -<484#type-aliases>`): +variables replaced with ``Any``. Examples: .. code-block:: python From b52f0f1d0e4b0aa37dcf04864994ae8f23f2756d Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Fri, 30 Oct 2026 00:31:43 +0530 Subject: [PATCH 14/15] docs: fix collection.abc typos and remaining typing imports --- docs/reference/generics.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index a6adcfaf4..de9369d94 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -85,7 +85,7 @@ can be used as base classes for other classes (generic or non-generic). For exam .. code-block:: python - from collection.abc import Mapping, Iterator + from collections.abc import Mapping, Iterator # This is a generic subclass of Mapping class MyMap[KT, VT](Mapping[KT, VT]): @@ -145,7 +145,7 @@ where the types of the arguments or return value have some relationship: .. code-block:: python - from collection.abc import Sequence + from collections.abc import Sequence # A generic function! def first[T](seq: Sequence[T]) -> T: @@ -172,7 +172,7 @@ functions do not share any typing relationship to each other: .. code-block:: python - from typing import Sequence + from collections.abc import Sequence def first[T](seq: Sequence[T]) -> T: return seq[0] @@ -244,8 +244,6 @@ For class methods, you can also define generic ``cls``, using :py:class:`type`: .. code-block:: python - from typing import Optional - class Friend: other: "Friend | None" = None @@ -591,7 +589,8 @@ Here's how one could annotate the decorator: .. code-block:: python - from typing import Any, Callable, cast + from collections.abc import Callable + from typing import Any, cast # A decorator that preserves the signature. def printing_decorator[F: Callable[..., Any]](func: F) -> F: @@ -622,7 +621,7 @@ for a more faithful type annotation: .. code-block:: python - from typing import Callable + from collections.abc import Callable def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]: def wrapper(*args: P.args, **kwds: P.kwargs) -> T: @@ -640,7 +639,7 @@ alter the signature of the input function: .. code-block:: python - from typing import Callable + from collections.abc import Callable # We reuse 'P' in the return type, but replace 'T' with 'str' def stringify[**P, T](func: Callable[P, T]) -> Callable[P, str]: @@ -660,7 +659,8 @@ Or insert an argument: .. code-block:: python - from typing import Callable, Concatenate + from collections.abc import Callable + from typing import Concatenate def printing_decorator[**P, T]( func: Callable[P, T], From 3298c6936d9287c51f9d0677e3baf88a70c88d07 Mon Sep 17 00:00:00 2001 From: Bhuvansh855 Date: Fri, 30 Oct 2026 00:32:31 +0530 Subject: [PATCH 15/15] docs: move Callable and Sequence imports to collections.abc in variance section --- docs/reference/generics.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst index de9369d94..22b20ee7e 100644 --- a/docs/reference/generics.rst +++ b/docs/reference/generics.rst @@ -333,7 +333,8 @@ Let us illustrate this by few simple examples: .. code-block:: python - from typing import Sequence, Union, Callable + from collections.abc import Callable, Sequence + from typing import Union # We'll use these classes in the examples below class Shape: ...