Files
demo-oidc/docs/superpowers/specs/2026-07-08-python-3.14-deps-update-design.md
T

4.0 KiB

Python 3.14 + Dependency Update — Design

Date: 2026-07-08 Status: Approved Author: brainstorming session

Goal

Update the project to run on Python 3.14 and bring all dependencies up to recent stable versions. No functional changes to application behavior.

Background

  • The local interpreter is already Python 3.14.6 (/opt/homebrew/bin/python3.14).
  • Dockerfile pins python:3.9-alpine and hardcodes /usr/local/lib/python3.9/site-packages in the final stage — a latent bug that breaks on any version bump.
  • python-jose is declared in requirements.txt but never imported anywhere in the codebase (verified by grep across all *.py). It is effectively unmaintained (last release 3.5.0).
  • main.py already runs fine under pydantic v2 and recent FastAPI on the local 3.14 venv; no code changes are required for the version bumps.
  • The repo contains pre-existing duplicate trees (code/, source/) that are out of scope.

Scope

In scope:

  • requirements.txt
  • requirements-test.txt
  • Dockerfile
  • New .python-version file

Out of scope:

  • Any code changes to main.py, generate_favicon.py, or templates.
  • Type-hint modernization (e.g. Optional[X]X | None). Not caused by this change.
  • Removal of unused imports in main.py (HTTPException, Header, StaticFiles). Pre-existing dead code.
  • The code/ and source/ duplicate directories.
  • Switching the Docker base image from alpine to slim. Considered and rejected — alpine is the existing convention and keeps the diff minimal.

Decisions

Dependency specifier strategy

Keep the existing >= lower-bound convention, but bump floors to recent stable majors. Rationale: matches existing convention, allows automatic patch/minor updates, avoids the maintenance burden of exact pinning.

python-jose removal

Drop python-jose from requirements.txt. Rationale: unused (no imports anywhere), unmaintained, reduces install time and attack surface. All JWT operations use pyjwt.

Dockerfile base image

python:3.9-alpinepython:3.14-alpine. Keeps the alpine + multi-stage + non-root-user convention. Only changes that are forced by the version bump will be touched (base image tag + the two hardcoded python3.9 path references).

Changes

1. requirements.txt

Replace contents with:

fastapi>=0.115.0
uvicorn>=0.30.0
pyjwt>=2.9.0
requests>=2.32.0
cryptography>=44.0.0

(python-jose line removed.)

2. requirements-test.txt

Replace contents with:

pytest>=8.3.0
pytest-cov>=5.0.0
coverage>=7.4.0
httpx>=0.27.0
asgi_lifespan>=2.0.0

3. Dockerfile

Two surgical edits:

  • Line 2: FROM python:3.9-alpine AS python-baseFROM python:3.14-alpine AS python-base
  • Line 32: COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packagespython3.14 in both path segments.

Everything else in the Dockerfile stays unchanged: alpine packages (curl, gcc musl-dev libffi-dev openssl-dev), non-root user, port, healthcheck, CMD.

4. .python-version (new file)

Contents: 3.14

Purpose: signals the intended interpreter to pyenv-aware tooling and documents the target version for local development.

Verification

Success criteria — all must pass:

  1. Venv install: Recreate venv/ with python3.14 -m venv venv, pip install -r requirements.txt -r requirements-test.txt exits 0.
  2. Test suite: ./run_tests.sh exits 0 with the same (or better) coverage as before. No new failures.
  3. Docker build (if available): docker build -t oidc-validator . exits 0.

Risks

  • cryptography build from source on alpine: mitigated by the existing gcc musl-dev libffi-dev openssl-dev build-deps line. cryptography 44+ ships musllinux wheels for 3.14, so a wheel should be available; if not, the build deps cover it.
  • New dependency majors changing behavior: FastAPI 0.115 and pydantic v2 are already running locally without issue; no code changes observed as necessary. If tests surface breakage, it will be caught by the verification step and addressed then.