From db97dc4610f3fdacd00bc3f8f62e0fc03994d020 Mon Sep 17 00:00:00 2001 From: Andrei Bodrov Date: Mon, 22 Aug 2022 14:46:44 +0300 Subject: [PATCH 1/6] 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/6] 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/6] 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/6] 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/6] 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/6] 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 -------------------