Increase ruff coverage (#23644)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run

* Pin ruff

* Add python upgrade fixes

This enables python upgrade checks in ruff to look for deprecated types and patterns. This namely fixes:
- usage of deprecated `Typing` which is now built in
- some specific exceptions which are caught and have new aliases

Some specific UP checks were also ignored as they are stylistic / unimportant and likely to cause bugs

* Remove async blocking calls

Use asyncio.to_thread on two remaining blocking calls to fix hanging event thread loop. Enable this specific rule to block it in the future.

* Use proper logging mechanism

* Correctly format logs

* Raise with context

When raising an exception include the from context to improve debugging

* Cleanup
This commit is contained in:
Nicolas Mowen
2026-07-06 12:28:02 -05:00
committed by GitHub
parent 455b8687e8
commit 4ee12e6237
169 changed files with 1053 additions and 1150 deletions
+6 -7
View File
@@ -9,7 +9,6 @@ from abc import ABC, abstractmethod
from asyncio.exceptions import TimeoutError
from logging.handlers import QueueHandler
from types import FrameType
from typing import Optional
import frigate.log
@@ -22,13 +21,13 @@ DEFAULT_STOP_TIMEOUT = 10 # seconds
class BaseServiceProcess(Service, ABC):
"""A Service the manages a multiprocessing.Process."""
_process: Optional[mp.Process]
_process: mp.Process | None
def __init__(
self,
*,
name: Optional[str] = None,
manager: Optional[ServiceManager] = None,
name: str | None = None,
manager: ServiceManager | None = None,
) -> None:
super().__init__(name=name, manager=manager)
@@ -55,7 +54,7 @@ class BaseServiceProcess(Service, ABC):
self,
*,
force: bool = False,
timeout: Optional[float] = None,
timeout: float | None = None,
) -> None:
if timeout is None:
timeout = DEFAULT_STOP_TIMEOUT
@@ -85,7 +84,7 @@ class BaseServiceProcess(Service, ABC):
self.manager.logger.info(f"{self.name} stopped")
@property
def pid(self) -> Optional[int]:
def pid(self) -> int | None:
return self._process.pid if self._process else None
def _run(self) -> None:
@@ -143,7 +142,7 @@ class ServiceProcess(BaseServiceProcess):
faulthandler.enable()
def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None:
def receiveSignal(signalNumber: int, frame: FrameType | None) -> None:
# Get the stop_event through the dict to bypass lazy initialization.
stop_event = self.__dict__.get("stop_event")
if stop_event is not None:
@@ -7,7 +7,7 @@ import threading
from multiprocessing.connection import Connection
from multiprocessing.connection import wait as mp_wait
from socket import socket
from typing import Any, Optional, Union
from typing import Any
logger = logging.getLogger(__name__)
@@ -118,10 +118,10 @@ class MultiprocessingWaiter(threading.Thread):
waiter_lock = threading.Lock()
waiter_thread: Optional[MultiprocessingWaiter] = None
waiter_thread: MultiprocessingWaiter | None = None
async def wait(object: Union[mp.Process, Connection, socket]) -> None:
async def wait(object: mp.Process | Connection | socket) -> None:
"""Wait for the supplied object to be ready.
Under the hood, this uses multiprocessing.connection.wait() and a background thread manage the
@@ -129,7 +129,7 @@ async def wait(object: Union[mp.Process, Connection, socket]) -> None:
"""
global waiter_thread, waiter_lock
sentinel: Union[Connection, socket, int]
sentinel: Connection | socket | int
if isinstance(object, mp.Process):
sentinel = object.sentinel
elif isinstance(object, Connection) or isinstance(object, socket):
+22 -23
View File
@@ -5,12 +5,11 @@ import atexit
import logging
import threading
from abc import ABC, abstractmethod
from collections.abc import Coroutine
from contextvars import ContextVar
from dataclasses import dataclass
from functools import partial
from typing import Coroutine, Optional, Union, cast
from typing_extensions import Self
from typing import Self, cast
class Service(ABC):
@@ -19,8 +18,8 @@ class Service(ABC):
def __init__(
self,
*,
name: Optional[str] = None,
manager: Optional[ServiceManager] = None,
name: str | None = None,
manager: ServiceManager | None = None,
):
if name:
self.__dict__["name"] = name
@@ -42,13 +41,13 @@ class Service(ABC):
try:
return self.__manager
except AttributeError:
raise RuntimeError("Cannot access associated service manager")
raise RuntimeError("Cannot access associated service manager") from None
def start(
self,
*,
wait: bool = False,
wait_timeout: Optional[float] = None,
wait_timeout: float | None = None,
) -> Self:
"""Start this service.
@@ -70,9 +69,9 @@ class Service(ABC):
self,
*,
force: bool = False,
timeout: Optional[float] = None,
timeout: float | None = None,
wait: bool = False,
wait_timeout: Optional[float] = None,
wait_timeout: float | None = None,
) -> Self:
"""Stop this service.
@@ -97,9 +96,9 @@ class Service(ABC):
self,
*,
force: bool = False,
stop_timeout: Optional[float] = None,
stop_timeout: float | None = None,
wait: bool = False,
wait_timeout: Optional[float] = None,
wait_timeout: float | None = None,
) -> Self:
"""Restart this service.
@@ -129,7 +128,7 @@ class Service(ABC):
self,
*,
force: bool = False,
timeout: Optional[float] = None,
timeout: float | None = None,
) -> None:
pass
@@ -137,14 +136,14 @@ class Service(ABC):
self,
*,
force: bool = False,
stop_timeout: Optional[float] = None,
stop_timeout: float | None = None,
) -> None:
await self.on_stop(force=force, timeout=stop_timeout)
await self.on_start()
default_service_manager_lock = threading.Lock()
default_service_manager: Optional[ServiceManager] = None
default_service_manager: ServiceManager | None = None
current_service_manager: ContextVar[ServiceManager] = ContextVar(
"current_service_manager"
@@ -162,8 +161,8 @@ class Command:
"""
coro: Coroutine
lock: Optional[asyncio.Lock] = None
done: Optional[threading.Event] = None
lock: asyncio.Lock | None = None
done: threading.Event | None = None
class ServiceManager:
@@ -189,7 +188,7 @@ class ServiceManager:
_services_lock: threading.Lock
# Commands will be queued with associated event loop. Queueing `None` signals shutdown.
_command_queue: asyncio.Queue[Union[Command, None]]
_command_queue: asyncio.Queue[Command | None]
_event_loop: asyncio.AbstractEventLoop
# The pending command counter is used to ensure all commands have been queued before shutdown.
@@ -204,7 +203,7 @@ class ServiceManager:
# Will be acquired to ensure the shutdown sentinel is sent only once. Never released.
_shutdown_lock: threading.Lock
def __init__(self, *, name: Optional[str] = None):
def __init__(self, *, name: str | None = None):
self._name = name if name is not None else (__package__ or __name__)
self._logger = logging.getLogger(self.name)
@@ -276,8 +275,8 @@ class ServiceManager:
coro: Coroutine,
*,
wait: bool = False,
wait_timeout: Optional[float] = None,
lock: Optional[asyncio.Lock] = None,
wait_timeout: float | None = None,
lock: asyncio.Lock | None = None,
) -> None:
"""Run an async task in the service manager thread.
@@ -299,7 +298,7 @@ class ServiceManager:
cmd.done.wait(timeout=wait_timeout)
def shutdown(
self, *, wait: bool = False, wait_timeout: Optional[float] = None
self, *, wait: bool = False, wait_timeout: float | None = None
) -> None:
"""Shutdown the service manager thread.
@@ -321,7 +320,7 @@ class ServiceManager:
if not self._manager_thread.is_alive():
raise RuntimeError(f"ServiceManager {self.name} is not running")
def _send_command(self, command: Union[Command, None]) -> None:
def _send_command(self, command: Command | None) -> None:
self._ensure_running()
async def queue_command() -> None:
@@ -336,7 +335,7 @@ class ServiceManager:
self._ensure_running()
with self._services_lock:
name_conflict: Optional[Service] = next(
name_conflict: Service | None = next(
(
existing
for name, existing in self._services.items()