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
+24 -24
View File
@@ -7,8 +7,8 @@ import string
import time
import zipfile
from collections import deque
from collections.abc import Iterator
from pathlib import Path
from typing import Iterator, List, Optional
import psutil
from fastapi import APIRouter, Depends, Query, Request
@@ -89,7 +89,7 @@ def _generate_export_id(camera_name: str) -> str:
def _create_export_case_record(
name: str,
description: Optional[str],
description: str | None,
) -> ExportCase:
now = datetime.datetime.fromtimestamp(time.time())
return ExportCase.create(
@@ -101,7 +101,7 @@ def _create_export_case_record(
)
def _validate_camera_name(request: Request, camera_name: str) -> Optional[JSONResponse]:
def _validate_camera_name(request: Request, camera_name: str) -> JSONResponse | None:
if camera_name and request.app.frigate_config.cameras.get(camera_name):
return None
@@ -111,7 +111,7 @@ def _validate_camera_name(request: Request, camera_name: str) -> Optional[JSONRe
)
def _validate_export_case(export_case_id: Optional[str]) -> Optional[JSONResponse]:
def _validate_export_case(export_case_id: str | None) -> JSONResponse | None:
if export_case_id is None:
return None
@@ -127,8 +127,8 @@ def _validate_export_case(export_case_id: Optional[str]) -> Optional[JSONRespons
def _sanitize_existing_image(
image_path: Optional[str],
) -> tuple[Optional[str], Optional[JSONResponse]]:
image_path: str | None,
) -> tuple[str | None, JSONResponse | None]:
# sanitize_filepath normalizes "\" to "/" but leaves ".." intact, so a path
# like "clips\..\..\etc/passwd" passes the CLIPS_DIR prefix check yet still
# escapes the directory once resolved. A valid snapshot path never uses "..".
@@ -154,7 +154,7 @@ def _validate_export_source(
start_time: float,
end_time: float,
playback_source: PlaybackSourceEnum,
) -> Optional[str]:
) -> str | None:
if playback_source == PlaybackSourceEnum.recordings:
recordings_count = (
Recordings.select()
@@ -257,14 +257,14 @@ def _build_export_job(
camera_name: str,
start_time: float,
end_time: float,
friendly_name: Optional[str],
existing_image: Optional[str],
friendly_name: str | None,
existing_image: str | None,
playback_source: PlaybackSourceEnum,
export_case_id: Optional[str],
ffmpeg_input_args: Optional[str] = None,
ffmpeg_output_args: Optional[str] = None,
export_case_id: str | None,
ffmpeg_input_args: str | None = None,
ffmpeg_output_args: str | None = None,
cpu_fallback: bool = False,
chapters: Optional[ChaptersEnum] = None,
chapters: ChaptersEnum | None = None,
) -> ExportJob:
return ExportJob(
id=_generate_export_id(camera_name),
@@ -302,11 +302,11 @@ def _export_case_to_dict(case: ExportCase) -> dict[str, object]:
Returns a list of exports ordered by date (most recent first).""",
)
def get_exports(
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
export_case_id: Optional[str] = None,
cameras: Optional[str] = Query(default="all"),
start_date: Optional[float] = None,
end_date: Optional[float] = None,
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
export_case_id: str | None = None,
cameras: str | None = Query(default="all"),
start_date: float | None = None,
end_date: float | None = None,
):
query = Export.select().where(Export.camera << allowed_cameras)
@@ -422,7 +422,7 @@ def _unique_archive_name(export: Export, used: set[str]) -> str:
return candidate
def _stream_case_archive(exports: List[Export]) -> Iterator[bytes]:
def _stream_case_archive(exports: list[Export]) -> Iterator[bytes]:
"""Yield bytes of a zip archive built from the given exports' mp4 files."""
buffer = _StreamingZipBuffer()
used_names: set[str] = set()
@@ -466,7 +466,7 @@ def _stream_case_archive(exports: List[Export]) -> Iterator[bytes]:
)
def download_export_case(
case_id: str,
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
):
try:
case = ExportCase.get(ExportCase.id == case_id)
@@ -580,7 +580,7 @@ def delete_export_case(case_id: str, request: Request, delete_exports: bool = Fa
)
def get_active_export_jobs(
request: Request,
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
):
jobs = list_active_export_jobs(request.app.frigate_config)
return JSONResponse(
@@ -622,7 +622,7 @@ async def get_export_job_status(export_id: str, request: Request):
def export_recordings_batch(
request: Request,
body: BatchExportBody,
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
current_user: dict = Depends(get_current_user),
):
if isinstance(current_user, JSONResponse):
@@ -662,7 +662,7 @@ def export_recordings_batch(
# Sanitize each item's image_path up front. A bad path in any item
# kills the whole request, consistent with single-export behavior.
sanitized_images: list[Optional[str]] = []
sanitized_images: list[str | None] = []
for item in body.items:
existing_image, image_validation_error = _sanitize_existing_image(
item.image_path
@@ -713,7 +713,7 @@ def export_recordings_batch(
export_case_id = export_case.id
export_ids: list[str] = []
results: list[dict[str, Optional[str] | bool | int]] = []
results: list[dict[str, str | None | bool | int]] = []
for index, item in enumerate(body.items):
if index in item_errors:
results.append(