[BUG] Discover validation registry through importlib.resources - #666
Open
Seth Fitzsimmons (sethfitz) wants to merge 2 commits into
Open
[BUG] Discover validation registry through importlib.resources#666Seth Fitzsimmons (sethfitz) wants to merge 2 commits into
Seth Fitzsimmons (sethfitz) wants to merge 2 commits into
Conversation
`_registry` walked the generated tree with `pathlib.Path.rglob`, which finds nothing when the package is zipimported from a wheel placed on `sys.path` -- what AWS Glue does with `--extra-py-files`. The namespace portion's `__path__` then points inside the archive, `rglob` returns no matches, and the registry comes back empty without raising, so every feature type reports as unregistered and validation silently does nothing. Read the tree through `importlib.resources.files` instead. It resolves a namespace portion whether that portion is a directory or an archive member, and multiplexes every portion, so a tree assembled from more than one distribution is still walked whole. `files()` raises `NotADirectoryError` on a namespace package with any non-directory portion through Python 3.12; the `importlib_resources` backport carries the 3.13 fix, so it is a dependency below 3.13. Glue 4.0 runs Python 3.10 and Glue 5.0 runs 3.11, so on Glue the backport is always the code path taken. Discovery now takes the root package name rather than a list of portion paths, which lets `_walk` drop its own import of the root -- `files()` performs it -- and lets the tests point the walk at a synthetic package. Verified end to end: a wheel on `sys.path`, never unpacked, yields 15 registry entries where the previous implementation yields 0. Signed-off-by: Seth Fitzsimmons <seth@mojodna.net>
Seth Fitzsimmons (sethfitz)
temporarily deployed
to
staging
August 17, 2026 20:58 — with
GitHub Actions
Inactive
🗺️ Schema reference docs preview is live!
Note ♻️ This preview updates automatically with each push to this PR. |
`--extra-py-files` is Glue's job parameter, not Spark's -- `spark-submit` takes `--py-files` and knows nothing by that name. Naming Glue as the cause made a general property read like a vendor quirk: any wheel left unextracted on `sys.path` is zipimported, which is what Spark's `--py-files` and `SparkContext.addPyFile` produce, and what a bare `PYTHONPATH` entry produces with no Spark involved at all. Lead with the mechanism and keep Glue as an instance of it. The version gate's comment still names the Glue runtimes below the 3.13 stdlib fix, since those are evidence for the gate rather than an attribution, and now invites readers to add other runtimes they find below it. Signed-off-by: Seth Fitzsimmons <seth@mojodna.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #661
Note
A third alternative for #661, alongside #662 (teach the filesystem walk to read inside a zip) and #663 (replace discovery with a codegen-emitted index). Merging any one resolves the issue.
What
overture.schema.pyspark._registry.REGISTRYcomes back empty whenoverture-schema-pysparkis loaded from a wheel placed onsys.pathrather than installed to a real directory, which is what AWS Glue does with--extra-py-files. Every feature type then reports as unregistered.Why
_registrywalked the generated tree withpathlib.Path.rglob. Under zipimport the namespace portion's__path__entry points inside the archive,pathlibcannot traverse there, andrglobreturns no matches without raising — so the empty registry is indistinguishable from the documented "generated tree absent" case.Fix
Read the tree through
importlib.resources.files, which resolves a namespace portion whether that portion is a directory on disk or a member of an archive. This is the API the stdlib provides for exactly this problem, so discovery no longer has to know how the package was loaded: any loader exposing a resource reader works, rather than only the ones the walk was taught about.files()raisesNotADirectoryErroron a namespace package with any non-directory portion through Python 3.12, which is why it isn't usable as-is (as #661 notes). CPython fixed it in 3.13 and theimportlib_resourcesbackport carries the same fix from 6.2, so the backport is a dependency below 3.13 and evaporates when the floor rises. Glue 4.0 runs Python 3.10 and Glue 5.0 runs Python 3.11, so on Glue the backport is always the code path taken.Two things this preserves that are worth naming.
files()multiplexes every portion of the namespace, so a generated tree assembled from more than one installed distribution is still walked whole — the plural-__path__behaviour the previous implementation had. And a missingexpressions/generated/subtree still yields an empty registry rather than raising.Discovery now takes the root package name instead of a list of portion paths. That lets
_walkdrop its ownimport_moduleof the root (files()performs it) and lets the tests point the walk at a synthetic package onsys.path.Verification
End to end, against a real wheel that is never unpacked — the deployment shape, not a simulation of it:
Control arm, same harness and same wheel shape built from
main:registry entries (UNFIXED code): 0. The check distinguishes fixed from unfixed rather than merely reporting a number.Also confirmed the wheel carries the PEP 420 generated tree (15 modules, zero
__init__.py), andmake checkpasses — full suite, mypy, lint, docformat.On the tests
Three tests are added; one of them is the gate.
test_iter_generated_module_names_reads_a_zipimported_treebuilds a wheel-shaped zip, puts it onsys.path, and walks the resulting namespace portion — so the portion string under test comes from real import machinery rather than being hand-written, which is the coupling the bug turned on. Verified by isolation: with the new signature but apathlibwalk behind it, that test fails and the other two pass.The other two — a directory-backed tree and an absent tree — pass under both implementations. They are behaviour-preservation guards for the rewrite, not proofs of the fix.
The zip fixture writes explicit directory entries, because
zipimportrecognises a namespace portion inside an archive only when the archive carries one, and that is whatuv_buildemits. Omitting them would test a shape no real wheel has.Not addressed
The issue reports that the task "erroneously succeeds".
overture-validatealready exits 1 on an unresolvable feature type, so the silent success is on the consumer side — a caller treatingfeature_type in REGISTRYas "is this one of ours". Worth a separate issue against data-platform. Switching those jobs from--extra-py-filesto--additional-python-moduleswould also sidestep zipimport entirely and let pip resolveclick/overture-schema-systeminstead of shipping them by hand.