Skip to content

Patching Python 3.14 Support#14324

Draft
nucleogenesis wants to merge 7 commits intolearningequality:release-v0.19.xfrom
nucleogenesis:13823--py3.14
Draft

Patching Python 3.14 Support#14324
nucleogenesis wants to merge 7 commits intolearningequality:release-v0.19.xfrom
nucleogenesis:13823--py3.14

Conversation

@nucleogenesis
Copy link
Member

Summary

Add Python 3.14 support to Kolibri using monkey patches for stdlib removals, without dropping support for Python 3.6–3.13.

Python 3.14 removed several stdlib APIs that our pinned dependencies rely on:

  • pkgutil.find_loader (used by django-filter 21.1) — patched via importlib.util.find_spec
  • ast.Str (used by pytest 6.x assertion rewriting) — handled by version-splitting test deps to use pytest 7+ on Python ≥3.10
  • BaseContext.__copy__ behavior change in Django 3.2 — patched with the fix from Django's main branch

Additionally:

  • le-utils and morango have python_requires <3.14 on PyPI (code works fine on 3.14). For py3.14, they're installed from py3.14-bump git branches with the constraint bumped to <3.15.
  • Existing monkey patches for cgi and distutils (added for 3.12/3.13) already cover Django and redis on 3.14.

Verification:

  • Full test suite (3729 tests) passes on Python 3.14.2 with pytest 8.4.2
  • Full test suite (3729 tests) passes on Python 3.9.25 with pytest 6.2.5 (no regressions)

References

Closes #13823

Reviewer guidance

The changes are isolated to build/CI config and monkey patches:

  1. kolibri/utils/env.py — Two new monkey patch functions (monkey_patch_pkgutil and fix_django_template_context_copy), both guarded by sys.version_info < (3, 14) so they're no-ops on older Python. They follow the exact same pattern as the existing monkey_patch_distutils and forward_port_cgi_module patches in the same file.

  2. requirements/test.txt — pytest 6.x for Python <3.10, pytest 7+ for Python ≥3.10. The split is needed because pytest 6 uses ast.Str (removed in 3.14) and pytest 7+ is incompatible with pytest-pythonpath.

  3. pytest.ini — Has both python_paths (for pytest-pythonpath on pytest <7) and pythonpath (built-in on pytest 7+). Each version warns about the other's config key but doesn't error.

  4. requirements/base.txt / tox.ini — le-utils and morango get python_version < "3.14" markers on the PyPI pins, with git-based installs from py3.14-bump branches for py≥3.14 in tox deps.

Risky areas:

  • The fix_django_template_context_copy patch is the least obvious — verify it's needed by reverting it and running tests on py3.14 if in doubt.
  • The test dep version split means py≥3.10 now uses newer pytest and factory-boy — unlikely to cause issues but worth noting.

AI usage

This PR was developed using Claude Code with a sub-agent driven workflow. I created the investigation plan and made key architectural decisions (monkey patching over conditional deps, version-split boundary at py3.10, scope limited to test-pass). Claude Code performed the empirical testing on a Python 3.14.2 venv to identify all compatibility issues, implemented each change via subagents with spec compliance and code quality reviews after each task, and ran the full test suite on both py3.14 and py3.9 to verify correctness and no regressions.

nucleogenesis and others added 6 commits March 2, 2026 20:59
django-filter 21.1 uses pkgutil.find_loader at import time, which was
removed in Python 3.14. Patch it to use importlib.util.find_spec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pytest 6.2.5 uses ast.Str removed in Python 3.14. Use pytest >=7 on
Python 3.10+, which has built-in pythonpath support (replacing
pytest-pythonpath).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python 3.14 changed the behavior of copy(super()), which breaks
Django 3.2's BaseContext.__copy__. Backport the fix from Django's
main branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added DEV: dev-ops Continuous integration & deployment DEV: backend Python, databases, networking, filesystem... SIZE: small labels Mar 3, 2026
@nucleogenesis nucleogenesis mentioned this pull request Mar 3, 2026
1 task
@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Build Artifacts

- Revert factory-boy version split: factory-boy 2.7.0 + fake-factory
  work fine on all Python versions including 3.14
- Version-split cffi and cryptography in cext.txt: pinned versions
  don't build on Python 3.14, use newer versions there

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DEV: backend Python, databases, networking, filesystem... DEV: dev-ops Continuous integration & deployment SIZE: small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant