chore: cleanup — remove shims, fix ruff warnings, ignore noisy rules

- Removed backward-compat shims _sanitise_for_fs / _strip_episode_from_normalised
  in domain/release/value_objects.py (zero callers).
- Fixed ruff warnings across the codebase:
    * PLW1510: explicit check=False on subprocess.run calls
    * PLC0415: promoted lazy imports to module top where no cycle exists
      (manage_subtitles, placer, qbittorrent/client, file_manager)
    * E402: fixed module-level import ordering in language_registry.py and
      subtitles/knowledge/loader.py
    * F841 / B007: removed unused locals (identifier.py)
    * C416: replaced unnecessary set comprehension with set() in
      release/knowledge.py
- Ruff config: ignore PLR0911/PLR0912 globally (noisy on mappers and
  orchestrator use-cases) and PLW0603 (intentional for the memory singleton).
- Updated tech debt memory: P1 done, ShowStatus actually complete (was a
  stale note).
This commit is contained in:
2026-05-18 00:02:45 +02:00
parent 1d50b63af2
commit f17abdbaec
13 changed files with 27 additions and 22 deletions
@@ -10,7 +10,11 @@ from alfred.domain.subtitles.knowledge.loader import KnowledgeLoader
from alfred.domain.subtitles.services.identifier import SubtitleIdentifier from alfred.domain.subtitles.services.identifier import SubtitleIdentifier
from alfred.domain.subtitles.services.matcher import SubtitleMatcher from alfred.domain.subtitles.services.matcher import SubtitleMatcher
from alfred.domain.subtitles.services.pattern_detector import PatternDetector from alfred.domain.subtitles.services.pattern_detector import PatternDetector
from alfred.domain.subtitles.services.placer import PlacedTrack, SubtitlePlacer from alfred.domain.subtitles.services.placer import (
PlacedTrack,
SubtitlePlacer,
_build_dest_name,
)
from alfred.domain.subtitles.services.utils import available_subtitles from alfred.domain.subtitles.services.utils import available_subtitles
from alfred.domain.subtitles.value_objects import ScanStrategy from alfred.domain.subtitles.value_objects import ScanStrategy
from alfred.infrastructure.persistence.context import get_memory from alfred.infrastructure.persistence.context import get_memory
@@ -174,8 +178,6 @@ class ManageSubtitlesUseCase:
# --- Dry run: skip placement --- # --- Dry run: skip placement ---
if dry_run: if dry_run:
from alfred.domain.subtitles.services.placer import _build_dest_name
placed_dtos = [] placed_dtos = []
for t in matched: for t in matched:
if not t.file_path: if not t.file_path:
+1 -1
View File
@@ -111,7 +111,7 @@ def load_editions() -> dict:
def load_sources_extra() -> set[str]: def load_sources_extra() -> set[str]:
"""Additional source tokens from site files.""" """Additional source tokens from site files."""
return {t for t in _load_sites().get("sources", [])} return set(_load_sites().get("sources", []))
def load_hdr_extra() -> set[str]: def load_hdr_extra() -> set[str]:
-5
View File
@@ -65,11 +65,6 @@ def _strip_episode_from_normalized(normalized: str) -> str:
return ".".join(result) return ".".join(result)
# Keep old names as aliases for backward compatibility during the US English migration
_sanitise_for_fs = _sanitize_for_fs
_strip_episode_from_normalised = _strip_episode_from_normalized
@dataclass @dataclass
class ParsedRelease: class ParsedRelease:
"""Structured representation of a parsed release name.""" """Structured representation of a parsed release name."""
@@ -11,12 +11,12 @@ from pathlib import Path
import yaml import yaml
import alfred as _alfred_pkg
from ..value_objects import Language from ..value_objects import Language
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
import alfred as _alfred_pkg
_BUILTIN_ROOT = Path(_alfred_pkg.__file__).parent / "knowledge" _BUILTIN_ROOT = Path(_alfred_pkg.__file__).parent / "knowledge"
_LEARNED_ROOT = Path(_alfred_pkg.__file__).parent.parent / "data" / "knowledge" _LEARNED_ROOT = Path(_alfred_pkg.__file__).parent.parent / "data" / "knowledge"
+2 -2
View File
@@ -5,10 +5,10 @@ from pathlib import Path
import yaml import yaml
logger = logging.getLogger(__name__)
import alfred as _alfred_pkg import alfred as _alfred_pkg
logger = logging.getLogger(__name__)
# Builtin knowledge — anchored on the alfred package itself, not on this file's depth # Builtin knowledge — anchored on the alfred package itself, not on this file's depth
_BUILTIN_ROOT = Path(_alfred_pkg.__file__).parent / "knowledge" _BUILTIN_ROOT = Path(_alfred_pkg.__file__).parent / "knowledge"
@@ -110,6 +110,7 @@ class SubtitleIdentifier:
capture_output=True, capture_output=True,
text=True, text=True,
timeout=30, timeout=30,
check=False,
) )
data = json.loads(result.stdout) data = json.loads(result.stdout)
except ( except (
@@ -127,7 +128,6 @@ class SubtitleIdentifier:
tags = stream.get("tags", {}) tags = stream.get("tags", {})
disposition = stream.get("disposition", {}) disposition = stream.get("disposition", {})
lang_code = tags.get("language", "") lang_code = tags.get("language", "")
title = tags.get("title", "")
lang = self.kb.language_for_token(lang_code) if lang_code else None lang = self.kb.language_for_token(lang_code) if lang_code else None
@@ -320,7 +320,7 @@ class SubtitleIdentifier:
lang_groups.setdefault(key, []).append(track) lang_groups.setdefault(key, []).append(track)
result = [] result = []
for lang_code, group in lang_groups.items(): for group in lang_groups.values():
if len(group) == 1: if len(group) == 1:
result.extend(group) result.extend(group)
continue continue
@@ -62,6 +62,7 @@ class PatternDetector:
capture_output=True, capture_output=True,
text=True, text=True,
timeout=30, timeout=30,
check=False,
) )
data = json.loads(result.stdout) data = json.loads(result.stdout)
return len(data.get("streams", [])) > 0 return len(data.get("streams", [])) > 0
+1 -2
View File
@@ -6,6 +6,7 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from ..entities import SubtitleCandidate from ..entities import SubtitleCandidate
from ..value_objects import SubtitleType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -18,8 +19,6 @@ def _build_dest_name(track: SubtitleCandidate, video_stem: str) -> str:
{video_stem}.{lang}.sdh.{ext} {video_stem}.{lang}.sdh.{ext}
{video_stem}.{lang}.forced.{ext} {video_stem}.{lang}.forced.{ext}
""" """
from ..value_objects import SubtitleType
if not track.language or not track.format: if not track.language or not track.format:
raise ValueError("Cannot compute destination name: language or format missing") raise ValueError("Cannot compute destination name: language or format missing")
@@ -1,6 +1,7 @@
"""qBittorrent Web API client.""" """qBittorrent Web API client."""
import logging import logging
from pathlib import Path
from typing import Any from typing import Any
import requests import requests
@@ -365,8 +366,6 @@ class QBittorrentClient:
return t return t
# 3. save_path ends with the folder name # 3. save_path ends with the folder name
from pathlib import Path
for t in torrents: for t in torrents:
if t.save_path and Path(t.save_path).name.lower() == name_lower: if t.save_path and Path(t.save_path).name.lower() == name_lower:
return t return t
@@ -34,6 +34,7 @@ def probe(path: Path) -> MediaInfo | None:
capture_output=True, capture_output=True,
text=True, text=True,
timeout=30, timeout=30,
check=False,
) )
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
logger.warning("ffprobe timed out on %s", path) logger.warning("ffprobe timed out on %s", path)
@@ -2,6 +2,7 @@
import logging import logging
import os import os
import shutil
from collections import namedtuple from collections import namedtuple
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -285,8 +286,6 @@ class FileManager:
if dest_item.exists(): if dest_item.exists():
skipped.append(str(rel)) skipped.append(str(rel))
continue continue
import shutil
shutil.copy2(item, dest_item) shutil.copy2(item, dest_item)
copied.append(str(rel)) copied.append(str(rel))
logger.debug(f"Copied for seeding: {rel}") logger.debug(f"Copied for seeding: {rel}")
@@ -59,6 +59,7 @@ def move(source: str, destination: str) -> dict[str, Any]:
["mv", str(src), str(dst)], ["mv", str(src), str(dst)],
capture_output=True, capture_output=True,
text=True, text=True,
check=False,
) )
if result.returncode != 0: if result.returncode != 0:
logger.error(f"mv failed: {result.stderr}") logger.error(f"mv failed: {result.stderr}")
+9 -1
View File
@@ -136,7 +136,15 @@ lint.select = [
"PL", "PL",
"UP", "UP",
] ]
lint.ignore = ["PLR0913", "PLR2004", "TID252", "E501"] lint.ignore = [
"PLR0911", # too-many-returns: noisy on mappers / orchestrator use-cases with early-return validation
"PLR0912", # too-many-branches: same as above
"PLR0913", # too-many-arguments
"PLR2004", # magic-value-comparison
"PLW0603", # global statement: intentional for the memory singleton (see project_memory_singleton.md)
"TID252",
"E501",
]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["PLC0415"] "tests/**/*.py" = ["PLC0415"]