Skip to content

Commit dafbf5a

Browse files
committed
gotchas: add 612, importlib.metadata shadowing on egg-info
A same-run importlib.metadata check can resolve a checkout's leftover <pkg>.egg-info instead of the installed wheel's dist-info, reading as a missing licence file with no exception (the pyscf case). Confirmed with a minimal dummy wheel that auditwheel repair itself preserves dist-info/licenses/ and its RECORD entries fine.
1 parent de694ea commit dafbf5a

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

‎skills/python-project-porting/references/gotchas-index.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,12 @@ The porting gotchas (551 of them) live in [`references/gotchas/`](gotchas/), spl
11711171
riscv64: find the polluter with a teardown hook and deselect it; a collected demo script
11721172
arming a repeating `SIGALRM` kills pytest at exit (142) only on riscv64's slower shutdown;
11731173
put the venv's platlib on `PYTHONPATH` for `sys._base_executable` helpers (the scalene case).
1174+
- **612** — A same-run `importlib.metadata` check shadows to a checkout's leftover
1175+
`<pkg>.egg-info/` instead of the installed wheel, not just `import <pkg>` (extends gotcha
1176+
25): a licence-file assertion via `importlib.metadata.files('<pkg>')` reads as an empty
1177+
set with no exception, because `python -c` puts the checkout root's stale egg-info ahead
1178+
of site-packages on `sys.path` — not because `auditwheel repair` dropped anything (the
1179+
pyscf case).
11741180

11751181
### Testing: pytest config, servers & test selection — [`gotchas/pytest-config-servers-and-selection.md`](gotchas/pytest-config-servers-and-selection.md)
11761182

‎skills/python-project-porting/references/gotchas/testing-and-shadowing.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/testing-and-shadowing.
3737
wraps `sys.executable` in a profiler launcher and every later subprocess test silently skips
3838
or times out; a collected demo script can leave a repeating `SIGALRM` that kills pytest at
3939
exit; `sys._base_executable` helpers escape the test venv.
40+
- **612** — A same-run `importlib.metadata` check can shadow to a checkout's leftover
41+
`egg-info` instead of the installed wheel, not just `import <pkg>` (extends gotcha 25).
4042

4143
---
4244

@@ -643,3 +645,35 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/testing-and-shadowing.
643645
elsewhere with a warning. With the memory tests skipping, the patch adding `riscv64`
644646
looked unnecessary; with the polluter isolated they fail without it and pass with it.
645647
648+
612. **A same-run `importlib.metadata` check can shadow to a checkout's leftover `egg-info`
649+
instead of the installed wheel, not just `import <pkg>` (extends gotcha 25; the pyscf
650+
case).** Gotcha 25's shadowing is about `import <pkg>` resolving to the source tree
651+
because cwd is on `sys.path[0]`. The same trigger breaks a *metadata* check that never
652+
imports the package at all: a build step's `pip wheel . --no-clean` (or any PEP 517
653+
`build_wheel` invoked against a local directory — build isolation only isolates the
654+
build *environment*, not where setuptools' `egg_info` command writes) leaves
655+
`<pkg>.egg-info/` sitting in the checkout root, and that checkout root is the very
656+
directory a *later, separate* `docker run -w <checkout>` reuses as cwd for the test
657+
step. `python -c "..."` prepends `''` (cwd) to `sys.path` unconditionally, so
658+
`importlib.metadata.distribution('<pkg>')`/`.files('<pkg>')` resolves the stale
659+
`egg-info` there before ever consulting the properly `pip install`ed `*.dist-info` in
660+
site-packages — and classic egg-info metadata predates PEP 639, so it has no
661+
`licenses/` subdirectory at all. A licence-file assertion built the gotcha-44 way
662+
(`{p.name for p in importlib.metadata.files('<pkg>') if '.dist-info/licenses/' in
663+
str(p)}`) then asserts against an empty set with no exception raised — reads exactly
664+
like the licence files never made it into the wheel, when the wheel (and its
665+
`auditwheel`-repaired copy, RECORD included) is fine.
666+
- **Verify by relocating, not by inspecting auditwheel.** `auditwheel repair` was the
667+
obvious suspect (it does rewrite `RECORD` and retag the wheel) but is not the culprit
668+
here: repairing a minimal dummy wheel with a `licenses/*` glob and inspecting the
669+
output directly shows the `dist-info/licenses/` files and their `RECORD` entries
670+
untouched. The tell that it's shadowing instead: `importlib.metadata.distribution(
671+
'<pkg>')._path` names the local `egg-info`, not the site-packages `dist-info`, and
672+
the file list it returns includes checkout-only names like `setup.py`/`pyproject.toml`
673+
that the installed wheel never ships.
674+
- **Fix by moving the check past a `cd` to a directory with no `<pkg>` on disk** — `/tmp`
675+
inside the test container is fresh per `docker run` and already the convention this
676+
same script uses one step later, before `import pyscf`, for the plainer gotcha-25
677+
reason. Run the metadata check *after* that `cd`, not before it: writing the check
678+
first (chronologically, before the later `import <pkg>` smoke test was added) is why
679+
it alone stayed exposed.

0 commit comments

Comments
 (0)