33from collections .abc import Awaitable , Callable , Iterator
44from dataclasses import dataclass , field
55from functools import partial
6- from inspect import getmro , isclass
6+ from inspect import Parameter , getmro , isclass
7+ from inspect import signature as inspect_signature
78from typing import Any , Protocol , Self , runtime_checkable
89
910import injection
@@ -88,16 +89,50 @@ class HandlerDecorator[I, O]:
8889 manager : HandlerManager [I , O ]
8990 injection_module : injection .Module = field (default_factory = injection .mod )
9091
91- def __call__ (self , input_type : type [I ], / ) -> Any :
92- def decorator (wrapped : type [Handler [[I ], O ]]) -> type [Handler [[I ], O ]]:
93- if not isclass (wrapped ) or not issubclass (wrapped , Handler ):
94- raise TypeError (f"`{ wrapped } ` isn't a valid handler." )
92+ def __call__ (
93+ self ,
94+ input_or_handler_type : type [I ] | HandlerType [[I ], O ] | None = None ,
95+ / ,
96+ ) -> Any :
97+ if input_or_handler_type is None :
98+ return self .__decorator
99+
100+ elif isclass (input_or_handler_type ) and issubclass (
101+ input_or_handler_type ,
102+ Handler ,
103+ ):
104+ return self .__decorator (input_or_handler_type )
105+
106+ return partial (self .__decorator , input_type = input_or_handler_type ) # type: ignore[arg-type]
107+
108+ def __decorator (
109+ self ,
110+ wrapped : HandlerType [[I ], O ],
111+ * ,
112+ input_type : type [I ] | None = None ,
113+ ) -> HandlerType [[I ], O ]:
114+ factory = self .injection_module .make_async_factory (wrapped )
115+ input_type = input_type or _resolve_input_type (wrapped )
116+ self .manager .subscribe (input_type , factory )
117+ return wrapped
95118
96- factory = self .injection_module .make_async_factory (wrapped )
97- self .manager .subscribe (input_type , factory )
98- return wrapped
99119
100- return decorator
120+ def _resolve_input_type [I , O ](handler_type : HandlerType [[I ], O ]) -> type [I ]:
121+ fake_handle_method = handler_type .handle .__get__ (NotImplemented )
122+ signature = inspect_signature (fake_handle_method , eval_str = True )
123+
124+ for parameter in signature .parameters .values ():
125+ input_type = parameter .annotation
126+
127+ if input_type is Parameter .empty :
128+ break
129+
130+ return input_type
131+
132+ raise TypeError (
133+ f"Unable to resolve input type for handler `{ handler_type } `, "
134+ "`handle` method must have a type annotation for its first parameter."
135+ )
101136
102137
103138def _make_handle_function [I , O ](
@@ -106,6 +141,6 @@ def _make_handle_function[I, O](
106141 return partial (__handle , factory = factory )
107142
108143
109- async def __handle [I , O ](input_value : I , factory : HandlerFactory [[I ], O ]) -> O :
144+ async def __handle [I , O ](input_value : I , * , factory : HandlerFactory [[I ], O ]) -> O :
110145 handler = await factory ()
111146 return await handler .handle (input_value )
0 commit comments