refactor: systematic code quality improvements across 20 dimensions (#377)
* refactor: improve code quality across multiple dimensions
Code quality improvements identified and driven by desloppify analysis:
**action_handler.py**
- Replace 9 repetitive shortcut forwarding methods with data-driven
_SHORTCUT_ACTIONS class-level dispatch table
- Add _make_shortcut_handler() factory for shortcut closures
- Add explicit type annotation for action_handlers dict
**config_manager.py**
- Move 'import copy' from __init__ to module level (stdlib best practice)
- Add _VALID_SECTIONS frozenset for section name validation in set()
- Narrow broad Exception catch to (KeyError, TypeError) in set()
- Add deprecation warning to save_settings() guiding toward save_config()
**tray_indicator.py**
- Consolidate all imports at module top level
- Remove noqa E402 suppressions for mid-file imports
- Move ResourceManager import alongside other local imports
**recognition_manager.py**
- Narrow broad 'except Exception' to 'except OSError' for audio device
enumeration errors
- Narrow 'except Exception' to 'except (FileNotFoundError, OSError)' for
notification subprocess errors
**Tests**
- Update test assertions to use specific exception types matching narrowed
catches (OSError, FileNotFoundError instead of Exception/RuntimeError)
* refactor: improve type safety, callback contracts, and method decomposition
**main.py** — Mid-level elegance
- Document callback wiring contract: text_callback, action_callback,
state_callback with threading context, argument types, and behavior
- Add return type annotations to nested callback functions
- Reorder: define callbacks before registration for readability
**config_manager.py** — Type safety, error consistency
- Add typed accessors: get_str(), get_bool(), get_int(), get_float(),
get_optional_int() eliminating Any returns for common access patterns
- Narrow load_config exception to (json.JSONDecodeError, OSError)
- Narrow save_config exception to (OSError, TypeError)
**recognition_manager.py** — Low-level elegance
- Decompose _init_whispercpp (141 lines) into three focused methods:
- _init_whispercpp: validation and download gating (35 lines)
- _load_whispercpp_model: backend detection and model loading (55 lines)
- _handle_gpu_fallback: GPU incompatibility recovery (40 lines)
- Narrow exception catch from broad Exception to specific types
**tray_indicator.py, settings_dialog.py** — Type safety
- Migrate config access from untyped get() to typed get_str(),
get_bool(), get_optional_int() across 8 call sites
* refactor: narrow 11 more broad Exception catches to specific types
Systematically replaced broad 'except Exception' with specific exception
types across recognition_manager.py:
- test_audio_input: (OSError, ValueError) for audio read errors
- test_audio_input outer: (OSError, ValueError, RuntimeError) for unexpected
- _init_whisper: (RuntimeError, OSError) for engine init failures
- _transcribe_with_whisper: (RuntimeError, OSError, ValueError) for transcription
- _download_whispercpp_model: (OSError, RuntimeError, ValueError) for downloads
- _download_vosk_model: (OSError, RuntimeError, ValueError) for downloads
- _download_whisper_model: (OSError, RuntimeError, ValueError) for downloads
This brings the total narrowed exception catches to 18+ across the codebase,
improving error consistency and making failure modes more predictable.
* style: fix black formatting issues
Auto-format with black to fix CI lint failures in recognition_manager.py,
settings_dialog.py, and test_settings_shortcuts.py.
* test: add coverage for typed config accessors
Add TestTypedAccessors class with 15 tests covering:
- get_str(): string values, defaults, non-string conversion, None handling
- get_bool(): boolean values, defaults
- get_int(): integer values, invalid input fallback, missing key defaults
- get_float(): float values, invalid input fallback
- get_optional_int(): int values, None values, missing keys, invalid input
Fixes codecov/patch coverage requirement for new typed accessor methods.