refactor(types): PEP-604 union sweep + UP007/UP045 enforcement

ruff --select UP007,UP045 --fix converted ~1760 sites across the
backend: `Optional[T]` → `T | None`, `Union[X, Y]` → `X | Y`. The
remaining module-level alias targets that ruff conservatively skips
(BindableFloatInput, ColorList, DeviceConfig) were converted by hand
earlier in the pass. black -formatted the result so the wider unions
fit cleanly under the 100-char line budget.

pyproject.toml now sets [tool.ruff.lint] extend-select = ["UP007",
"UP045"] so future legacy imports fire CI on every push. The
pre-commit ruff hook was bumped from v0.8.0 -> v0.15.12 to recognise
UP045 (split off from UP007 in v0.13).
This commit is contained in:
2026-05-23 01:21:44 +03:00
parent ea7ee88490
commit 888f8fd16e
240 changed files with 2102 additions and 2215 deletions
@@ -16,7 +16,6 @@ from ledgrab.storage.output_target_store import OutputTargetStore
from ledgrab.core.processing.processor_manager import ProcessorManager
from ledgrab.api import dependencies as deps
# ---------------------------------------------------------------------------
# App + fixtures (isolated from the real main app)
# ---------------------------------------------------------------------------
@@ -16,7 +16,6 @@ from ledgrab.core.game_integration.events import GameEvent
from ledgrab.storage.game_integration_store import GameIntegrationStore
from ledgrab.api import dependencies as deps
# ---------------------------------------------------------------------------
# Test adapter for ingestion tests
# ---------------------------------------------------------------------------
@@ -19,7 +19,6 @@ from ledgrab.storage.ha_light_output_target import HALightOutputTarget
from ledgrab.storage.wled_output_target import WledOutputTarget
from ledgrab.storage.z2m_light_output_target import Z2MLightOutputTarget
EXPECTED_TARGET_CLASSES = {WledOutputTarget, HALightOutputTarget, Z2MLightOutputTarget}
@@ -6,7 +6,6 @@ import pytest
from ledgrab.api.routes.webhooks import _check_rate_limit, _rate_hits
# ---------------------------------------------------------------------------
# Rate limiter unit tests (pure function, no HTTP)
# ---------------------------------------------------------------------------
-1
View File
@@ -58,7 +58,6 @@ from ledgrab.storage.automation import Automation # noqa: E402
from ledgrab.storage.automation_store import AutomationStore # noqa: E402
from ledgrab.storage.value_source_store import ValueSourceStore # noqa: E402
# ---------------------------------------------------------------------------
# Directory / path fixtures
# ---------------------------------------------------------------------------
@@ -9,7 +9,6 @@ from ledgrab.core.capture.edge_interpolation import (
fallback_edge_to_leds,
)
# ---------------------------------------------------------------------------
# average_edge_to_leds
# ---------------------------------------------------------------------------
@@ -17,7 +17,6 @@ from ledgrab.core.processing.effect_stream import (
_effect_renderer,
)
EXPECTED_EFFECTS = frozenset(
{
"fire",
@@ -21,7 +21,6 @@ from ledgrab.core.processing.metric_readers import (
get_spec,
)
EXPECTED_METRICS = {
"cpu_load",
"ram_usage",
-1
View File
@@ -13,7 +13,6 @@ import ledgrab.core.audio.filters # noqa: F401
from ledgrab.core.filters.filter_instance import FilterInstance
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
@@ -17,7 +17,6 @@ from ledgrab.storage.automation import (
)
from ledgrab.storage.automation_store import AutomationStore
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@@ -27,7 +27,6 @@ from ledgrab.storage.automation import (
WebhookRule,
)
EXPECTED_RULE_TYPES = {
StartupRule,
ApplicationRule,
-1
View File
@@ -4,7 +4,6 @@ import pytest
from ledgrab.core.game_integration.adapters.cs2_adapter import CS2Adapter
# ── Realistic CS2 GSI payload samples ────────────────────────────────────
-1
View File
@@ -14,7 +14,6 @@ from ledgrab.storage.color_strip_source import (
GameEventColorStripSource,
)
# ── Helpers ──────────────────────────────────────────────────────────
@@ -13,7 +13,6 @@ from ledgrab.storage.value_source import (
ValueSource,
)
# ---------------------------------------------------------------------------
# GameEventValueSource model tests (Task 5)
# ---------------------------------------------------------------------------
@@ -2,7 +2,7 @@
import asyncio
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Tuple
import numpy as np
import pytest
@@ -12,7 +12,6 @@ from ledgrab.core.processing.target_processor import TargetContext
from ledgrab.storage.bindable import BindableFloat
from ledgrab.storage.ha_light_output_target import HALightMapping
# ---------------------------------------------------------------------------
# Test doubles
# ---------------------------------------------------------------------------
@@ -115,9 +114,9 @@ class _FakeCSSManager:
def _make_ctx(
*,
ha_manager: Optional[_FakeHAManager] = None,
css_manager: Optional[_FakeCSSManager] = None,
vs_manager: Optional[_FakeVSManager] = None,
ha_manager: _FakeHAManager | None = None,
css_manager: _FakeCSSManager | None = None,
vs_manager: _FakeVSManager | None = None,
) -> TargetContext:
return TargetContext(
live_stream_manager=None, # type: ignore[arg-type]
@@ -11,7 +11,6 @@ from ledgrab.core.game_integration.mapping_adapter import (
validate_adapter_yaml,
)
# ── YAML validation tests ───────────────────────────────────────────────
+9 -5
View File
@@ -8,8 +8,6 @@ helpers to make unauthenticated requests by temporarily removing the header.
"""
def _unauth_get(client, url):
"""Make a GET request without the Authorization header."""
saved = client.headers.pop("Authorization", None)
@@ -54,7 +52,9 @@ class TestAuthEnforcement:
def test_request_with_wrong_key_returns_401(self, client):
"""Protected endpoint with an incorrect API key returns 401."""
resp = _with_header(
client, "GET", "/api/v1/devices",
client,
"GET",
"/api/v1/devices",
auth_value="Bearer wrong-key-12345",
)
assert resp.status_code == 401
@@ -82,7 +82,9 @@ class TestAuthEnforcement:
def test_post_without_auth_returns_401(self, client):
"""Creating a device without auth fails."""
resp = _unauth_request(
client, "POST", "/api/v1/devices",
client,
"POST",
"/api/v1/devices",
json={
"name": "Unauthorized Device",
"url": "mock://test",
@@ -115,7 +117,9 @@ class TestAuthEnforcement:
def test_malformed_bearer_token_returns_401_or_403(self, client):
"""A malformed Authorization header is rejected."""
resp = _with_header(
client, "GET", "/api/v1/devices",
client,
"GET",
"/api/v1/devices",
auth_value="just-a-key",
)
# FastAPI's HTTPBearer returns 403 for malformed format,
+19 -14
View File
@@ -5,7 +5,6 @@ create -> get -> update -> brightness -> power -> delete -> verify gone.
"""
class TestDeviceLifecycle:
"""A user creates a device, inspects it, modifies it, and deletes it."""
@@ -76,12 +75,15 @@ class TestDeviceLifecycle:
def test_create_multiple_devices_and_list(self, client):
"""Creating multiple devices shows all in the list."""
for i in range(3):
resp = client.post("/api/v1/devices", json={
"name": f"Device {i}",
"url": "mock://test",
"device_type": "mock",
"led_count": 30,
})
resp = client.post(
"/api/v1/devices",
json={
"name": f"Device {i}",
"url": "mock://test",
"device_type": "mock",
"led_count": 30,
},
)
assert resp.status_code == 201
resp = client.get("/api/v1/devices")
@@ -108,13 +110,16 @@ class TestDeviceLifecycle:
def test_update_tags(self, client):
"""Tags can be updated independently."""
resp = client.post("/api/v1/devices", json={
"name": "Tag Device",
"url": "mock://test",
"device_type": "mock",
"led_count": 10,
"tags": ["original"],
})
resp = client.post(
"/api/v1/devices",
json={
"name": "Tag Device",
"url": "mock://test",
"device_type": "mock",
"led_count": 10,
"tags": ["original"],
},
)
device_id = resp.json()["id"]
resp = client.put(
-1
View File
@@ -9,7 +9,6 @@ import pytest
from ledgrab.storage.base_store import BaseJsonStore, EntityNotFoundError
# ---------------------------------------------------------------------------
# Minimal concrete store for testing the base class
# ---------------------------------------------------------------------------
@@ -6,7 +6,6 @@ import pytest
from ledgrab.storage.device_store import Device, DeviceStore
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@@ -19,7 +19,6 @@ from ledgrab.storage.value_source import (
_VALUE_SOURCE_MAP,
)
# ---------------------------------------------------------------------------
# Coverage / shape
# ---------------------------------------------------------------------------
-1
View File
@@ -15,7 +15,6 @@ import pytest
from ledgrab.core.capture_engines import camera_engine as ce
# ---------------------------------------------------------------------------
# _parse_resolution — pure function, no stubs needed
# ---------------------------------------------------------------------------
-1
View File
@@ -5,7 +5,6 @@ import pytest
from ledgrab.storage.color_strip_store import ColorStripStore, MAX_COMPOSITE_DEPTH
from ledgrab.storage.database import Database
# ── Fixtures ──────────────────────────────────────────────────────────
-1
View File
@@ -16,7 +16,6 @@ from ledgrab.core.devices.ddp_provider import DDPDeviceProvider
from ledgrab.core.devices.device_config import DDPConfig
from ledgrab.core.devices.led_client import ProviderDeps
# ============================================================================
# parse_ddp_url
# ============================================================================
-1
View File
@@ -15,7 +15,6 @@ from pathlib import Path
import pytest
_REPO_ROOT = Path(__file__).resolve().parents[1]
_SERVER_SRC = _REPO_ROOT / "src" / "ledgrab"
_EVENTS_WS = _SERVER_SRC / "static" / "js" / "core" / "events-ws.ts"
-1
View File
@@ -19,7 +19,6 @@ from ledgrab.core.devices.govee_client import (
from ledgrab.core.devices.govee_provider import GoveeDeviceProvider
from ledgrab.core.devices.led_client import ProviderDeps
# ============================================================================
# URL parsing
# ============================================================================
-1
View File
@@ -8,7 +8,6 @@ from ledgrab.core.devices.led_client import ProviderDeps
from ledgrab.storage.database import Database
from ledgrab.storage.device_store import Device, DeviceStore
# ── Fixtures ──────────────────────────────────────────────────────────
-1
View File
@@ -27,7 +27,6 @@ from ledgrab.core.devices.lifx_client import (
)
from ledgrab.core.devices.lifx_provider import LIFXDeviceProvider
# ============================================================================
# URL parsing
# ============================================================================
-1
View File
@@ -20,7 +20,6 @@ from ledgrab.core.devices.nanoleaf_client import (
)
from ledgrab.core.devices.nanoleaf_provider import NanoleafDeviceProvider
# ============================================================================
# URL parsing
# ============================================================================
-1
View File
@@ -18,7 +18,6 @@ from ledgrab.core.devices.opc_client import (
)
from ledgrab.core.devices.opc_provider import OPCDeviceProvider
# ============================================================================
# URL parsing
# ============================================================================
-1
View File
@@ -16,7 +16,6 @@ from ledgrab.core.devices.serial_transport import (
port_exists,
)
# ── URL parsing ────────────────────────────────────────────────────
-1
View File
@@ -19,7 +19,6 @@ from ledgrab.core.devices.wiz_client import (
)
from ledgrab.core.devices.wiz_provider import WiZDeviceProvider
# ============================================================================
# parse_wiz_url
# ============================================================================
-1
View File
@@ -9,7 +9,6 @@ from fastapi.testclient import TestClient
import ledgrab.config as config_mod
from ledgrab.config import AuthConfig, Config, ServerConfig, StorageConfig
# ---------------------------------------------------------------------------
# Minimal app with a single WS endpoint using verify_ws_auth
# ---------------------------------------------------------------------------
-1
View File
@@ -20,7 +20,6 @@ from ledgrab.core.devices.yeelight_client import (
)
from ledgrab.core.devices.yeelight_provider import YeelightDeviceProvider
# ============================================================================
# parse_yeelight_url
# ============================================================================