ADR-013 — Pure-stdlib Python, reaffirmed on corrected evidence¶
Status: Accepted
Date: 2026-08-29
Supersedes: ADR-001
Deciders: stakeholder, implementer
Context¶
ADR-001 chose pure-stdlib Python but justified it with two claims that were never measured. Challenged on the point — hand-rolling a standard file format is a custom API and a novel bug surface for a solved problem — the claims were tested directly against the live Fusion 2704.1.53 install. Both were wrong.
What was measured¶
Vendoring numpy works. numpy 2.5.2 ships cp314 wheels for every target
platform. Unpacked onto sys.path inside Fusion, it imports and computes correctly,
at speed indistinguishable from stock CPython (400×400 linalg.solve: 1.6 ms inside
Fusion, 1.2 ms on system Python). ADR-001’s “not available and will not be vendored”
was false on feasibility.
“Pure Python” libraries are not dependency-free. Measured full install trees:
Library |
Installed size |
Transitive dependencies |
|---|---|---|
|
56 MB unpacked, per platform |
— (compiled, ABI-pinned to cp314) |
|
70 MB |
numpy, fontTools, pyparsing, typing_extensions |
|
37 MB (22 MB without Pillow) |
fontTools, Pillow (compiled), defusedxml |
|
26 MB |
Pillow (compiled), charset_normalizer |
|
4 MB |
none |
|
1 MB |
none |
Both PDF libraries drag in Pillow, which is compiled — reinstating the per-platform
wheel matrix. ezdxf drags in numpy, so “use a DXF library” is the numpy question.
Fusion’s interpreter is ~5x slower than stock CPython on identical pure-Python code (PRD §11.12). numpy sidesteps this penalty because it executes native code. Measured inside Fusion on the Tier E relaxation, numpy gives ~11x, moving the 2 s ceiling from ~15 000 to ~180 000 triangles.
The hand-written replacements are small and verifiable. Prototyped and validated:
Module |
Code lines |
Validated against |
|---|---|---|
|
123 |
numpy, thousands of random cases, worst error 2.8e-12 |
|
66 |
pypdf; rendered by macOS CoreGraphics; 1:1 verified |
|
31 |
|
Deducting the vector primitives (written regardless, since the core exchanges plain tuples across the adapter boundary per G6) and the relaxation algorithm itself (written either way), the surface numpy would actually replace is about 90 lines.
Decision¶
Pure Python standard library, unchanged. The decision stands; the reasoning does not.
Per format:
numpy — do not vendor. Not because it is infeasible, but because 56 MB × 3 platforms pinned to the cp314 ABI buys headroom above a ceiling the workload does not approach. A realistic template patch is ~3 000 triangles → 0.394 s against a 2 s budget.
DXF — hand-write. 31 lines against 70 MB that reintroduces numpy.
PDF — hand-write, with mandated guards. 66 lines, but with required test coverage for the failure classes a library would have handled: non-ASCII text encoding (
Ø, German component names), xref byte offsets, and multi-page tiling.SVG — genuinely marginal.
svgwriteis 1 MB with zero dependencies; hand-writing is ~40 lines. Either is defensible; hand-write for consistency.
Third-party libraries are used as dev-only test oracles. This captures the correctness assurance a library provides while shipping zero bytes, and is mandatory for every hand-written implementation (PRD §14.3).
Consequences¶
The oracle requirement is load-bearing, not decorative. It has already paid: the numpy cross-check caught a silent overflow in the symmetric-eigen Jacobi rotation (
theta * thetaoverflowing for a near-circular section — exactly the Ø 12.50 mm end of the reference chainstay, PRD §11.10), which would have produced garbage principal axes with no error raised.Because the oracle is what makes hand-rolling defensible, a hand-written component without an independent oracle is not acceptable. If a future format cannot be validated against an independent implementation, prefer the library and accept the vendoring cost.
The 300 ms preview budget (FR-9.6) no longer has comfortable margin: 3 000 triangles takes 394 ms in Fusion. The coarse preview grid is promoted from recommendation to requirement.
Revisit if a requirement genuinely exceeds the PRD §6.6.1 envelope. The escape hatch remains vendoring numpy, now known to work; the cost is the per-platform ABI-pinned bundle, not feasibility.