-
Notifications
You must be signed in to change notification settings - Fork 170
Description
Summary
Add comprehensive type annotations throughout the Vorta codebase and enforce type checking via CI. This will improve code quality, catch bugs earlier, enable better IDE support, and make the codebase more maintainable.
Current State
- Type coverage: ~22% of files import
typing, ~32% have actual type hints - No type checker: Neither mypy nor pyright is configured
- No py.typed marker: Package cannot be used as a typed library
- Inconsistent annotations: Some files have excellent type hints (e.g.,
network_status/abc.py,store/settings.py), while others have none (e.g.,borg/borg_job.py,views/repo_tab.py) - Minimum Python version: Currently 3.8
Examples of current inconsistency
Well-typed file (src/vorta/network_status/abc.py):
def is_network_metered(self) -> bool:
"""Is the currently connected network a metered connection?"""
def get_current_wifi(self) -> Optional[str]:
"""Get current SSID or None if Wifi is off."""
def get_known_wifis(self) -> List['SystemWifiInfo']:
"""Get WiFi networks known to system."""Untyped file (src/vorta/borg/create.py):
def process_result(self, result):
if result['returncode'] in [0, 1] and 'archive' in result['data']:Proposal
1. Bump minimum Python version to 3.10
Rationale:
- Python 3.8 reached end-of-life in October 2024
- Python 3.9 reaches end-of-life in October 2025
- Python 3.10+ provides better typing ergonomics:
X | Yunion syntax instead ofUnion[X, Y]list[str]instead ofList[str](builtin generics)ParamSpecandTypeAliasfor advanced patterns- Better error messages in type checkers
- PyQt6 (which Vorta uses) requires Python 3.6.1+ minimum, so this is compatible
- Most Linux distributions shipping Vorta have Python 3.10+ available
Alternative: Stay at 3.9 minimum if 3.10 is too aggressive, but strongly recommend 3.10+.
2. Choose and configure a type checker
Recommended: mypy
Reasons:
- Industry standard, well-documented
- Good PyQt/Qt stubs available (
PyQt6-stubs) - Mature, stable tool
- Integrates well with pre-commit and CI
Configuration to add to pyproject.toml:
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false # Start permissive, tighten later
check_untyped_defs = true
ignore_missing_imports = true # For untyped dependencies
# Per-module overrides for gradual adoption
[[tool.mypy.overrides]]
module = "vorta.store.*"
disallow_untyped_defs = true
[[tool.mypy.overrides]]
module = "vorta.network_status.*"
disallow_untyped_defs = true3. Add type stubs for dependencies
uv add --dev mypy PyQt6-stubs types-peewee4. Add to CI and pre-commit
.pre-commit-config.yaml:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies:
- PyQt6-stubs
- types-peewee
args: [--config-file=pyproject.toml].github/workflows/test.yml - add to lint job:
- name: Type check
run: uv run mypy src/vorta5. Add py.typed marker
Create src/vorta/py.typed (empty file) to indicate the package ships type information.
6. Phased rollout
Attempting to type the entire codebase at once would be overwhelming. Recommend a phased approach:
Phase 1: Infrastructure (this issue)
- Bump Python minimum to 3.10
- Configure mypy with permissive settings
- Add to CI (warnings only, don't fail builds initially)
- Add
py.typedmarker - Add type stubs for dependencies
Phase 2: Core modules
-
src/vorta/store/- Data models and settings (already partially typed) -
src/vorta/network_status/- Already well-typed, just enforce -
src/vorta/scheduler.py- Already partially typed
Phase 3: Borg layer
-
src/vorta/borg/borg_job.py- Base job class -
src/vorta/borg/*.py- All borg command implementations -
src/vorta/jobs_manager.py
Phase 4: Views layer
-
src/vorta/views/*.py- PyQt6 views (most complex due to dynamic UI loading) -
src/vorta/tray_menu.py
Phase 5: Enforcement
- Enable
disallow_untyped_defs = trueglobally - Make mypy failures block CI
- Remove
ignore_missing_importswhere possible
Benefits
- Catch bugs at development time - Type mismatches caught before runtime
- Better IDE support - Autocomplete, inline docs, refactoring tools work better
- Self-documenting code - Types serve as documentation
- Easier onboarding - New contributors understand code faster
- Safer refactoring - Type checker catches breaking changes
Challenges to address
- PyQt6 dynamic UI loading -
uic.loadUiType()returns dynamically created classes. May need# type: ignorecomments or custom stubs. - Peewee ORM - Model fields have runtime behavior that's tricky to type.
types-peeweestubs help but aren't perfect. - Large codebase - 81 Python files means significant effort. Phased approach essential.
Related work
- Ruff already enforces code style
- Pre-commit hooks already run on commits
- CI already blocks on lint failures
Adding type checking fits naturally into the existing quality infrastructure.