Drop Python 3.9 support (cleanup checklist)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
- Issue type
- Refactor
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- python
- Domain
- build-system, ci-cd, tooling
Research direction
Start with the four repositories' pyproject.toml files and CI matrices, then inspect the listed Python files and policyengine.py's Ruff and mypy configuration. Check the dependency versions and existing 3.9 compatibility work before changing conditional items. Done means all four repositories support Python 3.10+, their CI is green, and fresh 3.10 environments can install the us and uk extras.
Written by the indexing model from the issue text.
Description
Trigger
File when all confirmed 3.9 users (currently: Census) have migrated to 3.10+.
Background
Supporting 3.9 across the stack required working around non-trivial syntax and dependency cruft in four repos. ~95% of the cruft is 3.9-specific — dropping 3.9 alone recovers most of it, even if 3.10 support remains (3.10 hits EOL Oct 2026, ~6 months out, so keeping it through natural life is cheap).
Landed as: PolicyEngine/policyengine-core#454, PolicyEngine/policyengine-us#8035, PolicyEngine/policyengine-uk#1625, PolicyEngine/policyengine.py#278.
Checklist
All 4 repos
-
pyproject.toml: raiserequires-pythonfrom>=3.9to>=3.10 - Remove
Programming Language :: Python :: 3.9classifier - Remove
"3.9"from all CI Python matrices
policyengine-core (PolicyEngine/policyengine-core)
- Remove numpy env marker; go back to single
numpy>=2.1.0,<3(2.1+ supports 3.10+) - Hand-revert
Optional[X]/Tuple[X]→X | None/tuple[X]inpolicyengine_core/tools/hugging_face.pyandpolicyengine_core/tools/google_cloud.py(~5 sites; this repo's ruff config doesn't have UP rules enabled)
policyengine-us (PolicyEngine/policyengine-us)
- Remove
[tool.uv] environmentsblock inpyproject.tomlif it still exists (likely already gone once downstream core picks up 3.9-compat release) - Hand-revert
Optional[X]→X | Nonein:policyengine_us/system.py,data/economic_assumptions.py,data/dataset_schema.py,tests/core/test_payroll_contributions.py,tests/microsimulation/data/fixtures/test_extend_single_year_dataset.py(5 sites) - Regenerate
uv.lock
policyengine-uk (PolicyEngine/policyengine-uk)
- Remove
[tool.uv] environmentsblock (if still present) - Hand-revert 3 sites in
policyengine_uk/data/dataset_schema.pyandpolicyengine_uk/simulation.py -
policyengine_uk/tests/test_build_metadata.py: revertExitStackback to parenthesizedwith (a, b, c): -
[tool.ruff] target-version = "py39"→"py310" - Regenerate
uv.lock
policyengine.py (this repo)
-
pyproject.toml:[tool.ruff] target-version = "py39"→"py310"[tool.mypy] python_version = "3.9"→"3.10"- Remove
UP006,UP007,UP035,UP045from[tool.ruff.lint] ignore
- Auto-revert ~210 annotation sites:
ruff check --fix --unsafe-fixes .— this re-applies UP045 (Optional[X]→X | None), UP007 (Union[X, Y]→X | Y), UP006/UP035 (List→list). - Hand-revert the two discriminated-union sites ruff's UP rules don't cover:
src/policyengine/core/scoping_strategy.py:222(insideAnnotated[]) andsrc/policyengine/core/region.py:18(module-level type alias). - Simplify
Python-CompatCI job in.github/workflows/pr_code_changes.yaml— drop the explicith5pyinstall (needed becausescoping_strategy.pyimports it but extras weren't available; once[us]/[uk]extras are bumped to versions that pull h5py via core, it's redundant). - Bump
[us]/[uk]/[dev]extras offpolicyengine-us==1.602.0/policyengine-uk==2.74.0to versions that don't pin 3.11+ themselves (separate follow-up from this cleanup — may already be bumped by the time this issue is actioned).
Not in scope
Keep until 3.10 EOL (October 2026):
class Foo(str, Enum)pattern (4 files:outputs/{aggregate,change_aggregate,inequality,poverty}.py) — StrEnum is 3.11+timezone.utcincore/tax_benefit_model_version.py—datetime.UTCis 3.11+- Gotcha when
StrEnumreturns:str(Foo.X)differs —StrEnumreturns"x",(str, Enum)returns"Foo.X". Audit any code relying onstr()of enum members before reverting.
Keep until 3.12 EOL (October 2028):
TypeVar+Generic[T]pattern incore/cache.pyandcore/output.py— PEP 695class Foo[T]is 3.12+.
Verification
After cleanup, each repo's CI should be green with reduced matrix (3.10+). Cross-repo smoke: pip install policyengine[us] and pip install policyengine[uk] succeed on 3.10 in a fresh venv.
- Dominant language
- Python
- Stars
- 8
- Forks
- 9
- Avg merge
- 15h 28m
- Merged PRs (30d)
- 13
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from PolicyEngine/policyengine.py
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
PolicyEngine/policyengine.py#549 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
PolicyEngine/policyengine.py#473 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
PolicyEngine/policyengine.py#436 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
PolicyEngine/policyengine.py#408 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
PolicyEngine/policyengine.py#551 ·
Maintainers usually reply within 1 day
All issues in PolicyEngine/policyengine.py
Similar issues
-
good first issue hacktoberfest
Difficulty 1/5 Under an hour Newbie friendliness 92/100
RogueAlg0/taken#387 · 4 comments ·
Maintainers usually reply within 1 day
-
tool-calling
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
vllm-project/vllm#59838 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
raullenchai/Rapid-MLX#4042 ·
Maintainers usually reply within 1 day
-
documentation
Difficulty 1/5 Under an hour Newbie friendliness 92/100
transitmatters/mbta-slow-zone-bot#70 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
open-webui/open-webui#31871 · 1 comment ·
Maintainers usually reply within 1 day