[BUG] Discover validation registry via a generated index module - #663
[BUG] Discover validation registry via a generated index module#663Adam Lastowka (Rachmanin0xFF) wants to merge 4 commits into
Conversation
REGISTRY was built by walking expressions/generated/ with pathlib.Path.rglob, which only sees real filesystem directories. Loaded from a wheel on sys.path (zipimport) instead of extracted -- as AWS Glue does via --extra-py-files -- the generated modules live inside the zip, pathlib finds nothing, and every feature type reports as unregistered. Declare one overture.pyspark_validations entry point per generated module and read them through importlib.metadata, which resolves from dist metadata regardless of how the package is loaded. The table is hand-maintained like the theme packages' overture.models entry points; a new test asserts it matches the generated tree so a forgotten or stale declaration fails CI rather than silently dropping a feature. An entry point whose module is absent is skipped, preserving the empty-registry-on-missing-tree behavior for a build without the generated modules. Signed-off-by: Adam Lastowka <adamlastowka@gmail.com>
🗺️ Schema reference docs preview is live!
Note ♻️ This preview updates automatically with each push to this PR. |
There was a problem hiding this comment.
Pull request overview
Updates the PySpark validation registry discovery mechanism so it works when overture-schema-pyspark is loaded from a wheel on sys.path (zipimport, e.g., AWS Glue), by switching from filesystem walking to importlib.metadata entry-point discovery.
Changes:
- Replace filesystem-based generated-module discovery in
_registry.pywith discovery via theoverture.pyspark_validationsentry-point group. - Add/adjust tests to (a) assert the registry populates when the generated tree exists and (b) ensure the declared entry-point table matches the generated modules on disk.
- Add
overture.pyspark_validationsentry points topyproject.tomland document the behavior change in the changelog.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/overture-schema-pyspark/src/overture/schema/pyspark/_registry.py | Switch registry population from filesystem walking to entry-point-driven discovery. |
| packages/overture-schema-pyspark/tests/test_registry.py | Update/extend tests to validate entry-point-based discovery and prevent drift vs generated tree. |
| packages/overture-schema-pyspark/pyproject.toml | Declare one entry point per generated validation module (overture.pyspark_validations). |
| packages/overture-schema-pyspark/changelog.d/661.bugfix.md | Document the fix for zipimport / AWS Glue registry discovery. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Two review fixes on the entry-point discovery: Catching ModuleNotFoundError unconditionally would also swallow a real missing dependency inside a present generated module, silently dropping that validation -- the same empty-registry failure this change exists to prevent. Skip only when the generated module (or an ancestor namespace) is absent; re-raise when a present module fails to import. importlib.metadata.entry_points(group=...) returns entries from every installed distribution, so a foreign package declaring the same group could inject validations or skew the drift test. Filter to this distribution's own entry points, shared by the registry and the test. Signed-off-by: Adam Lastowka <adamlastowka@gmail.com>
docformat (D401/D404) rejects a docstring first line that opens with "This"; lead with the verb instead. Signed-off-by: Adam Lastowka <adamlastowka@gmail.com>
Replace the entry-point table approach with a codegen-emitted `_index` module. `make generate-pyspark` now writes an `_index.py` alongside the validation modules that imports each one and exposes them as `MODULES`; the runtime registry imports that index instead of walking the generated tree on disk. An ordinary import resolves whether the package is installed to a directory or loaded as a wheel on sys.path (zipimport), which is what AWS Glue does via --extra-py-files and where the on-disk walk found nothing. This drops the hand-maintained entry-point table entirely: the index is generated from the same specs as the modules, so it cannot drift, and the registry-building policy stays in _registry.py rather than in package metadata. A test asserts the index covers exactly the modules on disk, guarding the codegen step. The index is a plain `_index.py`, not an `__init__.py`, so the generated tree stays PEP 420. A build without the generated tree has no index and yields an empty registry; a present-but-broken module raises instead of being silently skipped. Signed-off-by: Adam Lastowka <adamlastowka@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (2)
packages/overture-schema-pyspark/src/overture/schema/pyspark/_registry.py:60
- Modules that import successfully but are missing
ENTRY_POINTorMODEL_VALIDATIONare currently skipped silently. That can cause validations to be dropped without any signal, which is especially hard to diagnose in production (and contradicts the module docstring’s “never dropped without notice” claim).
if entry_point is None or validation is None:
continue
packages/overture-schema-pyspark/tests/test_registry.py:51
- This PR’s core regression risk is zipimport behavior (raw wheel on
sys.path), but the updated tests only exercise the on-disk case. Consider adding a regression test that loads the generatedexpressions/generatedtree from a zip onsys.path(without an extracted directory) and assertsREGISTRYpopulates, so the original Glue failure mode is covered automatically.
def test_registry_discovers_generated_models() -> None:
"""The registry finds generated modules through the index."""
if not _generated_module_names():
pytest.skip("generated tree not present; run `make generate-pyspark`")
generated_entries = [
key for key in REGISTRY if ":" in key and key.startswith("overture.schema.")
]
assert generated_entries, "registry found no generated feature modules"
Fixes #661
Note
This is one of two alternative fixes for #661. The other, #662, keeps discovery on the filesystem and teaches the walk to read inside a zip. Merging either one resolves the issue.
What
Same bug as #662:
REGISTRYcomes back empty on AWS Glue, so every feature type silently reports as unregistered and the task erroneously succeeds 🫠. This happens whenoverture-schema-pysparkis loaded as a raw.whlonsys.path(Glue's--extra-py-files) instead of installed to a real directory.Why
_registry.pydiscovered generated modules by walkingexpressions/generated/withpathlib.Path(root_path).rglob("*.py"), which works... only when a real filesystem directory exists! When the package is a raw wheel onsys.path, those modules live inside the zip, andpathlibcan't traverse into one.Fix
We stop discovering by filesystem.
make generate-pysparknow emits an_index.pyalongside the validation modules that imports each one and exposes them asMODULES, and the registry imports that index. An ordinary import resolves whether the package is a directory or a wheel onsys.path. The generated modules were always importable under zipimport; only enumerating them by walking the tree was broken.The index is generated from the same specs as the modules. The registry-building policy stays in
_registry.py; codegen only decides which modules exist. There's also a test asserts that the index covers exactly the modules on disk, guarding the codegen step.Approach vs #662
pathlib-plus-zipfilewalking that has to know every way the package might be loaded.MODULES", zipimport-safe for free, at the cost of a small addition to the codegen (one extra emitted module).An earlier revision of this PR used a hand-maintained entry-point table; that was replaced with the codegen-emitted index so nothing has to be kept in sync by hand.
Verification
sys.pathwithout extracting it (matching Glue's--extra-py-files): all 15 generated modules resolve; previouslylen(REGISTRY) == 0. Confirmed_index.pyships in the wheel.make checkpasses (mypy, lint, docformat, full suite across the codegen and pyspark packages).Yet-untested on Glue!