14
14
15
15
from typing import Optional , Callable
16
16
from inspect import signature
17
- import asyncio
18
17
import logging
19
18
from starlette .routing import compile_path
20
19
from .core import expando_to_dict
25
24
_event_handlers = {} # dictionary of event_source => [(event_type, predicate, handler)]
26
25
_arg_handlers = {} # dictionary of arg_name => [(predicate, handler)]
27
26
_path_handlers = []
27
+ _arg_with_params_handlers = []
28
+ _handle_on_deprecated_warning_printed = False
28
29
29
30
30
31
def _get_arity (func : Callable ) -> int :
@@ -86,9 +87,8 @@ def wrap(func):
86
87
# if not asyncio.iscoroutinefunction(func):
87
88
# raise ValueError(f"@on function '{func_name}' must be async")
88
89
89
- if predicate :
90
- if not callable (predicate ):
91
- raise ValueError (f"@on predicate must be callable for '{ func_name } '" )
90
+ if predicate and not callable (predicate ):
91
+ raise ValueError (f"@on predicate must be callable for '{ func_name } '" )
92
92
if isinstance (arg , str ) and len (arg ):
93
93
if arg .startswith ('#' ): # location hash
94
94
rx , _ , conv = compile_path (arg [1 :])
@@ -100,6 +100,9 @@ def wrap(func):
100
100
if not len (event ):
101
101
raise ValueError (f"@on event type cannot be empty in '{ arg } ' for '{ func_name } '" )
102
102
_add_event_handler (source , event , func , predicate )
103
+ elif "{" in arg and "}" in arg :
104
+ rx , _ , conv = compile_path (arg )
105
+ _arg_with_params_handlers .append ((predicate , func , _get_arity (func ), rx , conv ))
103
106
else :
104
107
_add_handler (arg , func , predicate )
105
108
else :
@@ -110,28 +113,32 @@ def wrap(func):
110
113
return wrap
111
114
112
115
113
- async def _invoke_handler (func : Callable , arity : int , q : Q , arg : any ):
116
+ async def _invoke_handler (func : Callable , arity : int , q : Q , arg : any , ** params : any ):
114
117
if arity == 0 :
115
118
await func ()
116
119
elif arity == 1 :
117
120
await func (q )
118
- else :
121
+ elif len ( params ) == 0 :
119
122
await func (q , arg )
123
+ elif arity == len (params ) + 1 :
124
+ await func (q , ** params )
125
+ else :
126
+ await func (q , arg , ** params )
120
127
121
128
122
- async def _match_predicate (predicate : Callable , func : Callable , arity : int , q : Q , arg : any ) -> bool :
129
+ async def _match_predicate (predicate : Callable , func : Callable , arity : int , q : Q , arg : any , ** params : any ) -> bool :
123
130
if predicate :
124
131
if predicate (arg ):
125
- await _invoke_handler (func , arity , q , arg )
132
+ await _invoke_handler (func , arity , q , arg , ** params )
126
133
return True
127
134
else :
128
- if arg :
129
- await _invoke_handler (func , arity , q , arg )
135
+ if arg is not None :
136
+ await _invoke_handler (func , arity , q , arg , ** params )
130
137
return True
131
138
return False
132
139
133
140
134
- async def handle_on (q : Q ) -> bool :
141
+ async def run_on (q : Q ) -> bool :
135
142
"""
136
143
Handle the query using a query handler (a function annotated with `@on()`).
137
144
@@ -141,6 +148,67 @@ async def handle_on(q: Q) -> bool:
141
148
Returns:
142
149
True if a matching query handler was found and invoked, else False.
143
150
"""
151
+ submitted = str (q .args ['__wave_submission_name__' ])
152
+
153
+ # Event handlers.
154
+ for event_source in expando_to_dict (q .events ):
155
+ for entry in _event_handlers .get (event_source , []):
156
+ event_type , predicate , func , arity = entry
157
+ event = q .events [event_source ]
158
+ if event_type in event :
159
+ arg_value = event [event_type ]
160
+ if await _match_predicate (predicate , func , arity , q , arg_value ):
161
+ return True
162
+
163
+ # Hash handlers.
164
+ if submitted == '#' :
165
+ for rx , conv , func , arity in _path_handlers :
166
+ match = rx .match (q .args [submitted ])
167
+ if match :
168
+ params = match .groupdict ()
169
+ for key , value in params .items ():
170
+ params [key ] = conv [key ].convert (value )
171
+ if len (params ):
172
+ if arity <= 1 :
173
+ await _invoke_handler (func , arity , q , None )
174
+ else :
175
+ await func (q , ** params )
176
+ else :
177
+ await _invoke_handler (func , arity , q , None )
178
+ return True
179
+
180
+ # Arg handlers.
181
+ for entry in _arg_handlers .get (submitted , []):
182
+ predicate , func , arity = entry
183
+ if await _match_predicate (predicate , func , arity , q , q .args [submitted ]):
184
+ return True
185
+ for predicate , func , arity , rx , conv in _arg_with_params_handlers :
186
+ match = rx .match (submitted )
187
+ if match :
188
+ params = match .groupdict ()
189
+ for key , value in params .items ():
190
+ params [key ] = conv [key ].convert (value )
191
+ if await _match_predicate (predicate , func , arity , q , q .args [submitted ], ** params ):
192
+ return True
193
+
194
+ return False
195
+
196
+
197
+ async def handle_on (q : Q ) -> bool :
198
+ """
199
+ DEPRECATED: Handle the query using a query handler (a function annotated with `@on()`).
200
+
201
+ Args:
202
+ q: The query context.
203
+
204
+ Returns:
205
+ True if a matching query handler was found and invoked, else False.
206
+ """
207
+ global _handle_on_deprecated_warning_printed
208
+ if not _handle_on_deprecated_warning_printed :
209
+ print ('\033 [93m' + 'WARNING: handle_on() is deprecated, use run_on() instead.' + '\033 [0m' )
210
+ _handle_on_deprecated_warning_printed = True
211
+
144
212
event_sources = expando_to_dict (q .events )
145
213
for event_source in event_sources :
146
214
event = q .events [event_source ]
0 commit comments