Skip to content

CI: stop recompiling pyo3 on every run (pin PYO3_CONFIG_FILE in tests sessions)#14968

Closed
alex wants to merge 3 commits into
mainfrom
claude/magical-hopper-1R1Vj-build-isolation
Closed

CI: stop recompiling pyo3 on every run (pin PYO3_CONFIG_FILE in tests sessions)#14968
alex wants to merge 3 commits into
mainfrom
claude/magical-hopper-1R1Vj-build-isolation

Conversation

@alex
Copy link
Copy Markdown
Member

@alex alex commented Jun 7, 2026

What

In the tests* nox sessions, resolve the PyO3 build config once — against the session venv's interpreter, whose path is stable from run to run — and pin it via PYO3_CONFIG_FILE for the build of cryptography itself. Build isolation stays fully enabled. Bumps the shared rust cache key so caches get re-saved with the new fingerprints.

(Earlier revision of this PR used --no-build-isolation; reworked per review feedback.)

Why

CI logs show every run recompiles pyo3-build-config, pyo3-ffi, pyo3, pyo3-macros-backend, and pyo3-macros (and on Windows, openssl-sys/openssl too) despite a rust-cache hit — e.g. this main run.

Mechanism: PEP 517 builds run in a randomly-named temporary environment (~/.cache/uv/builds-v0/.tmpXXXX), and maturin points PyO3 at that environment's interpreter via PYO3_PYTHON. PyO3's build scripts emit cargo:rerun-if-env-changed for the env vars they actually consult, and the resolved config embeds the interpreter path — so a different temp path every run dirties the whole chain.

PYO3_CONFIG_FILE takes priority over PYO3_PYTHON: when it's set, PyO3 never consults (and therefore never fingerprints) the volatile variables. We generate the config using pyo3's own discovery (PYO3_PRINT_CONFIG=1 against the session interpreter), so it's correct across CPython/PyPy/free-threaded/Windows without hand-rolling sysconfig logic.

Three details to make this work:

  • the config file's name is content-addressed — a genuinely different config changes the PYO3_CONFIG_FILE value and correctly triggers a rebuild;
  • its mtime is pinned — a regenerated-but-identical file (every fresh CI run) doesn't trip cargo:rerun-if-changed, which compares mtimes;
  • ext_suffix and abiflags are appended: maturin also reads PYO3_CONFIG_FILE as the authoritative target description — it requires ext_suffix on Windows and uses abiflags for the wheel tag (without it, free-threaded wheels get tagged cp314 instead of cp314t and the installer rejects them). PyO3 ignores unknown config keys with a warning, so appending both is safe.

Verified locally

With a warm target/, removing .nox entirely and re-running nox -e tests-nocoverage --install-only:

  • before: Compiling pyo3-build-config / pyo3-ffi / pyo3-macros-backend / pyo3 / pyo3-macros + workspace crates, every time
  • after: Finished release [optimized] target(s) in 0.04s — zero recompiles

Also verified that churning PYO3_PYTHON/VIRTUAL_ENV across fake temp envs with PYO3_CONFIG_FILE set recompiles nothing in the pyo3 chain.

Expected CI impact

Same as measured for the previous revision of this PR (warm run vs baseline): windows build step 1m48s → ~1m04s, the critical-path windows (3.9, tests-nocoverage) job 6.4m → ~4.1m; linux saves the CPU of the pyo3 chain. The first run on each revision is cold (key bump); the following push demonstrates warm behavior.

Behavior notes

  • Generation runs via session.run_install, so it executes during nox --install-only (CI's "create env" step) and is skipped under --no-install (CI's "tests" step, where the build already exists).
  • The PYO3_PRINT_CONFIG cargo invocation is the documented mechanism for producing config files and costs a few seconds (cached dev-profile artifacts).
  • nox -e flake passes locally.

https://claude.ai/code/session_014StKTjk7GBcVdiWKimEsQb


Generated by Claude Code

@alex alex force-pushed the claude/magical-hopper-1R1Vj-build-isolation branch 2 times, most recently from 64a9214 to b94f4cf Compare June 7, 2026 05:16
PEP 517 builds run in a randomly-named temporary environment, and
PyO3's build script fingerprints the interpreter path it is given by
maturin. That path changes on every CI run, which makes cargo recompile
pyo3-build-config, pyo3-ffi, pyo3, pyo3-macros-backend, and pyo3-macros
(and on Windows openssl-sys/openssl too) despite a warm rust-cache.

Instead of disabling build isolation, resolve the PyO3 build config
once per session -- against the session venv's interpreter, whose path
is stable from run to run -- using pyo3's own discovery
(PYO3_PRINT_CONFIG), and pin it via PYO3_CONFIG_FILE. PYO3_CONFIG_FILE
takes priority over the PYO3_PYTHON that maturin points at the
ephemeral build environment, so cached pyo3 artifacts stay fresh while
the build itself remains fully isolated.

The config file's name is content-addressed and its mtime pinned so
that a regenerated-but-identical config doesn't trip cargo's
rerun-if-changed, while a genuinely different config changes the
PYO3_CONFIG_FILE value and correctly triggers a rebuild.

The cache key is bumped so caches are re-saved with the new
fingerprints (rust-cache does not re-save on a primary key hit).

https://claude.ai/code/session_014StKTjk7GBcVdiWKimEsQb
@alex alex force-pushed the claude/magical-hopper-1R1Vj-build-isolation branch from b94f4cf to 67f3427 Compare June 7, 2026 05:17
@alex alex changed the title CI: stop recompiling pyo3 on every run (build tests sessions without isolation) CI: stop recompiling pyo3 on every run (pin PYO3_CONFIG_FILE in tests sessions) Jun 7, 2026
claude added 2 commits June 7, 2026 05:23
maturin also reads PYO3_CONFIG_FILE and treats it as the authoritative
description of the target interpreter: it requires ext_suffix on
Windows, and without abiflags it tags free-threaded wheels as regular
CPython (cp314 instead of cp314t), which the installer then rejects.
PyO3 itself ignores unknown config keys with a warning, so appending
both is safe.

https://claude.ai/code/session_014StKTjk7GBcVdiWKimEsQb
When PYO3_CONFIG_FILE is set, maturin doesn't export PYO3_PYTHON to
cargo, so cryptography-cffi's build script fell back to 'python3' to
run _cffi_src/build_openssl.py. On Windows that resolved to an
interpreter without cffi (venvs there don't ship python3.exe), breaking
all Windows jobs.

Point PYO3_PYTHON at the session venv's interpreter -- a stable path,
so no cache churn -- and install the build requirements into the
session venv so that interpreter can run the cffi bridge script. The
build itself still runs in an isolated environment.

https://claude.ai/code/session_014StKTjk7GBcVdiWKimEsQb
Copy link
Copy Markdown
Member Author

alex commented Jun 7, 2026

Closing in favor of the upstream fix: maturin will export a PYO3_BASE_PYTHON pointing at the stable base interpreter path, and pyo3-build-config will prefer it when resolving the interpreter config — giving stable cargo fingerprints under PEP 517 build isolation without any downstream workarounds. The diagnosis and measurements in this PR (and the maturin PYO3_CONFIG_FILE edge cases it surfaced: ext_suffix required on Windows, abiflags driving the free-threaded wheel tag, PYO3_PYTHON not exported when a config file is present) fed into that design.


Generated by Claude Code

@alex alex closed this Jun 7, 2026
@alex alex deleted the claude/magical-hopper-1R1Vj-build-isolation branch June 7, 2026 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants