Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Drop Python 3.9 support (cleanup checklist)

Open
#279 0 comments 0 reactions 0 assignees View on GitHub

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

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: raise requires-python from >=3.9 to >=3.10
  • Remove Programming Language :: Python :: 3.9 classifier
  • 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] in policyengine_core/tools/hugging_face.py and policyengine_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] environments block in pyproject.toml if it still exists (likely already gone once downstream core picks up 3.9-compat release)
  • Hand-revert Optional[X] → X | None in: 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] environments block (if still present)
  • Hand-revert 3 sites in policyengine_uk/data/dataset_schema.py and policyengine_uk/simulation.py
  • policyengine_uk/tests/test_build_metadata.py: revert ExitStack back to parenthesized with (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, UP045 from [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 (inside Annotated[]) and src/policyengine/core/region.py:18 (module-level type alias).
  • Simplify Python-Compat CI job in .github/workflows/pr_code_changes.yaml — drop the explicit h5py install (needed because scoping_strategy.py imports 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 off policyengine-us==1.602.0 / policyengine-uk==2.74.0 to 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.utc in core/tax_benefit_model_version.py — datetime.UTC is 3.11+
  • Gotcha when StrEnum returns: str(Foo.X) differs — StrEnum returns "x", (str, Enum) returns "Foo.X". Audit any code relying on str() of enum members before reverting.

Keep until 3.12 EOL (October 2028):

  • TypeVar + Generic[T] pattern in core/cache.py and core/output.py — PEP 695 class 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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PolicyEngine/policyengine.py

All issues in PolicyEngine/policyengine.py

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.