@@ -412,6 +412,8 @@ specified only by name, use the keyword-only separator (``*``).
412
412
def create_user (age :int ,* ,dob : Optional[date]= None ):
413
413
...
414
414
415
+ .. _annotating-decorators :
416
+
415
417
Annotating Decorators
416
418
---------------------
417
419
@@ -448,6 +450,66 @@ original signature, thus blinding type checkers and other tools that
448
450
provide signature assistance. As such, library authors are discouraged
449
451
from creating decorators that mutate function signatures in this manner.
450
452
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 typingimport TYPE_CHECKING
476
+
477
+ if TYPE_CHECKING :
478
+ from _typeshedimport 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
+
451
513
Generic Classes and Functions
452
514
-----------------------------
453
515