@@ -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+
415417Annotating Decorators
416418---------------------
417419
@@ -448,6 +450,66 @@ original signature, thus blinding type checkers and other tools that
448450provide signature assistance. As such, library authors are discouraged
449451from 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