From db97dc4610f3fdacd00bc3f8f62e0fc03994d020 Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Mon, 22 Aug 2022 14:46:44 +0300 Subject: [PATCH 1/8] Add Aliasing Decorators section to libraries.rst I believe I'm not the only one who will struggle with behavior of `Callable` type aliases. I hope if this is added to the documentation - some people will save an hour on figuring how `TypeAlias` work. I'm not sure if it is worth introducing "decorator factory" term here, but it is used in [mypy documentation](https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories). Related discussion: https://github.com/python/typing/discussions/1236 --- docs/source/libraries.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 201cf4138..7b138d708 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -490,6 +490,36 @@ original signature, thus blinding type checkers and other tools that provide signature assistance. As such, library authors are discouraged from creating decorators that mutate function signatures in this manner. +Aliasing Decorators +------------------- + +When writing a library with a couple of decorator factories +(i.e. functions returning decorators, like ``complex_decorator`` from +Annotating Decorators section) it may be tempting to create a shortcut +for a decorator. + +Different type checkers handle ``TypeAlias`` involving ``Callable`` in a +different manner, so the most portable and easy way to create a shortcut +is to define a callable ``Protocol`` as described in `PEP +544 `_: + +.. code:: python + + _F = TypeVar("_F", bound=Callable[..., Any]) + + class PDecorator(Protocol): + def __call__(self, _: _F, /) -> _F: + ... + + def decorator_factory(*, mode: str) -> PDecorator: + """ + Decorator factory is invoked with arguments like this: + @decorator_factory(mode="easy") + def my_function(): ... + """ + ... + + Generic Classes and Functions ----------------------------- From 724920d52cf73ee99327f3eb7722eff4fb4bdcba Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Mon, 22 Aug 2022 19:23:48 +0300 Subject: [PATCH 2/8] Add link to annotation-decorators Co-authored-by: Sebastian Rittau --- docs/source/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 7b138d708..2bbc2fe8b 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -495,7 +495,7 @@ Aliasing Decorators When writing a library with a couple of decorator factories (i.e. functions returning decorators, like ``complex_decorator`` from -Annotating Decorators section) it may be tempting to create a shortcut +:ref:`annotating-decorators` section) it may be tempting to create a shortcut for a decorator. Different type checkers handle ``TypeAlias`` involving ``Callable`` in a From 45d421106536a918c45ae7247c4293e10492ce37 Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Mon, 22 Aug 2022 19:47:19 +0300 Subject: [PATCH 3/8] Use -> IdentityFunction --- docs/source/libraries.rst | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 2bbc2fe8b..126c591f6 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -501,23 +501,47 @@ for a decorator. Different type checkers handle ``TypeAlias`` involving ``Callable`` in a different manner, so the most portable and easy way to create a shortcut is to define a callable ``Protocol`` as described in `PEP -544 `_: +544 `_. + +There is already a ``Protcol`` called ``IndentityFunction`` defined in ``_typeshed``: .. code:: python - _F = TypeVar("_F", bound=Callable[..., Any]) + if TYPE_CHECKING: + from _typeshed import IdentityFunction + + def decorator_factory(*, mode: str) -> "IdentityFunction": + """ + Decorator factory is invoked with arguments like this: + @decorator_factory(mode="easy") + def my_function(): ... + """ + ... + +For non-trivial decorators with custom logic, it is still possible +to define a custom protocol using ``ParamSpec`` and ``Concatenate`` +mechanisms described in `PEP 612 +`__: + +.. code:: python + + class Client: ... - class PDecorator(Protocol): - def __call__(self, _: _F, /) -> _F: + P = ParamSpec("P") + R = TypeVar("R") + + class PClientInjector(Protocol): + def __call__(self, _: Callable[Concatenate[Client, P], R], /) -> Callable[P, R]: ... - def decorator_factory(*, mode: str) -> PDecorator: - """ - Decorator factory is invoked with arguments like this: - @decorator_factory(mode="easy") - def my_function(): ... - """ - ... + def inject_client(service: str) -> PClientInjector: + """ + Decorator factory is invoked with arguments like this: + @inject_client("testing") + def my_function(client: Client, value: int): ... + + my_function then takes only value + """ Generic Classes and Functions From 30f2d4999dea29c236ea6dc8b65bbcc0821770ad Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Mon, 22 Aug 2022 23:25:56 +0300 Subject: [PATCH 4/8] Fix typos Co-authored-by: Alex Waygood --- docs/source/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 126c591f6..2c8c7d295 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -503,7 +503,7 @@ different manner, so the most portable and easy way to create a shortcut is to define a callable ``Protocol`` as described in `PEP 544 `_. -There is already a ``Protcol`` called ``IndentityFunction`` defined in ``_typeshed``: +There is already a ``Protocol`` called ``IdentityFunction`` defined in ``_typeshed``: .. code:: python From 9631223dc5d645643a13f5aa86ee7f7db16cf359 Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Wed, 24 Aug 2022 19:34:07 +0300 Subject: [PATCH 5/8] Add a link to _typeshed readme --- docs/source/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 2c8c7d295..e9750701f 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -503,7 +503,7 @@ different manner, so the most portable and easy way to create a shortcut is to define a callable ``Protocol`` as described in `PEP 544 `_. -There is already a ``Protocol`` called ``IdentityFunction`` defined in ``_typeshed``: +There is already a ``Protocol`` called ``IdentityFunction`` defined in `_typeshed `_: .. code:: python From 1816e48183d4082f28cf8bbfc2ef53f7e13c69a9 Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Tue, 13 Sep 2022 20:22:47 +0300 Subject: [PATCH 6/8] Add anchor --- docs/source/libraries.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index e9750701f..199285b67 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -453,6 +453,8 @@ specified only by name, use the keyword-only separator (``*``). def create_user(age: int, *, dob: Optional[date] = None): ... +.. _annotating-decorators: + Annotating Decorators --------------------- @@ -490,6 +492,8 @@ original signature, thus blinding type checkers and other tools that provide signature assistance. As such, library authors are discouraged from creating decorators that mutate function signatures in this manner. +.. _aliasing-decorators: + Aliasing Decorators ------------------- From ac70ae1ff2542d548b0fb6d4693a8349198203c6 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 17 Mar 2025 11:07:41 +0100 Subject: [PATCH 7/8] Link fixes Also, replace class names with links to the Python docs --- docs/guides/libraries.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/guides/libraries.rst b/docs/guides/libraries.rst index 3eae8836d..edc385197 100644 --- a/docs/guides/libraries.rst +++ b/docs/guides/libraries.rst @@ -460,14 +460,19 @@ When writing a library with a couple of decorator factories :ref:`annotating-decorators` section) it may be tempting to create a shortcut for a decorator. -Different type checkers handle ``TypeAlias`` involving ``Callable`` in a +Different type checkers handle :class:`TypeAlias ` involving +:class:`Callable ` in a different manner, so the most portable and easy way to create a shortcut -is to define a callable ``Protocol`` as described in `PEP -544 `_. +is to define a callable :class:`Protocol ` as described in the +:ref:`callback-protocols` section of the Typing Specification. -There is already a ``Protocol`` called ``IdentityFunction`` defined in `_typeshed `_: +There is already a :class:`Protocol ` called +``IdentityFunction`` defined in +`_typeshed `_: .. code:: python + + from typing import TYPE_CHECKING if TYPE_CHECKING: from _typeshed import IdentityFunction @@ -481,9 +486,8 @@ There is already a ``Protocol`` called ``IdentityFunction`` defined in `_typeshe ... For non-trivial decorators with custom logic, it is still possible -to define a custom protocol using ``ParamSpec`` and ``Concatenate`` -mechanisms described in `PEP 612 -`__: +to define a custom protocol using :class:`ParamSpec ` +and :class:`Concatenate ` mechanisms: .. code:: python From 7e52aedf7cd2aab14606837bae8c535992950f3e Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 17 Mar 2025 11:11:33 +0100 Subject: [PATCH 8/8] Link fixes, add an article --- docs/guides/libraries.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/libraries.rst b/docs/guides/libraries.rst index edc385197..d5edb1378 100644 --- a/docs/guides/libraries.rst +++ b/docs/guides/libraries.rst @@ -456,11 +456,11 @@ Aliasing Decorators ------------------- When writing a library with a couple of decorator factories -(i.e. functions returning decorators, like ``complex_decorator`` from +(i.e. functions returning decorators, like ``complex_decorator`` from the :ref:`annotating-decorators` section) it may be tempting to create a shortcut for a decorator. -Different type checkers handle :class:`TypeAlias ` involving +Different type checkers handle :data:`TypeAlias ` involving :class:`Callable ` in a different manner, so the most portable and easy way to create a shortcut is to define a callable :class:`Protocol ` as described in the @@ -487,7 +487,7 @@ There is already a :class:`Protocol ` called For non-trivial decorators with custom logic, it is still possible to define a custom protocol using :class:`ParamSpec ` -and :class:`Concatenate ` mechanisms: +and :data:`Concatenate ` mechanisms: .. code:: python