ADR-013: Python 3.9+ Minimum Version¶
Status: ✅ Accepted Date: 2026-01-29 Deciders: Rodrigo Roldán
Context¶
Python versions and support:
Version |
Release |
EOL |
Status |
|---|---|---|---|
3.7 |
2018-06 |
2023-06 |
❌ EOL |
3.8 |
2019-10 |
2024-10 |
❌ EOL |
3.9 |
2020-10 |
2025-10 |
✅ Supported |
3.10 |
2021-10 |
2026-10 |
✅ Supported |
3.11 |
2022-10 |
2027-10 |
✅ Supported |
3.12 |
2023-10 |
2028-10 |
✅ Supported |
Features per version relevant for Reqivo:
Python 3.9+:
dictmerge operator (|)Type hints improvements (
list[str]instead ofList[str])zoneinfomodulePerformance improvements
Python 3.10+:
Pattern matching (
match/case)Better error messages
Union types (
str | Noneinstead ofOptional[str])
Python 3.11+:
Exception groups
Performance (10-60% faster)
Decision¶
Python 3.9+ is the minimum supported version.
Configuration (pyproject.toml):
[project]
requires-python = ">=3.9"
Testing matrix (CI):
# .github/workflows/ci.yml
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
Reason:
3.9 has EOL in 2025-10 (sufficient runway)
Modern type hints without
typing.List,typing.DictAsync improvements
Balance between modernity and compatibility
Consequences¶
Positive ✅¶
Modern features: Dict merge, better type hints
Long support: 3.9 EOL in 2025-10
Performance: 3.9+ is faster than 3.7/3.8
Security: Old versions without security patches
Negative ❌¶
Excludes 3.8: Some users still on 3.8
Legacy systems: Old systems may not have 3.9+
Corporate environments: Companies slow to update
Mitigations¶
Clear documentation: Indicate 3.9+ requirement
Error message: Setup.py fails with clear message if <3.9
Long support: 3.9 EOL still distant
Type Hints Examples¶
Python 3.9+:
# ✅ Modern (3.9+)
def get_headers(self) -> dict[str, str]:
return self._headers
def get_cookies(self) -> list[str]:
return self._cookies
# ❌ Old (3.7-3.8)
from typing import Dict, List
def get_headers(self) -> Dict[str, str]:
return self._headers
def get_cookies(self) -> List[str]:
return self._cookies
Python 3.10+ (future, requires bump):
# Cleaner union types
def timeout(self) -> float | None: # Instead of Optional[float]
return self._timeout
Migration Path¶
If in the future we need 3.10+ features:
Major version bump (breaking change)
Document in CHANGELOG.md
Migration guide for users
Alternatives Considered¶
Python 3.8+: Rejected. 3.8 EOL in 2024-10.
Python 3.10+: Rejected. Excludes too many users.
Python 3.11+: Rejected. Too recent.
References¶
PEP 596: Python 3.9 Release Schedule