You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If we upgrade past Python 3.12, we can replace see if we can replace os.walk with Path.rglob. When doing so, we should check if it's paired with e.g. .endswith("*.extension") logic, which we can roll into the glob.
Searched each sibling repository's default branch with the regex above, plus CI matrices ('3.10', '3.11'), requires-python, and version-conditional code.
1. Packages: require Python 3.11 or later
Drop the classifier and matrix entry, set requires-python = ">=3.11", apply Ruff fixes, add a changelog entry.
Release the Rust packages (cardinal-rs 0.0.9, ocds-merge-rs 0.1.7), for wheels on 3.15. The pure-Python packages' wheels already install on 3.15, so release them when there's a substantive change.
All three run on kingfisher-main, in a virtualenv built from the server's default Python (3.10 on Ubuntu 22.04). Separate the apps' Python from the OS (see open-contracting/deploy#602). These first moved to 3.13, which was wrongly taken to be Ubuntu 26.04's version; it's 3.14 (see section 5):
These were on 3.11, below the Ubuntu 26.04 LTS version, which is 3.14 (not 3.13, as first written). Docker images don't depend on the server, so these can be upgraded now. Each updates .python-version (which CI reads), the Dockerfiles and the contributing docs:
Deploy: pull the images and recreate the containers (kingfisher-process on both kingfisher-main and registry)
5. Applications to 3.14
The handbook's minimum for applications is the latest Ubuntu LTS's Python: 3.14 in Ubuntu 26.04 (0d70430 also rewords the policy, and moves cookiecutter-django and the docs' examples to 3.14). The PRs also upgrade compiled packages without 3.14 wheels.
Deploy: pull the images and recreate the containers (cove-oc4ids, data-registry, digitalbuying, kingfisher-process on kingfisher-main and registry, pelican-backend, pelican-frontend)
Deploy: kingfisher-main, while Scrapyd is idle (recreates the virtualenvs for kingfisher-collect, kingfisher-summarize and collect-generic)
Deferred
os.walk → Path.rglob (comment above) applies once packages require 3.12+, not to this upgrade.
Post-upgrade checks (do these next time, before deploying)
The test suites passed on 3.14, but two runtime failures reached production after deploying:
pika 1.3.2 (indirect, via yapw) calls asyncio.get_event_loop(), which raises on Python 3.14 if no loop is set. Every yapw Async/AsyncConsumer worker crashed on startup (data-registry, kingfisher-process, pelican-backend, pelican-frontend). No test starts an async consumer, so CI didn't catch it. Fixed by upgrading to pika 1.4.4.
sentry-sdk 2.8.0 raises an internal error on Python 3.13+ when serializing a function frame's locals (copy(frame.f_locals); PEP 667), so unhandled exceptions weren't reported. A module-level exception still reports, so a trivial test passes. Fixed by upgrading to sentry-sdk 2.73.0.
Checklist:
Smoke test every entry point on the new Python, using the commands in the deploy repository's salt/docker_apps/files/*.yaml (workers, management commands, and gunicorn core.wsgi). Use a fresh environment, a throwaway RABBIT_EXCHANGE_NAME (to not consume development messages), PYTHONWARNINGS=default, and SENTRY_DSN pointing at a local HTTP sink. Each worker must stay up, then exit cleanly on SIGINT; each web server must serve a request.
Check that Sentry reports an unhandled exception raised inside a function (not at module level), for example by running a worker without RabbitMQ.
Audit old pins, including indirect ones: list pins released before the new Python's release that have a newer version, then read the changelogs of those that touch event loops, frames, annotations, pickling or C extensions. Upgrade those first.
After deploying, check docker ps for restarting containers (docker inspect --format '{{.RestartCount}}') and check Sentry for new issues.
This time's smoke test (after the fixes) passed for all 27 entry points. Remaining warnings, to follow up:
ResourceWarning: unclosed event loop on shutdown of every async worker. Fixed: yapw 0.3.0 closes the event loop that Pika creates (open-contracting/yapw@05e7361), and the applications use yapw 0.3.0.
datetime.utcfromtimestamp() is deprecated: pytz 2021.1 and python-dateutil 2.8.2 (data-registry). Fixed: upgraded to pytz 2026.5 and python-dateutil 3.0.0.
SyntaxWarning: "\s" is an invalid escape sequence: django-bootstrap3 24.2 (cove-oc4ids). Fixed: upgraded to django-bootstrap3 26.3.
asyncio.iscoroutinefunction is deprecated (removed in 3.16): sentry-sdk 2.63.0 (digitalbuying, kingfisher-collect). Fixed: upgraded to sentry-sdk 2.73.0.
Activity
[-]Python 3.10 end-of-life (EOL)[/-][+]Python 3.10 end-of-life (EOL) (2025-10-31)[/+][-]Python 3.10 end-of-life (EOL) (2025-10-31)[/-][+]Python 3.10 end-of-life (EOL) (2026-10-31)[/+]jpmckinney commentedon Feb 10, 2026
For a few applications, related: open-contracting/deploy#602
jpmckinney commentedon Jul 24, 2026
If we upgrade past Python 3.12, we can replace see if we can replace
os.walkwithPath.rglob. When doing so, we should check if it's paired with e.g..endswith("*.extension")logic, which we can roll into the glob.jpmckinney commentedon Oct 28, 2026
Plan
Searched each sibling repository's default branch with the regex above, plus CI matrices (
'3.10', '3.11'),requires-python, and version-conditional code.1. Packages: require Python 3.11 or later
Drop the classifier and matrix entry, set
requires-python = ">=3.11", apply Ruff fixes, add a changelog entry.requires-python, so maturin's--find-interpreterstops building cp310 wheels)datetime.UTC)requires-python)StrEnum)Statebranch and CI step;typing.NamedTuple)2. Shared tooling and templates
tomlifallback. Merge after yapw#64.3. Applications on 3.10
All three run on kingfisher-main, in a virtualenv built from the server's default Python (3.10 on Ubuntu 22.04). Separate the apps' Python from the OS (see open-contracting/deploy#602). These first moved to 3.13, which was wrongly taken to be Ubuntu 26.04's version; it's 3.14 (see section 5):
python:versionpillar for kingfisher-main to 3.13 (deadsnakes PPA) and droppython{version}-distutils. After deploying, remove the four.vedirectories and deploy again (see the PR's deploy steps).fromisoformat()workaround)[tool.uv.pip] python-version = "3.10"fails the deploy'suv pip syncon kingfisher-main)4. Docker applications to 3.13
These were on 3.11, below the Ubuntu 26.04 LTS version, which is 3.14 (not 3.13, as first written). Docker images don't depend on the server, so these can be upgraded now. Each updates
.python-version(which CI reads), the Dockerfiles and the contributing docs:os.walk→Path.walk)os.walk→Path.walk)python_version(3.11 → 3.13), and the docs' examples5. Applications to 3.14
The handbook's minimum for applications is the latest Ubuntu LTS's Python: 3.14 in Ubuntu 26.04 (0d70430 also rewords the policy, and moves cookiecutter-django and the docs' examples to 3.14). The PRs also upgrade compiled packages without 3.14 wheels.
python:versionto 3.14. Merge after kingfisher-collect, kingfisher-summarize and collect-generic.Deferred
os.walk→Path.rglob(comment above) applies once packages require 3.12+, not to this upgrade.🤖 Generated with Claude Code
jpmckinney commentedon Oct 31, 2026
Post-upgrade checks (do these next time, before deploying)
The test suites passed on 3.14, but two runtime failures reached production after deploying:
asyncio.get_event_loop(), which raises on Python 3.14 if no loop is set. Every yapwAsync/AsyncConsumerworker crashed on startup (data-registry, kingfisher-process, pelican-backend, pelican-frontend). No test starts an async consumer, so CI didn't catch it. Fixed by upgrading to pika 1.4.4.copy(frame.f_locals); PEP 667), so unhandled exceptions weren't reported. A module-level exception still reports, so a trivial test passes. Fixed by upgrading to sentry-sdk 2.73.0.Checklist:
salt/docker_apps/files/*.yaml(workers, management commands, andgunicorn core.wsgi). Use a fresh environment, a throwawayRABBIT_EXCHANGE_NAME(to not consume development messages),PYTHONWARNINGS=default, andSENTRY_DSNpointing at a local HTTP sink. Each worker must stay up, then exit cleanly on SIGINT; each web server must serve a request.docker psfor restarting containers (docker inspect --format '{{.RestartCount}}') and check Sentry for new issues.This time's smoke test (after the fixes) passed for all 27 entry points. Remaining warnings, to follow up:
ResourceWarning: unclosed event loopon shutdown of every async worker. Fixed: yapw 0.3.0 closes the event loop that Pika creates (open-contracting/yapw@05e7361), and the applications use yapw 0.3.0.datetime.utcfromtimestamp()is deprecated: pytz 2021.1 and python-dateutil 2.8.2 (data-registry). Fixed: upgraded to pytz 2026.5 and python-dateutil 3.0.0.SyntaxWarning: "\s" is an invalid escape sequence: django-bootstrap3 24.2 (cove-oc4ids). Fixed: upgraded to django-bootstrap3 26.3.asyncio.iscoroutinefunctionis deprecated (removed in 3.16): sentry-sdk 2.63.0 (digitalbuying, kingfisher-collect). Fixed: upgraded to sentry-sdk 2.73.0.ResourceWarning: <psycopg.Connection ...> was deleted while still openon shutdown of pelican-backend'sdata_itemanddatasetworkers. Fixed: pelican-backend closes all threads' database connections when the consumer stops (open-contracting/pelican-backend@24760b4, open-contracting/pelican-backend@1c3949a).Smoke test script
🤖 Generated with Claude Code