@@ -522,14 +522,19 @@ Declaring decorators
522
522
523
523
One common application of type variable upper bounds is in declaring a
524
524
decorator that preserves the signature of the function it decorates,
525
- regardless of that signature. Here's a complete example:
525
+ regardless of that signature.
526
+
527
+ Note that class decorators are handled differently than function decorators in
528
+ mypy: decorating a class does not erase its type, even if the decorator has
529
+ incomplete type annotations.
530
+
531
+ Here's a complete example of a function decorator:
526
532
527
533
.. code-block :: python
528
534
529
535
from typing import Any, Callable, TypeVar, Tuple, cast
530
536
531
- FuncType = Callable[... , Any]
532
- F = TypeVar(' F' , bound = FuncType)
537
+ F = TypeVar(' F' , bound = Callable[... , Any])
533
538
534
539
# A decorator that preserves the signature.
535
540
def my_decorator (func : F) -> F:
@@ -543,15 +548,8 @@ regardless of that signature. Here's a complete example:
543
548
def foo (a : int ) -> str :
544
549
return str (a)
545
550
546
- # Another.
547
- @my_decorator
548
- def bar (x : float , y : float ) -> Tuple[float , float , bool ]:
549
- return (x, y, x > y)
550
-
551
551
a = foo(12 )
552
552
reveal_type(a) # str
553
- b = bar(3.14 , 0 )
554
- reveal_type(b) # Tuple[float, float, bool]
555
553
foo(' x' ) # Type check error: incompatible type "str"; expected "int"
556
554
557
555
From the final block we see that the signatures of the decorated
@@ -566,6 +564,59 @@ functions are typically small enough that this is not a big
566
564
problem. This is also the reason for the :py:func: `~typing.cast ` call in the
567
565
``return `` statement in ``my_decorator() ``. See :ref: `casts `.
568
566
567
+ .. _decorator-factories :
568
+
569
+ Decorator factories
570
+ -------------------
571
+
572
+ Functions that take arguments and return a decorator (also called second-order decorators), are
573
+ similarly supported via generics:
574
+
575
+ .. code-block :: python
576
+
577
+ from typing import Any, Callable, TypeVar
578
+
579
+ F = TypeVar(' F' , bound = Callable[... , Any])
580
+
581
+ def route (url : str ) -> Callable[[F], F]:
582
+ ...
583
+
584
+ @route (url = ' /' )
585
+ def index (request : Any) -> str :
586
+ return ' Hello world'
587
+
588
+ Sometimes the same decorator supports both bare calls and calls with arguments. This can be
589
+ achieved by combining with :py:func: `@overload <typing.overload> `:
590
+
591
+ .. code-block :: python
592
+
593
+ from typing import Any, Callable, TypeVar, overload
594
+
595
+ F = TypeVar(' F' , bound = Callable[... , Any])
596
+
597
+ # Bare decorator usage
598
+ @overload
599
+ def atomic (__func : F) -> F: ...
600
+ # Decorator with arguments
601
+ @overload
602
+ def atomic (* , savepoint : bool = True ) -> Callable[[F], F]: ...
603
+
604
+ # Implementation
605
+ def atomic (__func : Callable[... , Any] = None , * , savepoint : bool = True ):
606
+ def decorator (func : Callable[... , Any]):
607
+ ... # Code goes here
608
+ if __func is not None :
609
+ return decorator(__func)
610
+ else :
611
+ return decorator
612
+
613
+ # Usage
614
+ @atomic
615
+ def func1 () -> None : ...
616
+
617
+ @atomic (savepoint = False )
618
+ def func2 () -> None : ...
619
+
569
620
Generic protocols
570
621
*****************
571
622
0 commit comments