paddlepaddle has no Python 3.14 (cp314) wheel on PyPI, so host-agent
deployments on 3.14 can never install it, causing OCR to fail at
runtime with RuntimeError. Pin the workspace to Python 3.13 across
all pyproject.toml files, the Docker base image, and the Jenkins CI
image; regenerate uv.lock against 3.13.
Also fixes a pre-existing Python-2-style `except X, Y:` syntax error
(invalid in all Python 3.x) in runtime/task.py and
packages/cloud-platform/cloud/{sql_repository,internal_api/api}.py,
introduced in 22d37ca9 and unrelated to this change's scope, which
blocked the full test suite from collecting on any interpreter
version.
openspec change: downgrade-python-3-13-paddleocr
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
## Context
|
||||
|
||||
The workspace root and all three `uv` workspace members (`apps/device-host-agent`, `apps/cloud-api`, `packages/cloud-platform`) pin `requires-python = ">=3.14"`. `Dockerfile` and `Jenkinsfile` both build on `astral-sh/uv:python3.14-bookworm-slim`. `paddleocr>=3.0.0` is a hard dependency of the root `device-agent-runtime` project (`pyproject.toml:13`), and `perception/ocr.py::PaddleOCREngine._load()` requires the separate `paddlepaddle` package at runtime to actually run inference — but `paddlepaddle` has no `cp314` wheel on PyPI (verified against the PyPI project page and `PaddlePaddle/PaddleOCR#17370`), so no Python-3.14 environment in this project can ever install it. This has already caused a production task failure on a real host-agent device. `docs/MACOS_IPHONE_SETUP.md:156-158` already flagged this exact tension and required it be resolved as a deliberate, validated workspace-wide change rather than an ad hoc per-host downgrade — this design is that change.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Make `paddlepaddle` installable (and therefore OCR functional) on every environment that follows this project's documented setup, by moving the supported Python version to one PaddlePaddle actually ships wheels for.
|
||||
- Keep the four workspace `pyproject.toml` files, `uv.lock`, `Dockerfile`, and `Jenkinsfile` mutually consistent on Python 3.13.
|
||||
- Preserve all existing runtime behavior — this is a toolchain change, not a feature change. No application code logic should need to change to satisfy this proposal (only the interpreter/dependency-resolution target).
|
||||
|
||||
**Non-Goals:**
|
||||
- Not declaring `paddlepaddle` as a project dependency in `pyproject.toml`/`uv.lock`. Per the earlier decision in this incident, installing `paddlepaddle` itself remains a manual, per-host operational step (`uv pip install paddlepaddle`), independent of this change. This proposal only removes the upstream blocker that made that manual install impossible; it does not automate the install.
|
||||
- Not hardening `perception/ocr.py::run_ocr()` to catch `RuntimeError` from the OCR engine. That was raised and explicitly declined as a separate concern during incident triage — a task can still fail today if `paddlepaddle` is missing on a 3.13 host, just for a legitimate reason (nobody installed it) rather than an impossible one (no wheel exists).
|
||||
- Not adding a Python version support matrix / CI test matrix across multiple Python versions. This project has always targeted a single pinned version; this change moves that single pin from 3.14 to 3.13, it does not introduce multi-version support.
|
||||
- Not touching any application-level requirement/behavior — no `openspec/specs/*` capability changes accompany this change (see proposal.md Capabilities section).
|
||||
|
||||
## Decisions
|
||||
|
||||
**D1: Target `>=3.13,<3.14` (not a bare `>=3.13`).**
|
||||
An open-ended `>=3.13` would silently re-admit 3.14 (and any future 3.15+) the moment a contributor's local `uv` picks a newer interpreter, reintroducing this exact bug with no warning. Pinning `<3.14` forces `uv sync`/`uv lock` to fail loudly if someone's environment tries to resolve against an unsupported interpreter, and makes the reason (paddlepaddle wheel availability) discoverable by grepping for the upper bound. When PaddlePaddle eventually ships `cp314` wheels, lifting this bound is a deliberate, reviewable one-line change rather than an implicit drift.
|
||||
|
||||
**D2: Change all four `pyproject.toml` files together, in one change.**
|
||||
`uv` workspaces require a single resolved environment shared across all members (one `uv.lock`, one `.venv`) — `apps/cloud-api`, `apps/device-host-agent`, and `packages/cloud-platform` cannot each pin a different Python floor without breaking `uv sync --all-packages`. Alternatives considered: downgrading only `apps/device-host-agent` (the only member that actually touches OCR) — rejected, because `uv` workspace resolution is root-driven and per-member `requires-python` values are intersected, not independently honored; leaving the others at `>=3.14` while the root moves to `<3.14` would make the workspace intersection empty and `uv sync` would fail outright.
|
||||
|
||||
**D3: Re-run `uv lock` from scratch against Python 3.13 rather than hand-editing `uv.lock`.**
|
||||
`uv.lock` embeds resolution metadata (wheel tags, hashes) tied to the target Python version; hand-editing it to swap version markers risks producing an internally inconsistent lockfile that `uv sync --locked` would reject or, worse, silently misresolve. A clean `uv lock` re-resolution is the only way to get a trustworthy lockfile, and lets the resolver naturally surface any dependency that (unlike `paddleocr`) turns out to have its own 3.13-vs-3.14 constraint conflicts.
|
||||
|
||||
**D4: Treat the Python-3.14-only-syntax audit as a verification task, not a design-time judgment call.**
|
||||
A preliminary grep for known 3.14-only constructs (PEP 750 t-strings, `annotationlib`, `compression.zstd`) found no usage — but the only reliable check is actually running the full non-integration test suite under a real 3.13 interpreter, since static grepping cannot catch every possible 3.14-only stdlib call. `tasks.md` makes this an explicit, checked step rather than assuming the grep was sufficient.
|
||||
|
||||
**D5: No `openspec/specs/*` capability delta.**
|
||||
This change alters the supported Python interpreter version and dependency resolution, not any capability's observable request/response behavior, protocol, or user-facing requirement. Every existing capability spec (`host-agent-protocol`, `perception-provider`, etc.) continues to describe the system correctly after this change. Consistent with treating this as a pure infrastructure/toolchain change (see proposal.md Capabilities section, which lists none).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Risk] Some other pinned dependency in `uv.lock` might not have resolved the same way (or at all) for cp313 as it did for cp314** → Mitigation: `uv lock` will surface any resolution failure explicitly at lock time (task 2), and the full non-integration test suite must pass against the regenerated lock before this change is considered complete (task 5).
|
||||
- **[Risk] Any contributor or deployment target still holding a Python 3.14 interpreter/venv will have builds/CI break the moment this merges** → Mitigation: this is the intended, accepted breaking change (called out as **BREAKING** in proposal.md); `docs/MACOS_IPHONE_SETUP.md` is updated in the same change to tell operators to install Python 3.13 instead, and the Docker/Jenkins base images are updated in lockstep so CI and containerized deployments do not need a separate manual step.
|
||||
- **[Risk] Undiscovered 3.14-only syntax somewhere in the ~200+ Python files** → Mitigation: D4 — full test suite run under 3.13 is a required, verified task, not an assumption.
|
||||
- **[Trade-off] This still does not make OCR work out of the box** — `paddlepaddle` remains an unlisted, manual dependency per the prior decision (Non-Goals). Every host still needs a manual `uv pip install paddlepaddle` after this change, and that install will still be wiped by a future `uv sync --locked` (a previously accepted and documented risk, now finally actionable since a compatible wheel will exist). This trade-off was deliberately kept out of scope to keep this change focused on the single blocking issue (wheel availability), not dependency-declaration policy.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Update `requires-python` in all four `pyproject.toml` files to `>=3.13,<3.14`.
|
||||
2. Update `Dockerfile` and `Jenkinsfile` base images to `astral-sh/uv:python3.13-bookworm-slim`.
|
||||
3. `uv python install 3.13`, then `uv lock` to regenerate `uv.lock` against the new interpreter.
|
||||
4. `uv sync --locked --all-packages` to provision a 3.13 `.venv` from the new lock.
|
||||
5. Run the full non-integration test suite (`uv run --all-packages pytest -m "not integration"`) and fix any failures caused by the interpreter change (expected: none, per D4).
|
||||
6. Update `docs/MACOS_IPHONE_SETUP.md` to reflect the new 3.13 baseline and remove the now-obsolete "do not downgrade" warning.
|
||||
7. No code rollback path is meaningfully separate from a `git revert` of this change — reverting restores the 3.14 pin and the pre-existing (already-broken) OCR situation, which is a safe rollback target since it is today's status quo.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- None blocking. If PaddlePaddle later ships a `cp314` wheel, lifting the `<3.14` bound (D1) is a natural, separately-reviewable follow-up, not something this change needs to anticipate further than the explicit upper bound already makes discoverable.
|
||||
Reference in New Issue
Block a user