From 26c802c9bb53e3d60849d514493489d0435ad18d Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:00:34 +0100 Subject: [PATCH 1/9] fix return type errors --- livekit-rtc/livekit/rtc/audio_frame.py | 2 +- livekit-rtc/livekit/rtc/event_emitter.py | 6 +++--- livekit-rtc/livekit/rtc/synchronizer.py | 4 ++-- livekit-rtc/livekit/rtc/track.py | 8 ++++---- livekit-rtc/livekit/rtc/track_publication.py | 2 +- livekit-rtc/livekit/rtc/video_frame.py | 2 +- livekit-rtc/livekit/rtc/video_stream.py | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/livekit-rtc/livekit/rtc/audio_frame.py b/livekit-rtc/livekit/rtc/audio_frame.py index 3ed9a461..7759cb03 100644 --- a/livekit-rtc/livekit/rtc/audio_frame.py +++ b/livekit-rtc/livekit/rtc/audio_frame.py @@ -186,7 +186,7 @@ def __repr__(self) -> str: ) @classmethod - def __get_pydantic_core_schema__(cls, *_: Any): + def __get_pydantic_core_schema__(cls, *_: Any) -> Any: from pydantic_core import core_schema import base64 diff --git a/livekit-rtc/livekit/rtc/event_emitter.py b/livekit-rtc/livekit/rtc/event_emitter.py index c2fabae5..bc64d1d7 100644 --- a/livekit-rtc/livekit/rtc/event_emitter.py +++ b/livekit-rtc/livekit/rtc/event_emitter.py @@ -1,6 +1,6 @@ import inspect import asyncio -from typing import Callable, Dict, Set, Optional, Generic, TypeVar +from typing import Any, Callable, Dict, Set, Optional, Generic, TypeVar from .log import logger @@ -14,7 +14,7 @@ def __init__(self) -> None: """ self._events: Dict[T_contra, Set[Callable]] = dict() - def emit(self, event: T_contra, *args) -> None: + def emit(self, event: T_contra, *args: Any) -> None: """ Trigger all callbacks associated with the given event. @@ -104,7 +104,7 @@ def greet_once(name): """ if callback is not None: - def once_callback(*args, **kwargs): + def once_callback(*args: Any, **kwargs: Any) -> None: self.off(event, once_callback) callback(*args, **kwargs) diff --git a/livekit-rtc/livekit/rtc/synchronizer.py b/livekit-rtc/livekit/rtc/synchronizer.py index 8c5f6347..813a3641 100644 --- a/livekit-rtc/livekit/rtc/synchronizer.py +++ b/livekit-rtc/livekit/rtc/synchronizer.py @@ -2,7 +2,7 @@ import logging import time from collections import deque -from typing import Optional, Union +from typing import Any, Optional, Union from .video_frame import VideoFrame from .audio_frame import AudioFrame @@ -149,7 +149,7 @@ def __init__(self, *, expected_fps: float, max_delay_tolerance_ms: float = 300) async def __aenter__(self) -> None: await self.wait_next_process() - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self.after_process() def reset(self) -> None: diff --git a/livekit-rtc/livekit/rtc/track.py b/livekit-rtc/livekit/rtc/track.py index 8a6fe692..0eeb3465 100644 --- a/livekit-rtc/livekit/rtc/track.py +++ b/livekit-rtc/livekit/rtc/track.py @@ -80,14 +80,14 @@ def create_audio_track(name: str, source: "AudioSource") -> "LocalAudioTrack": resp = FfiClient.instance.request(req) return LocalAudioTrack(resp.create_audio_track.track) - def mute(self): + def mute(self) -> None: req = proto_ffi.FfiRequest() req.local_track_mute.track_handle = self._ffi_handle.handle req.local_track_mute.mute = True FfiClient.instance.request(req) self._info.muted = True - def unmute(self): + def unmute(self) -> None: req = proto_ffi.FfiRequest() req.local_track_mute.track_handle = self._ffi_handle.handle req.local_track_mute.mute = False @@ -111,14 +111,14 @@ def create_video_track(name: str, source: "VideoSource") -> "LocalVideoTrack": resp = FfiClient.instance.request(req) return LocalVideoTrack(resp.create_video_track.track) - def mute(self): + def mute(self) -> None: req = proto_ffi.FfiRequest() req.local_track_mute.track_handle = self._ffi_handle.handle req.local_track_mute.mute = True FfiClient.instance.request(req) self._info.muted = True - def unmute(self): + def unmute(self) -> None: req = proto_ffi.FfiRequest() req.local_track_mute.track_handle = self._ffi_handle.handle req.local_track_mute.mute = False diff --git a/livekit-rtc/livekit/rtc/track_publication.py b/livekit-rtc/livekit/rtc/track_publication.py index 5095841c..ab3d67e4 100644 --- a/livekit-rtc/livekit/rtc/track_publication.py +++ b/livekit-rtc/livekit/rtc/track_publication.py @@ -106,7 +106,7 @@ def track(self) -> Optional[RemoteTrack]: def subscribed(self) -> bool: return self._subscribed - def set_subscribed(self, subscribed: bool): + def set_subscribed(self, subscribed: bool) -> None: req = proto_ffi.FfiRequest() req.set_subscribed.subscribe = subscribed req.set_subscribed.publication_handle = self._ffi_handle.handle diff --git a/livekit-rtc/livekit/rtc/video_frame.py b/livekit-rtc/livekit/rtc/video_frame.py index 76b66625..fd87a7fd 100644 --- a/livekit-rtc/livekit/rtc/video_frame.py +++ b/livekit-rtc/livekit/rtc/video_frame.py @@ -202,7 +202,7 @@ def __repr__(self) -> str: return f"rtc.VideoFrame(width={self.width}, height={self.height}, type={proto_video.VideoBufferType.Name(self.type)})" @classmethod - def __get_pydantic_core_schema__(cls, *_: Any): + def __get_pydantic_core_schema__(cls, *_: Any) -> Any: from pydantic_core import core_schema import base64 diff --git a/livekit-rtc/livekit/rtc/video_stream.py b/livekit-rtc/livekit/rtc/video_stream.py index 916328aa..f79c6882 100644 --- a/livekit-rtc/livekit/rtc/video_stream.py +++ b/livekit-rtc/livekit/rtc/video_stream.py @@ -44,7 +44,7 @@ def __init__( loop: Optional[asyncio.AbstractEventLoop] = None, capacity: int = 0, format: Optional[proto_video_frame.VideoBufferType.ValueType] = None, - **kwargs, + **kwargs: Any, ) -> None: self._loop = loop or asyncio.get_event_loop() self._ffi_queue = FfiClient.instance.queue.subscribe(self._loop) From 9f377b19d34d968a58bed39a1c781ced745547de Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:10:01 +0100 Subject: [PATCH 2/9] fix async and unused type errors --- livekit-api/livekit/api/access_token.py | 4 ++-- livekit-api/livekit/api/livekit_api.py | 10 +++++----- livekit-rtc/livekit/rtc/audio_stream.py | 4 ++-- livekit-rtc/livekit/rtc/data_stream.py | 20 ++++++++++---------- livekit-rtc/livekit/rtc/participant.py | 9 +++++---- livekit-rtc/livekit/rtc/room.py | 22 +++++++++++----------- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/livekit-api/livekit/api/access_token.py b/livekit-api/livekit/api/access_token.py index 9d0e5145..43ca5bd2 100644 --- a/livekit-api/livekit/api/access_token.py +++ b/livekit-api/livekit/api/access_token.py @@ -278,9 +278,9 @@ def verify(self, token: str, *, verify_signature: bool = True) -> Claims: return grant_claims -def camel_to_snake(t: str): +def camel_to_snake(t: str) -> str: return re.sub(r"(? str: return "".join(word.capitalize() if i else word for i, word in enumerate(t.split("_"))) diff --git a/livekit-api/livekit/api/livekit_api.py b/livekit-api/livekit/api/livekit_api.py index e57bb4e3..16d26831 100644 --- a/livekit-api/livekit/api/livekit_api.py +++ b/livekit-api/livekit/api/livekit_api.py @@ -6,7 +6,7 @@ from .sip_service import SipService from .agent_dispatch_service import AgentDispatchService from .connector_service import ConnectorService -from typing import Optional +from typing import Any, Optional class LiveKitAPI: @@ -96,21 +96,21 @@ def connector(self) -> ConnectorService: """Instance of the ConnectorService""" return self._connector - async def aclose(self): + async def aclose(self) -> None: """Close the API client Call this before your application exits or when the API client is no longer needed.""" # we do not close custom sessions, that's up to the caller - if not self._custom_session: + if not self._custom_session and self._session is not None: await self._session.close() - async def __aenter__(self): + async def __aenter__(self) -> "LiveKitAPI": """@private Support for `async with`""" return self - async def __aexit__(self, exc_type, exc_val, exc_tb): + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: """@private Support for `async with`""" diff --git a/livekit-rtc/livekit/rtc/audio_stream.py b/livekit-rtc/livekit/rtc/audio_stream.py index b4dd43dc..27713d84 100644 --- a/livekit-rtc/livekit/rtc/audio_stream.py +++ b/livekit-rtc/livekit/rtc/audio_stream.py @@ -65,7 +65,7 @@ def __init__( num_channels: int = 1, frame_size_ms: int | None = None, noise_cancellation: Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]] = None, - **kwargs, + **kwargs: Any, ) -> None: """Initialize an `AudioStream` instance. @@ -266,7 +266,7 @@ def _create_owned_stream_from_participant( resp = FfiClient.instance.request(req) return resp.audio_stream_from_participant.stream - async def _run(self): + async def _run(self) -> None: while True: event = await self._ffi_queue.wait_for(self._is_event) audio_event: proto_audio_frame.AudioStreamEvent = event.audio_stream_event diff --git a/livekit-rtc/livekit/rtc/data_stream.py b/livekit-rtc/livekit/rtc/data_stream.py index beb806f4..92ce9c24 100644 --- a/livekit-rtc/livekit/rtc/data_stream.py +++ b/livekit-rtc/livekit/rtc/data_stream.py @@ -66,10 +66,10 @@ def __init__( ) self._queue: asyncio.Queue[proto_DataStream.Chunk | None] = asyncio.Queue() - async def _on_chunk_update(self, chunk: proto_DataStream.Chunk): + async def _on_chunk_update(self, chunk: proto_DataStream.Chunk) -> None: await self._queue.put(chunk) - async def _on_stream_close(self, trailer: proto_DataStream.Trailer): + async def _on_stream_close(self, trailer: proto_DataStream.Trailer) -> None: self.info.attributes = self.info.attributes or {} self.info.attributes.update(trailer.attributes) await self._queue.put(None) @@ -114,10 +114,10 @@ def __init__(self, header: proto_DataStream.Header, capacity: int = 0) -> None: ) self._queue: asyncio.Queue[proto_DataStream.Chunk | None] = asyncio.Queue(capacity) - async def _on_chunk_update(self, chunk: proto_DataStream.Chunk): + async def _on_chunk_update(self, chunk: proto_DataStream.Chunk) -> None: await self._queue.put(chunk) - async def _on_stream_close(self, trailer: proto_DataStream.Trailer): + async def _on_stream_close(self, trailer: proto_DataStream.Trailer) -> None: self.info.attributes = self.info.attributes or {} self.info.attributes.update(trailer.attributes) await self._queue.put(None) @@ -166,7 +166,7 @@ def __init__( self._sender_identity = sender_identity or self._local_participant.identity self._closed = False - async def _send_header(self): + async def _send_header(self) -> None: req = proto_ffi.FfiRequest( send_stream_header=proto_room.SendStreamHeaderRequest( header=self._header, @@ -188,7 +188,7 @@ async def _send_header(self): if cb.send_stream_header.error: raise ConnectionError(cb.send_stream_header.error) - async def _send_chunk(self, chunk: proto_DataStream.Chunk): + async def _send_chunk(self, chunk: proto_DataStream.Chunk) -> None: if self._closed: raise RuntimeError(f"Cannot send chunk after stream is closed: {chunk}") req = proto_ffi.FfiRequest( @@ -212,7 +212,7 @@ async def _send_chunk(self, chunk: proto_DataStream.Chunk): if cb.send_stream_chunk.error: raise ConnectionError(cb.send_stream_chunk.error) - async def _send_trailer(self, trailer: proto_DataStream.Trailer): + async def _send_trailer(self, trailer: proto_DataStream.Trailer) -> None: req = proto_ffi.FfiRequest( send_stream_trailer=proto_room.SendStreamTrailerRequest( trailer=trailer, @@ -233,7 +233,7 @@ async def _send_trailer(self, trailer: proto_DataStream.Trailer): if cb.send_stream_chunk.error: raise ConnectionError(cb.send_stream_trailer.error) - async def aclose(self, *, reason: str = "", attributes: Optional[Dict[str, str]] = None): + async def aclose(self, *, reason: str = "", attributes: Optional[Dict[str, str]] = None) -> None: if self._closed: raise RuntimeError("Stream already closed") self._closed = True @@ -281,7 +281,7 @@ def __init__( ) self._write_lock = asyncio.Lock() - async def write(self, text: str): + async def write(self, text: str) -> None: async with self._write_lock: for chunk in split_utf8(text, STREAM_CHUNK_SIZE): content = chunk @@ -333,7 +333,7 @@ def __init__( ) self._write_lock = asyncio.Lock() - async def write(self, data: bytes): + async def write(self, data: bytes) -> None: async with self._write_lock: chunked_data = [ data[i : i + STREAM_CHUNK_SIZE] for i in range(0, len(data), STREAM_CHUNK_SIZE) diff --git a/livekit-rtc/livekit/rtc/participant.py b/livekit-rtc/livekit/rtc/participant.py index 22b07435..a7b878f4 100644 --- a/livekit-rtc/livekit/rtc/participant.py +++ b/livekit-rtc/livekit/rtc/participant.py @@ -49,6 +49,7 @@ from .rpc import RpcInvocationData from .data_stream import ( TextStreamWriter, + TextStreamInfo, ByteStreamWriter, ByteStreamInfo, STREAM_CHUNK_SIZE, @@ -160,7 +161,7 @@ def __init__( ) -> None: super().__init__(owned_info) self._room_queue = room_queue - self._track_publications: dict[str, LocalTrackPublication] = {} # type: ignore + self._track_publications: dict[str, LocalTrackPublication] = {} self._rpc_handlers: Dict[str, RpcHandler] = {} @property @@ -327,7 +328,7 @@ async def perform_rpc( if cb.perform_rpc.HasField("error"): raise RpcError._from_proto(cb.perform_rpc.error) - return cb.perform_rpc.payload + return cast(str, cb.perform_rpc.payload) def register_rpc_method( self, @@ -587,7 +588,7 @@ async def send_text( topic: str = "", attributes: Optional[Dict[str, str]] = None, reply_to_id: str | None = None, - ): + ) -> TextStreamInfo: total_size = len(text.encode()) writer = await self.stream_text( destination_identities=destination_identities, @@ -743,7 +744,7 @@ def __repr__(self) -> str: class RemoteParticipant(Participant): def __init__(self, owned_info: proto_participant.OwnedParticipant) -> None: super().__init__(owned_info) - self._track_publications: dict[str, RemoteTrackPublication] = {} # type: ignore + self._track_publications: dict[str, RemoteTrackPublication] = {} @property def track_publications(self) -> Mapping[str, RemoteTrackPublication]: diff --git a/livekit-rtc/livekit/rtc/room.py b/livekit-rtc/livekit/rtc/room.py index 3fc56a17..0a9ca204 100644 --- a/livekit-rtc/livekit/rtc/room.py +++ b/livekit-rtc/livekit/rtc/room.py @@ -470,10 +470,10 @@ def on_participant_connected(participant): if options.rtc_config: req.connect.options.rtc_config.ice_transport_type = ( options.rtc_config.ice_transport_type - ) # type: ignore + ) req.connect.options.rtc_config.continual_gathering_policy = ( options.rtc_config.continual_gathering_policy - ) # type: ignore + ) req.connect.options.rtc_config.ice_servers.extend(options.rtc_config.ice_servers) # subscribe before connecting so we don't miss any events @@ -540,25 +540,25 @@ async def get_rtc_stats(self) -> RtcStats: return RtcStats(publisher_stats=publisher_stats, subscriber_stats=subscriber_stats) - def register_byte_stream_handler(self, topic: str, handler: ByteStreamHandler): + def register_byte_stream_handler(self, topic: str, handler: ByteStreamHandler) -> None: existing_handler = self._byte_stream_handlers.get(topic) if existing_handler is None: self._byte_stream_handlers[topic] = handler else: raise ValueError("byte stream handler for topic '%s' already set" % topic) - def unregister_byte_stream_handler(self, topic: str): + def unregister_byte_stream_handler(self, topic: str) -> None: if self._byte_stream_handlers.get(topic): self._byte_stream_handlers.pop(topic) - def register_text_stream_handler(self, topic: str, handler: TextStreamHandler): + def register_text_stream_handler(self, topic: str, handler: TextStreamHandler) -> None: existing_handler = self._text_stream_handlers.get(topic) if existing_handler is None: self._text_stream_handlers[topic] = handler else: raise ValueError("text stream handler for topic '%s' already set" % topic) - def unregister_text_stream_handler(self, topic: str): + def unregister_text_stream_handler(self, topic: str) -> None: if self._text_stream_handlers.get(topic): self._text_stream_handlers.pop(topic) @@ -618,7 +618,7 @@ async def _listen_task(self) -> None: await self._drain_rpc_invocation_tasks() await self._drain_data_stream_tasks() - def _on_rpc_method_invocation(self, rpc_invocation: RpcMethodInvocationEvent): + def _on_rpc_method_invocation(self, rpc_invocation: RpcMethodInvocationEvent) -> None: if self._local_participant is None: return @@ -636,7 +636,7 @@ def _on_rpc_method_invocation(self, rpc_invocation: RpcMethodInvocationEvent): self._rpc_invocation_tasks.add(task) task.add_done_callback(self._rpc_invocation_tasks.discard) - def _on_room_event(self, event: proto_room.RoomEvent): + def _on_room_event(self, event: proto_room.RoomEvent) -> None: which = event.WhichOneof("message") if which == "participant_connected": rparticipant = self._create_remote_participant(event.participant_connected.info) @@ -905,7 +905,7 @@ def _on_room_event(self, event: proto_room.RoomEvent): def _handle_stream_header( self, header: proto_room.DataStream.Header, participant_identity: str - ): + ) -> None: stream_type = header.WhichOneof("content_header") if stream_type == "text_header": text_stream_handler = self._text_stream_handlers.get(header.topic) @@ -935,7 +935,7 @@ def _handle_stream_header( logging.warning("received unknown header type, %s", stream_type) pass - async def _handle_stream_chunk(self, chunk: proto_room.DataStream.Chunk): + async def _handle_stream_chunk(self, chunk: proto_room.DataStream.Chunk) -> None: text_reader = self._text_stream_readers.get(chunk.stream_id) file_reader = self._byte_stream_readers.get(chunk.stream_id) @@ -944,7 +944,7 @@ async def _handle_stream_chunk(self, chunk: proto_room.DataStream.Chunk): elif file_reader: await file_reader._on_chunk_update(chunk) - async def _handle_stream_trailer(self, trailer: proto_room.DataStream.Trailer): + async def _handle_stream_trailer(self, trailer: proto_room.DataStream.Trailer) -> None: text_reader = self._text_stream_readers.get(trailer.stream_id) file_reader = self._byte_stream_readers.get(trailer.stream_id) From 1994d60547970d6be4b9803a0bce500b6ba074bc Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:15:44 +0100 Subject: [PATCH 3/9] fix remaining type errors --- livekit-rtc/livekit/rtc/_ffi_client.py | 10 ++++----- livekit-rtc/livekit/rtc/_utils.py | 10 ++++----- livekit-rtc/livekit/rtc/e2ee.py | 10 ++++----- livekit-rtc/livekit/rtc/jupyter.py | 4 ++-- livekit-rtc/livekit/rtc/media_devices.py | 27 ++++++++++++------------ pyproject.toml | 2 +- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/livekit-rtc/livekit/rtc/_ffi_client.py b/livekit-rtc/livekit/rtc/_ffi_client.py index e7abef81..8b57ba8f 100644 --- a/livekit-rtc/livekit/rtc/_ffi_client.py +++ b/livekit-rtc/livekit/rtc/_ffi_client.py @@ -24,7 +24,7 @@ import platform import atexit import threading -from typing import Generic, List, Optional, TypeVar +from typing import Any, Generic, List, Optional, TypeVar from ._proto import ffi_pb2 as proto_ffi from ._utils import Queue, classproperty @@ -34,7 +34,7 @@ atexit.register(_resource_files.close) -def get_ffi_lib(): +def get_ffi_lib() -> ctypes.CDLL: # allow to override the lib path using an env var libpath = os.environ.get("LIVEKIT_LIB_PATH", "").strip() if libpath: @@ -92,7 +92,7 @@ def __init__(self, handle: int) -> None: self.handle = handle self._disposed = False - def __del__(self): + def __del__(self) -> None: self.dispose() @property @@ -142,7 +142,7 @@ def unsubscribe(self, queue: Queue[T]) -> None: break -@ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t) +@ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t) # type: ignore[untyped-decorator] def ffi_event_callback( data_ptr: ctypes.POINTER(ctypes.c_uint8), # type: ignore data_len: ctypes.c_size_t, @@ -219,7 +219,7 @@ def __init__(self) -> None: ) @atexit.register - def _dispose_lk_ffi(): + def _dispose_lk_ffi() -> None: ffi_lib.livekit_ffi_dispose() @property diff --git a/livekit-rtc/livekit/rtc/_utils.py b/livekit-rtc/livekit/rtc/_utils.py index 2342b21a..bc2cec59 100644 --- a/livekit-rtc/livekit/rtc/_utils.py +++ b/livekit-rtc/livekit/rtc/_utils.py @@ -17,16 +17,16 @@ from collections import deque import ctypes import random -from typing import Callable, Generator, Generic, List, TypeVar +from typing import Any, Callable, Generator, Generic, List, TypeVar logger = logging.getLogger("livekit") class classproperty(object): - def __init__(self, f): - self.f = classmethod(f) + def __init__(self, f: Callable[..., Any]) -> None: + self.f: Any = classmethod(f) - def __get__(self, *a): + def __get__(self, *a: Any) -> Any: return self.f.__get__(*a)() @@ -118,7 +118,7 @@ async def join(self) -> None: _base62_characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -def generate_random_base62(length=12): +def generate_random_base62(length: int = 12) -> str: """ Generate a random base62 encoded string of a specified length. diff --git a/livekit-rtc/livekit/rtc/e2ee.py b/livekit-rtc/livekit/rtc/e2ee.py index 6fe62258..823030f8 100644 --- a/livekit-rtc/livekit/rtc/e2ee.py +++ b/livekit-rtc/livekit/rtc/e2ee.py @@ -13,7 +13,7 @@ # limitations under the License. from dataclasses import dataclass, field -from typing import List, Optional +from typing import List, Optional, cast from ._ffi_client import FfiClient from ._proto import e2ee_pb2 as proto_e2ee @@ -84,7 +84,7 @@ def export_shared_key(self, key_index: int) -> bytes: req.e2ee.get_shared_key.key_index = key_index resp = FfiClient.instance.request(req) key = resp.e2ee.get_shared_key.key - return key + return cast(bytes, key) def ratchet_shared_key(self, key_index: int) -> bytes: """Ratchets the shared encryption key to a new key. @@ -107,7 +107,7 @@ def ratchet_shared_key(self, key_index: int) -> bytes: resp = FfiClient.instance.request(req) new_key = resp.e2ee.ratchet_shared_key.new_key - return new_key + return cast(bytes, new_key) def set_key(self, participant_identity: str, key: bytes, key_index: int) -> None: """Sets the encryption key for a specific participant. @@ -152,7 +152,7 @@ def export_key(self, participant_identity: str, key_index: int) -> bytes: req.e2ee.get_key.key_index = key_index resp = FfiClient.instance.request(req) key = resp.e2ee.get_key.key - return key + return cast(bytes, key) def ratchet_key(self, participant_identity: str, key_index: int) -> bytes: """Ratchets the encryption key for a specific participant to a new key. @@ -176,7 +176,7 @@ def ratchet_key(self, participant_identity: str, key_index: int) -> bytes: resp = FfiClient.instance.request(req) new_key = resp.e2ee.ratchet_key.new_key - return new_key + return cast(bytes, new_key) class FrameCryptor: diff --git a/livekit-rtc/livekit/rtc/jupyter.py b/livekit-rtc/livekit/rtc/jupyter.py index f6a3b689..8a2a2367 100644 --- a/livekit-rtc/livekit/rtc/jupyter.py +++ b/livekit-rtc/livekit/rtc/jupyter.py @@ -50,7 +50,7 @@ def room_html(url: str, token: str, *, width: str, height: str) -> HTML: f'srcdoc="{escaped_content}">' ) - return HTML(html_text) + return HTML(html_text) # type: ignore[no-untyped-call] def display_room(url: str, token: str, *, width: str = "100%", height: str = "110px") -> None: @@ -65,4 +65,4 @@ def display_room(url: str, token: str, *, width: str = "100%", height: str = "11 The rendered HTML will include the provided `url` and `token` in plain text. Avoid using sensitive tokens in public notebooks (e.g., tokens with long expiration times). """ - display(room_html(url, token, width=width, height=height)) + display(room_html(url, token, width=width, height=height)) # type: ignore[no-untyped-call] diff --git a/livekit-rtc/livekit/rtc/media_devices.py b/livekit-rtc/livekit/rtc/media_devices.py index 4958a9de..4567e660 100644 --- a/livekit-rtc/livekit/rtc/media_devices.py +++ b/livekit-rtc/livekit/rtc/media_devices.py @@ -22,7 +22,7 @@ import threading if TYPE_CHECKING: - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd # type: ignore[import-not-found] from . import AudioSource from .audio_frame import AudioFrame @@ -163,7 +163,7 @@ def __init__( output_device: Optional[int] = None, delay_estimator: Optional[_APMDelayEstimator] = None, ) -> None: - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd self._sample_rate = sample_rate self._num_channels = num_channels @@ -303,16 +303,17 @@ async def start(self) -> None: if self._mixer is None: self._mixer = AudioMixer(sample_rate=self._sample_rate, num_channels=self._num_channels) - async def _playback_loop(): + async def _playback_loop() -> None: """Internal playback loop that consumes frames from the mixer.""" self._running = True self._stream.start() try: - async for frame in self._mixer: - if not self._running: - break - # Append raw PCM bytes for callback consumption - self._buffer.extend(frame.data.tobytes()) + if self._mixer is not None: + async for frame in self._mixer: + if not self._running: + break + # Append raw PCM bytes for callback consumption + self._buffer.extend(frame.data.tobytes()) finally: self._running = False try: @@ -402,7 +403,7 @@ def list_input_devices(self) -> list[dict[str, Any]]: Returns a list of dictionaries with the `sounddevice` metadata and an added `index` key corresponding to the device index. """ - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd devices = sd.query_devices() result: list[dict[str, Any]] = [] @@ -413,7 +414,7 @@ def list_input_devices(self) -> list[dict[str, Any]]: def list_output_devices(self) -> list[dict[str, Any]]: """List available output devices with indices.""" - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd devices = sd.query_devices() result: list[dict[str, Any]] = [] @@ -424,14 +425,14 @@ def list_output_devices(self) -> list[dict[str, Any]]: def default_input_device(self) -> Optional[int]: """Return the default input device index (or None).""" - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd dev = sd.default.device return dev[0] if isinstance(dev, (list, tuple)) else None def default_output_device(self) -> Optional[int]: """Return the default output device index (or None).""" - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd dev = sd.default.device return dev[1] if isinstance(dev, (list, tuple)) else None @@ -471,7 +472,7 @@ def open_input( Returns: InputCapture: Holder with `source`, `apm`, and `aclose()`. """ - import sounddevice as sd # type: ignore[import-not-found, import-untyped] + import sounddevice as sd loop = self._loop source = AudioSource(self._in_sr, self._channels, loop=loop) diff --git a/pyproject.toml b/pyproject.toml index ef507d5f..0badeff3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,6 @@ exclude = "(build|setup\\.py|livekit-rtc/rust-sdks.*)" namespace_packages = true explicit_package_bases = true mypy_path = "livekit-protocol:livekit-api:livekit-rtc" -strict = false # TODO re-enable strict checking +strict = true disallow_any_generics = false plugins = ["pydantic.mypy"] From 42e1fc086d09647d8bb58578a9c316217f4cb69b Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:17:42 +0100 Subject: [PATCH 4/9] format --- livekit-rtc/livekit/rtc/data_stream.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/livekit-rtc/livekit/rtc/data_stream.py b/livekit-rtc/livekit/rtc/data_stream.py index 92ce9c24..0e214e4c 100644 --- a/livekit-rtc/livekit/rtc/data_stream.py +++ b/livekit-rtc/livekit/rtc/data_stream.py @@ -233,7 +233,9 @@ async def _send_trailer(self, trailer: proto_DataStream.Trailer) -> None: if cb.send_stream_chunk.error: raise ConnectionError(cb.send_stream_trailer.error) - async def aclose(self, *, reason: str = "", attributes: Optional[Dict[str, str]] = None) -> None: + async def aclose( + self, *, reason: str = "", attributes: Optional[Dict[str, str]] = None + ) -> None: if self._closed: raise RuntimeError("Stream already closed") self._closed = True From 3aaa280e5bf7eae42e901ad68d00001bf2522541 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:21:30 +0100 Subject: [PATCH 5/9] lint --- livekit-rtc/livekit/rtc/_ffi_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livekit-rtc/livekit/rtc/_ffi_client.py b/livekit-rtc/livekit/rtc/_ffi_client.py index 8b57ba8f..49e5201c 100644 --- a/livekit-rtc/livekit/rtc/_ffi_client.py +++ b/livekit-rtc/livekit/rtc/_ffi_client.py @@ -24,7 +24,7 @@ import platform import atexit import threading -from typing import Any, Generic, List, Optional, TypeVar +from typing import Generic, List, Optional, TypeVar from ._proto import ffi_pb2 as proto_ffi from ._utils import Queue, classproperty From 543cdac057011af27c882c28520772024c0583d7 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:31:09 +0100 Subject: [PATCH 6/9] fix test files --- livekit-api/tests/test_access_token.py | 10 ++++++---- livekit-api/tests/test_webhook.py | 6 +++--- livekit-rtc/tests/test_apm.py | 2 +- livekit-rtc/tests/test_resampler.py | 9 +++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/livekit-api/tests/test_access_token.py b/livekit-api/tests/test_access_token.py index 48835ea1..dc892e4e 100644 --- a/livekit-api/tests/test_access_token.py +++ b/livekit-api/tests/test_access_token.py @@ -9,7 +9,7 @@ TEST_API_SECRET = "thiskeyistotallyunsafe" -def test_verify_token(): +def test_verify_token() -> None: grants = VideoGrants(room_join=True, room="test_room") sip = SIPGrants(admin=True) @@ -30,11 +30,12 @@ def test_verify_token(): assert claims.metadata == "test_metadata" assert claims.video == grants assert claims.sip == sip + assert claims.attributes is not None assert claims.attributes["key1"] == "value1" assert claims.attributes["key2"] == "value2" -def test_agent_config(): +def test_agent_config() -> None: token = ( AccessToken(TEST_API_KEY, TEST_API_SECRET) .with_identity("test_identity") @@ -50,6 +51,7 @@ def test_agent_config(): token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) claims = token_verifier.verify(token) # Verify the decoded claims match + assert claims.room_config is not None assert claims.room_config.agents[0].agent_name == "test-agent" # Split token into header.payload.signature @@ -74,7 +76,7 @@ def test_agent_config(): assert payload_json["roomConfig"]["agents"][0]["agentName"] == "test-agent" -def test_verify_token_invalid(): +def test_verify_token_invalid() -> None: token = AccessToken(TEST_API_KEY, TEST_API_SECRET).with_identity("test_identity").to_jwt() token_verifier = TokenVerifier(TEST_API_KEY, "invalid_secret") @@ -86,7 +88,7 @@ def test_verify_token_invalid(): token_verifier.verify(token) -def test_verify_token_expired(): +def test_verify_token_expired() -> None: token = ( AccessToken(TEST_API_KEY, TEST_API_SECRET) .with_identity("test_identity") diff --git a/livekit-api/tests/test_webhook.py b/livekit-api/tests/test_webhook.py index fc8828e5..002f9616 100644 --- a/livekit-api/tests/test_webhook.py +++ b/livekit-api/tests/test_webhook.py @@ -45,7 +45,7 @@ """ -def test_webhook_receiver(): +def test_webhook_receiver() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) @@ -56,7 +56,7 @@ def test_webhook_receiver(): receiver.receive(TEST_EVENT, jwt) -def test_bad_hash(): +def test_bad_hash() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) @@ -68,7 +68,7 @@ def test_bad_hash(): receiver.receive(TEST_EVENT, jwt) -def test_invalid_body(): +def test_invalid_body() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) diff --git a/livekit-rtc/tests/test_apm.py b/livekit-rtc/tests/test_apm.py index da3d7ab1..5d24aca8 100644 --- a/livekit-rtc/tests/test_apm.py +++ b/livekit-rtc/tests/test_apm.py @@ -5,7 +5,7 @@ from livekit.rtc import AudioProcessingModule, AudioFrame -def test_audio_processing(): +def test_audio_processing() -> None: sample_rate = 48000 num_channels = 1 frames_per_chunk = sample_rate // 100 diff --git a/livekit-rtc/tests/test_resampler.py b/livekit-rtc/tests/test_resampler.py index 83108de3..3930d166 100644 --- a/livekit-rtc/tests/test_resampler.py +++ b/livekit-rtc/tests/test_resampler.py @@ -1,10 +1,11 @@ -from livekit.rtc import AudioResampler, AudioResamplerQuality +from livekit.rtc import AudioResampler, AudioResamplerQuality, AudioFrame import time import wave import os +from typing import List -def test_audio_resampler(): +def test_audio_resampler() -> None: current_dir = os.path.dirname(os.path.abspath(__file__)) wav_file_path = os.path.join(current_dir, "test_audio.wav") @@ -27,9 +28,9 @@ def test_audio_resampler(): ] for quality in qualities: - total_time = 0 + total_time = 0.0 nb_runs = 20 - output_frames = [] + output_frames: List[AudioFrame] = [] for i in range(nb_runs): output_frames = [] resampler = AudioResampler(44100, 8000, quality=quality) From 3ad2f65925477ccd61732519530c84dc248404cb Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:37:10 +0100 Subject: [PATCH 7/9] fix remaining test issues --- livekit-rtc/tests/test_e2e.py | 74 ++++++++++++++++--------------- livekit-rtc/tests/test_emitter.py | 28 ++++++------ 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/livekit-rtc/tests/test_e2e.py b/livekit-rtc/tests/test_e2e.py index c248cd1d..e060cf9b 100644 --- a/livekit-rtc/tests/test_e2e.py +++ b/livekit-rtc/tests/test_e2e.py @@ -21,12 +21,12 @@ import asyncio import os import uuid -from typing import Callable, TypeVar +from typing import Any, Callable, Dict, List, TypeVar import numpy as np -import pytest +import pytest # type: ignore[import-not-found] from livekit import rtc, api -from livekit.rtc.utils import sine_wave_generator +from livekit.rtc.utils import sine_wave_generator # type: ignore[attr-defined] SAMPLE_RATE = 48000 @@ -55,7 +55,7 @@ async def assert_eventually( raise AssertionError(f"{message} (last result: {last_result})") -def skip_if_no_credentials(): +def skip_if_no_credentials() -> Any: required_vars = ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"] missing = [var for var in required_vars if not os.getenv(var)] return pytest.mark.skipif( @@ -82,12 +82,13 @@ def unique_room_name(base: str) -> str: return f"{base}-{uuid.uuid4().hex[:8]}" -@pytest.mark.asyncio -@skip_if_no_credentials() -async def test_publish_track(): +@pytest.mark.asyncio # type: ignore[misc] +@skip_if_no_credentials() # type: ignore[misc] +async def test_publish_track() -> None: """Test that a published track can be subscribed by another participant""" room_name = unique_room_name("test-publish-track") url = os.getenv("LIVEKIT_URL") + assert url is not None publisher_room = rtc.Room() subscriber_room = rtc.Room() @@ -102,7 +103,7 @@ async def test_publish_track(): @subscriber_room.on("track_published") def on_track_published( publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): + ) -> None: track_published_event.set() @subscriber_room.on("track_subscribed") @@ -110,7 +111,7 @@ def on_track_subscribed( track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant, - ): + ) -> None: nonlocal subscribed_track if track.kind == rtc.TrackKind.KIND_AUDIO: subscribed_track = track @@ -140,12 +141,13 @@ def on_track_subscribed( await subscriber_room.disconnect() -@pytest.mark.asyncio -@skip_if_no_credentials() -async def test_audio_stream_subscribe(): +@pytest.mark.asyncio # type: ignore[misc] +@skip_if_no_credentials() # type: ignore[misc] +async def test_audio_stream_subscribe() -> None: """Test that published audio can be consumed and has similar energy levels""" room_name = unique_room_name("test-audio-stream") url = os.getenv("LIVEKIT_URL") + assert url is not None publisher_room = rtc.Room() subscriber_room = rtc.Room() @@ -161,7 +163,7 @@ def on_track_subscribed( track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant, - ): + ) -> None: nonlocal subscribed_track if track.kind == rtc.TrackKind.KIND_AUDIO: subscribed_track = track @@ -178,9 +180,9 @@ def on_track_subscribed( await publisher_room.local_participant.publish_track(track, options) target_duration = 5.0 - published_energy = [] + published_energy: List[Any] = [] - async def publish_audio(): + async def publish_audio() -> None: async for frame in sine_wave_generator(440, target_duration, SAMPLE_RATE): data = np.frombuffer(frame.data.tobytes(), dtype=np.int16) energy = np.mean(np.abs(data.astype(np.float32))) @@ -235,12 +237,13 @@ async def publish_audio(): await subscriber_room.disconnect() -@pytest.mark.asyncio -@skip_if_no_credentials() -async def test_room_lifecycle_events(): +@pytest.mark.asyncio # type: ignore[misc] +@skip_if_no_credentials() # type: ignore[misc] +async def test_room_lifecycle_events() -> None: """Test that room lifecycle and track events are fired properly""" room_name = unique_room_name("test-lifecycle-events") url = os.getenv("LIVEKIT_URL") + assert url is not None room1 = rtc.Room() room2 = rtc.Room() @@ -248,7 +251,7 @@ async def test_room_lifecycle_events(): token1 = create_token("participant-1", room_name) token2 = create_token("participant-2", room_name) - events = { + events: Dict[str, List[str]] = { "disconnected": [], "participant_connected": [], "participant_disconnected": [], @@ -263,37 +266,37 @@ async def test_room_lifecycle_events(): } @room1.on("disconnected") - def on_room1_disconnected(reason): + def on_room1_disconnected(reason: Any) -> None: events["disconnected"].append("room1") @room1.on("participant_connected") - def on_room1_participant_connected(participant: rtc.RemoteParticipant): + def on_room1_participant_connected(participant: rtc.RemoteParticipant) -> None: events["participant_connected"].append(f"room1-{participant.identity}") @room1.on("participant_disconnected") - def on_room1_participant_disconnected(participant: rtc.RemoteParticipant): + def on_room1_participant_disconnected(participant: rtc.RemoteParticipant) -> None: events["participant_disconnected"].append(f"room1-{participant.identity}") @room1.on("local_track_published") - def on_room1_local_track_published(publication: rtc.LocalTrackPublication, track): + def on_room1_local_track_published(publication: rtc.LocalTrackPublication, track: Any) -> None: events["local_track_published"].append(f"room1-{publication.sid}") @room1.on("local_track_unpublished") - def on_room1_local_track_unpublished(publication: rtc.LocalTrackPublication): + def on_room1_local_track_unpublished(publication: rtc.LocalTrackPublication) -> None: events["local_track_unpublished"].append(f"room1-{publication.sid}") @room1.on("room_updated") - def on_room1_room_updated(): + def on_room1_room_updated() -> None: events["room_updated"].append("room1") @room1.on("connection_state_changed") - def on_room1_connection_state_changed(state: rtc.ConnectionState): + def on_room1_connection_state_changed(state: rtc.ConnectionState) -> None: events["connection_state_changed"].append(f"room1-{state}") @room2.on("track_published") def on_room2_track_published( publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): + ) -> None: events["track_published"].append(f"room2-{publication.sid}") @room2.on("track_subscribed") @@ -301,13 +304,13 @@ def on_room2_track_subscribed( track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant, - ): + ) -> None: events["track_subscribed"].append(f"room2-{publication.sid}") @room2.on("track_unpublished") def on_room2_track_unpublished( publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): + ) -> None: events["track_unpublished"].append(f"room2-{publication.sid}") try: @@ -388,20 +391,21 @@ def on_room2_track_unpublished( await room2.disconnect() -@pytest.mark.asyncio -@skip_if_no_credentials() -async def test_connection_state_transitions(): +@pytest.mark.asyncio # type: ignore[misc] +@skip_if_no_credentials() # type: ignore[misc] +async def test_connection_state_transitions() -> None: """Test that connection state transitions work correctly""" room_name = unique_room_name("test-connection-state") url = os.getenv("LIVEKIT_URL") + assert url is not None room = rtc.Room() token = create_token("state-test", room_name) - states = [] + states: List[rtc.ConnectionState] = [] @room.on("connection_state_changed") - def on_state_changed(state: rtc.ConnectionState): + def on_state_changed(state: rtc.ConnectionState) -> None: states.append(state) try: @@ -414,7 +418,7 @@ def on_state_changed(state: rtc.ConnectionState): message="Room did not reach CONN_CONNECTED state", ) await assert_eventually( - lambda: rtc.ConnectionState.CONN_CONNECTED in states, + lambda: rtc.ConnectionState.CONN_CONNECTED in states, # type: ignore[comparison-overlap] message="CONN_CONNECTED state not in state change events", ) diff --git a/livekit-rtc/tests/test_emitter.py b/livekit-rtc/tests/test_emitter.py index 830feb3a..dd62f4bb 100644 --- a/livekit-rtc/tests/test_emitter.py +++ b/livekit-rtc/tests/test_emitter.py @@ -1,16 +1,16 @@ from livekit.rtc import EventEmitter -from typing import Literal -import pytest +from typing import Any, Literal +import pytest # type: ignore[import-not-found] -def test_events(): +def test_events() -> None: EventTypes = Literal["connected", "reconnected", "disconnected"] emitter = EventEmitter[EventTypes]() connected_calls = [] @emitter.once("connected") - def on_connected(): + def on_connected() -> None: connected_calls.append(True) emitter.emit("connected") @@ -22,7 +22,7 @@ def on_connected(): reconnected_calls = [] @emitter.on("reconnected") - def on_reconnected(): + def on_reconnected() -> None: reconnected_calls.append(True) emitter.emit("reconnected") @@ -32,11 +32,11 @@ def on_reconnected(): disconnected_calls = [] @emitter.on("disconnected") - def on_disconnected(): + def on_disconnected() -> None: disconnected_calls.append(True) @emitter.on("disconnected") - def on_disconnected_another(): + def on_disconnected_another() -> None: disconnected_calls.append(True) emitter.emit("disconnected") @@ -46,7 +46,7 @@ def on_disconnected_another(): assert len(disconnected_calls) == 5 -def test_args(): +def test_args() -> None: EventTypes = Literal["whatever"] emitter = EventEmitter[EventTypes]() @@ -54,7 +54,7 @@ def test_args(): calls = [] @emitter.on("whatever") - def on_whatever(first, second, third): + def on_whatever(first: Any, second: Any, third: Any) -> None: calls.append((first, second, third)) emitter.emit("whatever", 1, 2, 3) @@ -66,7 +66,7 @@ def on_whatever(first, second, third): emitter.emit("whatever", 1, 2) -def test_varargs(): +def test_varargs() -> None: EventTypes = Literal["whatever"] emitter = EventEmitter[EventTypes]() @@ -74,7 +74,7 @@ def test_varargs(): calls = [] @emitter.on("whatever") - def on_whatever_varargs(*args): + def on_whatever_varargs(*args: Any) -> None: calls.append(args) emitter.emit("whatever", 1, 2, 3, 4, 5) @@ -83,7 +83,7 @@ def on_whatever_varargs(*args): assert calls == [(1, 2, 3, 4, 5), (1, 2)] -def test_throw(): +def test_throw() -> None: EventTypes = Literal["error"] emitter = EventEmitter[EventTypes]() @@ -91,12 +91,12 @@ def test_throw(): calls = [] @emitter.on("error") - def on_error(): + def on_error() -> None: calls.append(True) raise ValueError("error") @emitter.on("error") - def on_error_another(): + def on_error_another() -> None: calls.append(True) emitter.emit("error") From d078ec6ce46bb229b423397a234ec03794845648 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:41:16 +0100 Subject: [PATCH 8/9] fix --- livekit-api/tests/test_access_token.py | 2 +- livekit-api/tests/test_webhook.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/livekit-api/tests/test_access_token.py b/livekit-api/tests/test_access_token.py index dc892e4e..85aeb6ce 100644 --- a/livekit-api/tests/test_access_token.py +++ b/livekit-api/tests/test_access_token.py @@ -1,6 +1,6 @@ import datetime -import pytest # type: ignore +import pytest from livekit.api import AccessToken, TokenVerifier, VideoGrants, SIPGrants from livekit.protocol.room import RoomConfiguration from livekit.protocol.agent_dispatch import RoomAgentDispatch diff --git a/livekit-api/tests/test_webhook.py b/livekit-api/tests/test_webhook.py index 002f9616..3937cdba 100644 --- a/livekit-api/tests/test_webhook.py +++ b/livekit-api/tests/test_webhook.py @@ -1,7 +1,7 @@ import base64 import hashlib -import pytest # type: ignore +import pytest from livekit.api import AccessToken, TokenVerifier, WebhookReceiver TEST_API_KEY = "myapikey" From f802dccd51a77730491a676bf4a2c2234d58360d Mon Sep 17 00:00:00 2001 From: lukasIO Date: Fri, 19 Dec 2025 15:44:47 +0100 Subject: [PATCH 9/9] one more test fix --- livekit-rtc/tests/test_e2e.py | 20 ++++++++++---------- livekit-rtc/tests/test_emitter.py | 2 +- makefile | 6 +----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/livekit-rtc/tests/test_e2e.py b/livekit-rtc/tests/test_e2e.py index e060cf9b..583a836d 100644 --- a/livekit-rtc/tests/test_e2e.py +++ b/livekit-rtc/tests/test_e2e.py @@ -23,10 +23,10 @@ import uuid from typing import Any, Callable, Dict, List, TypeVar import numpy as np -import pytest # type: ignore[import-not-found] +import pytest from livekit import rtc, api -from livekit.rtc.utils import sine_wave_generator # type: ignore[attr-defined] +from livekit.rtc.utils import sine_wave_generator SAMPLE_RATE = 48000 @@ -82,8 +82,8 @@ def unique_room_name(base: str) -> str: return f"{base}-{uuid.uuid4().hex[:8]}" -@pytest.mark.asyncio # type: ignore[misc] -@skip_if_no_credentials() # type: ignore[misc] +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] async def test_publish_track() -> None: """Test that a published track can be subscribed by another participant""" room_name = unique_room_name("test-publish-track") @@ -141,8 +141,8 @@ def on_track_subscribed( await subscriber_room.disconnect() -@pytest.mark.asyncio # type: ignore[misc] -@skip_if_no_credentials() # type: ignore[misc] +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] async def test_audio_stream_subscribe() -> None: """Test that published audio can be consumed and has similar energy levels""" room_name = unique_room_name("test-audio-stream") @@ -237,8 +237,8 @@ async def publish_audio() -> None: await subscriber_room.disconnect() -@pytest.mark.asyncio # type: ignore[misc] -@skip_if_no_credentials() # type: ignore[misc] +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] async def test_room_lifecycle_events() -> None: """Test that room lifecycle and track events are fired properly""" room_name = unique_room_name("test-lifecycle-events") @@ -391,8 +391,8 @@ def on_room2_track_unpublished( await room2.disconnect() -@pytest.mark.asyncio # type: ignore[misc] -@skip_if_no_credentials() # type: ignore[misc] +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] async def test_connection_state_transitions() -> None: """Test that connection state transitions work correctly""" room_name = unique_room_name("test-connection-state") diff --git a/livekit-rtc/tests/test_emitter.py b/livekit-rtc/tests/test_emitter.py index dd62f4bb..b5a1bf59 100644 --- a/livekit-rtc/tests/test_emitter.py +++ b/livekit-rtc/tests/test_emitter.py @@ -1,6 +1,6 @@ from livekit.rtc import EventEmitter from typing import Any, Literal -import pytest # type: ignore[import-not-found] +import pytest def test_events() -> None: diff --git a/makefile b/makefile index f58b7f88..5b9472c6 100644 --- a/makefile +++ b/makefile @@ -70,11 +70,7 @@ lint-fix: ## Run ruff linter and fix issues automatically type-check: ## Run mypy type checker @echo "$(BOLD)$(CYAN)Running type checker...$(RESET)" - @uv pip install pip 2>/dev/null || true - @if uv run mypy --install-types --non-interactive \ - -p livekit.rtc \ - -p livekit.api \ - -p livekit.protocol; then \ + @if uv run mypy livekit-protocol livekit-api livekit-rtc; then \ echo "$(BOLD)$(GREEN)✓ Type checking passed$(RESET)"; \ else \ echo "$(BOLD)$(RED)✗ Type checking failed$(RESET)"; \