Skip to content

emrg: packaging: disable library validation on bundled python so pip C extensions load (journal R2 scipy blocked) - #967

Merged
argszero merged 1 commit into
argszero:masterfrom
how2how2how2-arch:feature/python-disable-library-validation
Aug 24, 2026
Merged

emrg: packaging: disable library validation on bundled python so pip C extensions load (journal R2 scipy blocked)#967
argszero merged 1 commit into
argszero:masterfrom
how2how2how2-arch:feature/python-disable-library-validation

Conversation

@how2how2how2-arch

Copy link
Copy Markdown
Contributor

Fixes pip-installed C extensions failing to load under the bundled Python (blocks journal R2 scipy ILP even after the #966 venv fix).

Symptom (observed by the silicon-science-cs journal task, 2026-08-25)

pip-installed C extensions fail to import in the bundled python:

mypy(mypyc .so)、pytype(msgspec .so)、numpy/scipy 全部失败 — "mapping process and mapped file have different Team IDs"

Even with a working venv (#966), import numpy / import scipy fails — research tasks cannot install/use any C-extension dependency.

Root cause

packaging/make-installer.sh re-signs all runtime Mach-O binaries (required for notarization) with:

codesign --force --timestamp --options runtime --sign "$SIGN_ID" "$BIN"

--options runtime = hardened runtime. With hardened runtime and no com.apple.security.cs.disable-library-validation entitlement, macOS enforces library validation: the main executable may only load libraries signed by the same Team ID (or Apple). PyPI wheels are unsigned/ad-hoc → "different Team IDs" on every dlopen.

Reproduced locally on the real install bundle (negative + positive states):

stateresult
bundled python (runtime, TeamID, no entitlement) + unsigned .soBLOCKED: "mapping process and mapped file have different Team IDs"
system python3 (no TeamID) + same .soloads fine
bundled python re-signed + runtime + disable-library-validationLOADED OK

Fix

  1. packaging/assets/python-entitlements.plist (new): com.apple.security.cs.disable-library-validation = true — the standard plugin-loading entitlement; notarization accepts it.
  2. packaging/make-installer.sh: apply --entitlements only to python* binaries in the codesign loop (the interpreter is the process that loads plugins; .so/.dylib entries don't need it), then fail-loud: assert the signed python3.1X carries the entitlement, exit 1 otherwise (prevents silent regression).

The entitlement is on the interpreter itself, so venv pythons (symlink to the same binary) inherit it automatically. Linux/Windows unaffected (block is macOS-only).

Verification

  • Both states proven on the real bundle (table above); re-signed copy shows entitlement via codesign -d --entitlements and loads the unsigned dylib.
  • bash -n packaging/make-installer.sh clean; the test.yml emrg.iss heredoc render step unaffected.
  • pytest tests/: 1050 passed + 1 skipped; import check + emrg --help OK.

Host self-check (after next install): codesign -d --entitlements - ~/.emrg/install/bin/python-dist/bin/python3.13 | grep disable-library-validation should match; then python -m venv .v && .v/bin/pip install numpy && .v/bin/python -c "import numpy" works.

…so pip C extensions load (rant: journal R2 scipy ILP blocked)

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260825-060822 (1/3)

Independent review of head f6ff0f4 passed:

  • Root cause sound: --options runtime (hardened runtime) + no com.apple.security.cs.disable-library-validation entitlement → macOS library validation blocks unsigned pip C extensions ("different Team IDs"). Standard, well-understood macOS behavior; the entitlement is the canonical plugin-loading allowance and is accepted by notarization.
  • Fix scope correct: entitlement applied only to python* Mach-O binaries inside the codesign loop (python3.13 matches; non-Mach-O wrappers like python-config are filtered out by the file/Mach-O grep). Venv pythons are symlinks to the same binary → inherit automatically.
  • Fail-loud guard solid: the assertion lives outside the while read subshell, so exit 1 genuinely aborts; it greps the codesign -d --entitlements -output (not exit code), so a missing entitlement is reliably detected in both positive and negative states.
  • $ROOT is defined (line 25) before the loop uses it; bash -n clean; plist is valid XML (plutil OK).
  • CI green: test + test-windows both pass (run 32780579817); the change is macOS-only and does not touch the .iss render step.

No issues found.

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260825-065036 (2/3)

Re-review: head f6ff0f4 unchanged since LGTM 1/3 (060822); merge state CLEAN/MERGEABLE; CI green (test + test-windows, run 32780579817). Diff reviewed again — entitlement scope (python* only), fail-loud assertion outside the subshell, plist validity, and $ROOT usage all confirmed. No issues found.

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260825-065657 (3/3)

Final review: head f6ff0f4 unchanged, merge state CLEAN/MERGEABLE, CI green (test + test-windows, run 32780579817). Three consecutive LGTMs from distinct cycles (060822 → 065036 → 065657), no ❌ in between. Merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@how2how2how2-arch@argszero