Skip to content

Commit 736f9ec

Browse files
authored
Add Aliasing Decorators section to libraries.rst (#1243)
1 parent be7ba97 commit 736f9ec

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

docs/guides/libraries.rst

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ specified only by name, use the keyword-only separator (``*``).
412412
def create_user(age: int, *, dob: Optional[date] = None):
413413
...
414414
415+
.. _annotating-decorators:
416+
415417
Annotating Decorators
416418
---------------------
417419

@@ -448,6 +450,66 @@ original signature, thus blinding type checkers and other tools that
448450
provide signature assistance. As such, library authors are discouraged
449451
from creating decorators that mutate function signatures in this manner.
450452

453+
.. _aliasing-decorators:
454+
455+
Aliasing Decorators
456+
-------------------
457+
458+
When writing a library with a couple of decorator factories
459+
(i.e. functions returning decorators, like ``complex_decorator`` from the
460+
:ref:`annotating-decorators` section) it may be tempting to create a shortcut
461+
for a decorator.
462+
463+
Different type checkers handle :data:`TypeAlias <typing.TypeAlias>` involving
464+
:class:`Callable <collections.abc.Callable>` in a
465+
different manner, so the most portable and easy way to create a shortcut
466+
is to define a callable :class:`Protocol <typing.Protocol>` as described in the
467+
:ref:`callback-protocols` section of the Typing Specification.
468+
469+
There is already a :class:`Protocol <typing.Protocol>` called
470+
``IdentityFunction`` defined in
471+
`_typeshed <https://github.com/python/typeshed/blob/main/stdlib/_typeshed/README.md>`_:
472+
473+
.. code:: python
474+
475+
from typing import TYPE_CHECKING
476+
477+
if TYPE_CHECKING:
478+
from _typeshed import IdentityFunction
479+
480+
def decorator_factory(*, mode: str) -> "IdentityFunction":
481+
"""
482+
Decorator factory is invoked with arguments like this:
483+
@decorator_factory(mode="easy")
484+
def my_function(): ...
485+
"""
486+
...
487+
488+
For non-trivial decorators with custom logic, it is still possible
489+
to define a custom protocol using :class:`ParamSpec <typing.ParamSpec>`
490+
and :data:`Concatenate <typing.Concatenate>` mechanisms:
491+
492+
.. code:: python
493+
494+
class Client: ...
495+
496+
P = ParamSpec("P")
497+
R = TypeVar("R")
498+
499+
class PClientInjector(Protocol):
500+
def __call__(self, _: Callable[Concatenate[Client, P], R], /) -> Callable[P, R]:
501+
...
502+
503+
def inject_client(service: str) -> PClientInjector:
504+
"""
505+
Decorator factory is invoked with arguments like this:
506+
@inject_client("testing")
507+
def my_function(client: Client, value: int): ...
508+
509+
my_function then takes only value
510+
"""
511+
512+
451513
Generic Classes and Functions
452514
-----------------------------
453515

0 commit comments

Comments
 (0)