[tool.ruff] line-length = 200 target-version = "py39" exclude = [".venv", "_AUTOMATIC_BACKUP", "models", "installer"] [tool.ruff.lint] # Hard-error rules (syntax errors, undefined names, shadowed imports) plus the # bug-finding tiers: B = bugbear (likely-bug patterns), RUF = ruff-native checks. # (F401/F841 were cleaned once but availability-check imports and a few # intentional unused locals remain, so they're not part of the CI gate. # Style-tier families — docstrings, quotes, pathlib, SIM rewrites — stay off: # they churn a deployed monolith for no behavior benefit.) select = ["E9", "F63", "F7", "F82", "F811", "B", "RUF"] ignore = [ "RUF001", "RUF002", "RUF003", # ambiguous-unicode: Cyrillic strings are real data here, # in docstrings (002) as much as in code — the captions # a rule was measured against belong next to the rule. "RUF100", # unused-noqa: noqa comments document intent for wider local runs ] [tool.ruff.lint.per-file-ignores] # Class-level constant dicts/lists are idiomatic in test classes. "tests/*" = ["RUF012"] [tool.mypy] # Type checking is scoped to the stt/ package (the logic layer); the monolith # can't be imported, so it stays unchecked until its logic is extracted here. files = ["stt"] # Runtime target is 3.9 (see ruff target-version), but mypy >=1.19 dropped # 3.9 analysis support — 3.10 is the closest it accepts. python_version = "3.10" check_untyped_defs = true ignore_missing_imports = true [[tool.mypy.overrides]] # The extracted logic modules are fully annotated — keep them that way. module = [ "stt.text_utils", "stt.digital_silence", "stt.formatting", "stt.segments", "stt.config_utils", "stt.translation_utils", "stt.downloads", "stt.paths", "stt.self_update", "stt.server_port", "stt.owner_exec", "stt.server_restart", "stt.parent_watch", "stt.model_disk", "stt.model_files", "stt.model_catalog", "stt.nllb_catalog", "stt.tts_catalog", "stt.calibration", "stt.model_memory", "stt.session_meta", "stt.http_params", "stt.cuda_index", "stt.livemap", "stt.llm_translate", "stt.service_phase", "stt.sermon_summary", "stt.session_index", "stt.translation_replay", "stt.peer_load", "stt.offload_jobs", "stt.translation_backfill", "stt.tts_queue", "stt.session_cleanup", "stt.wav_edit", "stt.hostname_lookup", "stt.pair_tokens", "stt.repeat_filter", "stt.tts_backends", "stt.tunnel", "stt.request_origin", "stt.session_reset", "stt.log_gate", "stt.crash_reports", "stt.wheel_policy", "stt.optional_deps", "stt.demo_mode", "stt.demo_window", "stt.demo_playback", "stt.demo_api", "stt.demo_redact", "stt.demo_synth", "stt.demo_scrub", "stt.demo_guard", "stt.worker_crash", "stt.diagnostics", "stt.start_watch", "stt.session_edit", "stt.shutdown_channel", "stt.win_job", "stt.fd_limit", "stt.engineio_compat", "stt.caption_order", "stt.segment_disposition", "stt.disposition_stats", "stt.music_gate", "stt.asr_replay", "stt.hq_audio", "stt.display_clear", ] disallow_untyped_defs = true [[tool.mypy.overrides]] # Legacy capture loop: Optional process/queue attributes are runtime-guarded # (start() sets them before the loop runs); not worth restructuring for types. module = "stt.audio_capture" disable_error_code = ["union-attr"] [tool.pytest.ini_options] testpaths = ["tests"] # Repo root on sys.path so tests import the stt/ package regardless of how # pytest is invoked (`uv run pytest` doesn't add the cwd, `python -m pytest` does). pythonpath = ["."] # Coverage is measured over the whole stt/ package, which grows as pure logic # is extracted out of speech_to_text.py (which cannot be imported — it has # import-time side effects, so coverage can't instrument it directly). # Nothing is omitted by choice: the IO-heavy regions (watchdog GUI/process # management/provisioning, the ffmpeg capture loop, SMB/mount code) stay in # the denominator, so the package total understates the logic layer. The 85% # target applies per logic module (text_utils, formatting, segments, # config_utils, translation_utils, ...), not the package total. # Run: pytest --cov [tool.coverage.run] source = ["stt"] [tool.coverage.report] skip_covered = false show_missing = true