11from __future__ import annotations
22
33import functools
4+ import inspect
45from logging import getLogger
56from typing import TYPE_CHECKING
67
78from pydantic import ValidationError
9+ from typing_extensions import TypeGuard # noqa: UP035
810
911from streamdeck .actions import Action , ActionBase , ActionRegistry
1012from streamdeck .command_sender import StreamDeckCommandSender
1113from streamdeck .event_listener import EventListener , EventListenerManager
1214from streamdeck .models .events .adapter import EventAdapter
1315from streamdeck .models .events .common import ContextualEventMixin
14- from streamdeck .types import (
15- EventHandlerBasicFunc ,
16- EventHandlerFunc ,
17- TEvent_contra ,
18- is_bindable_handler ,
19- )
2016from streamdeck .utils .logging import configure_streamdeck_logger
2117from streamdeck .websocket import WebSocketClient
2218
2521 from collections .abc import Generator
2622 from typing import Any , Literal
2723
24+ from streamdeck .actions import (
25+ EventHandlerFunc ,
26+ EventModel_contra ,
27+ InjectableParams ,
28+ )
2829 from streamdeck .models .events import EventBase
2930
3031
32+ BindableEventHandlerFunc = EventHandlerFunc [EventModel_contra , [StreamDeckCommandSender ]]
33+ """Type alias for a bindable event handler function that takes an event (of subtype of EventBase) and a command_sender parameter that is to be injected."""
34+ BoundEventHandlerFunc = EventHandlerFunc [EventModel_contra , []]
35+ """Type alias for a bound event handler function that takes an event (of subtype of EventBase) and no other parameters.
36+
37+ Typically used for event handlers that have already had parameters injected.
38+ """
39+
40+
41+
3142# TODO: Fix this up to push to a log in the apropos directory and filename.
3243logger = getLogger ("streamdeck.manager" )
3344
3445
46+ def is_bindable_handler (handler : EventHandlerFunc [EventModel_contra , InjectableParams ]) -> TypeGuard [BindableEventHandlerFunc [EventModel_contra ]]:
47+ """Check if the handler is prebound with the `command_sender` parameter."""
48+ # Check dynamically if the `command_sender`'s name is in the handler's arguments.
49+ return "command_sender" in inspect .signature (handler ).parameters
50+
51+
52+ def is_not_bindable_handler (handler : EventHandlerFunc [EventModel_contra , InjectableParams ]) -> TypeGuard [BoundEventHandlerFunc [EventModel_contra ]]:
53+ """Check if the handler only accepts the event_data parameter.
54+
55+ If this function returns False after the is_bindable_handler check is True, then the function has invalid parameters, and will subsequently need to be handled in the calling code.
56+ """
57+ handler_params = inspect .signature (handler ).parameters
58+ return len (handler_params ) == 1 and "event_data" in handler_params
59+
3560
3661class PluginManager :
3762 """Manages plugin actions and communicates with a WebSocket server to handle events."""
@@ -105,7 +130,7 @@ def register_event_listener(self, listener: EventListener) -> None:
105130 for event_model in listener .event_models :
106131 self ._event_adapter .add_model (event_model )
107132
108- def _inject_command_sender (self , handler : EventHandlerFunc [TEvent_contra ], command_sender : StreamDeckCommandSender ) -> EventHandlerBasicFunc [ TEvent_contra ]:
133+ def _inject_command_sender (self , handler : EventHandlerFunc [EventModel_contra , InjectableParams ], command_sender : StreamDeckCommandSender ) -> BoundEventHandlerFunc [ EventModel_contra ]:
109134 """Inject command_sender into handler if it accepts it as a parameter.
110135
111136 Args:
@@ -116,8 +141,13 @@ def _inject_command_sender(self, handler: EventHandlerFunc[TEvent_contra], comma
116141 The handler with command_sender injected if needed
117142 """
118143 if is_bindable_handler (handler ):
144+ # If the handler accepts command_sender, inject it and return the handler.
119145 return functools .partial (handler , command_sender = command_sender )
120146
147+ if not is_not_bindable_handler (handler ):
148+ # If the handler is neither bindable nor not bindable, raise an error.
149+ raise TypeError (f"Invalid event handler function signature: { handler } " ) # noqa: TRY003, EM102
150+
121151 return handler
122152
123153 def _stream_event_data (self ) -> Generator [EventBase , None , None ]:
0 commit comments