From 38736fb270fa582589fbe9afe71be29b2b90c53d Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Mon, 14 Sep 2026 09:28:32 -0400 Subject: [PATCH] fix(rtc): use inspect.iscoroutinefunction in EventEmitter.on --- livekit-rtc/livekit/rtc/event_emitter.py | 3 +- tests/rtc/test_emitter.py | 40 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/livekit-rtc/livekit/rtc/event_emitter.py b/livekit-rtc/livekit/rtc/event_emitter.py index bc64d1d7..e5c24685 100644 --- a/livekit-rtc/livekit/rtc/event_emitter.py +++ b/livekit-rtc/livekit/rtc/event_emitter.py @@ -1,5 +1,4 @@ import inspect -import asyncio from typing import Any, Callable, Dict, Set, Optional, Generic, TypeVar from .log import logger @@ -157,7 +156,7 @@ def greet(name): ``` """ if callback is not None: - if asyncio.iscoroutinefunction(callback): + if inspect.iscoroutinefunction(callback): raise ValueError( "Cannot register an async callback with `.on()`. Use `asyncio.create_task` within your synchronous callback instead." ) diff --git a/tests/rtc/test_emitter.py b/tests/rtc/test_emitter.py index b5a1bf59..13c9fa97 100644 --- a/tests/rtc/test_emitter.py +++ b/tests/rtc/test_emitter.py @@ -1,5 +1,7 @@ +import functools from livekit.rtc import EventEmitter from typing import Any, Literal +import warnings import pytest @@ -102,3 +104,41 @@ def on_error_another() -> None: emitter.emit("error") assert len(calls) == 2 + + +def test_on_does_not_warn() -> None: + """Registering a callback must not emit a DeprecationWarning. + + `asyncio.iscoroutinefunction` is deprecated in Python 3.14 and slated for removal + in 3.16; `inspect.iscoroutinefunction` is the supported replacement. + """ + EventTypes = Literal["connected"] + + emitter = EventEmitter[EventTypes]() + + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + @emitter.on("connected") + def on_connected() -> None: + pass + + emitter.once("connected", on_connected) + + emitter.emit("connected") + + +def test_on_rejects_async_callback() -> None: + """`.on()` refuses coroutine functions, however they are spelled.""" + EventTypes = Literal["connected"] + + emitter = EventEmitter[EventTypes]() + + async def on_connected() -> None: + pass + + with pytest.raises(ValueError, match="Cannot register an async callback"): + emitter.on("connected", on_connected) + + with pytest.raises(ValueError, match="Cannot register an async callback"): + emitter.on("connected", functools.partial(on_connected))