Skip to content

Commit 9335249

Browse files
committed
fix: raise HtagEventError when a synchronous callback returns a coroutine instead of awaiting it
1 parent 3652b73 commit 9335249

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

‎htag/runner.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,13 @@ async def handle_event(self, msg: dict[str, Any], ws: WebSocket | None) -> None:
448448
res = await callback(event)
449449
else:
450450
res = callback(event)
451+
if inspect.iscoroutine(res):
452+
from .exceptions import HtagEventError
453+
res.close()
454+
raise HtagEventError(
455+
f"Callback '{getattr(callback, '__name__', str(callback))}' returned a coroutine. "
456+
"If you call async functions, the callback itself must be declared 'async def' and use 'await'."
457+
)
451458

452459
# Handle generators/async generators for intermediate rendering
453460
if inspect.isasyncgen(res):

‎tests/test_simple_events.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,40 @@ async def test_event_empty_data():
188188
e = Event(target, msg)
189189
assert e.value is None
190190
assert e["id"] == "123"
191+
192+
193+
@pytest.mark.asyncio
194+
async def test_app_handle_coroutine_callback_error():
195+
app = App()
196+
197+
async def some_async_func():
198+
pass
199+
200+
def my_cb(e):
201+
return some_async_func() # returns a coroutine!
202+
203+
class MyTag(Tag.div):
204+
def init(self):
205+
self["oncustom"] = my_cb
206+
207+
tag = MyTag()
208+
app += tag
209+
210+
ws = AsyncMock()
211+
msg = {
212+
"id": tag.id,
213+
"event": "custom",
214+
"data": "hello"
215+
}
216+
217+
app.broadcast_updates = AsyncMock()
218+
219+
await app.handle_event(msg, ws)
220+
221+
ws.send_text.assert_called_once()
222+
payload_str = ws.send_text.call_args[0][0]
223+
from htag.utils import _obf_loads
224+
payload = _obf_loads(payload_str, getattr(app, "parano_key", None))
225+
assert payload["action"] == "error"
226+
assert "Callback 'my_cb' returned a coroutine" in payload["traceback"]
227+

0 commit comments

Comments
 (0)