Resolve package spec directories dynamically instead of hardcoded maps - #2987
Conversation
find_package.py used hand-maintained package_mappings/reverse_mappings/ lowercase_packages tables to guess a PyPI project name's on-disk spec directory. These tables silently broke whenever a mapping was missing or stale: - aiohttp-socks reverse-mapped to a nonexistent 'aiohttp_socks' (underscore) directory, so it was silently dropped from every automated update run. This let python-socks get bumped to 3.0.0 while aiohttp-socks stayed on 0.10.1 (which pins python-socks<3.0.0), breaking repoclosure on PR #2973. - galaxy-importer and importlib-resources reverse-mapped to stale underscored directory names after those directories were renamed to hyphenated form; same silent failure. - The forward-transformed name written to packages-to-update.txt didn't always match the on-disk directory suffix used by update_packages.sh's own (unmapped) "packages/python-$pkg/python-$pkg.spec" template, e.g. poetry-core would have failed at the actual bump step. Replace all of this with a single directory index built from the real packages/python-*/ tree, matched via PEP 503-style canonicalization (lowercase, collapse -_. into a single separator). This works for all 272 packages currently in the repo with no hand-maintained table, and self-adapts as packages are added, renamed, or removed. Rewrote the test suite to validate resolution against the real packages/ tree (catching the three bugs above) instead of only testing the old cosmetic name transform in isolation.
zjhuntin
left a comment
There was a problem hiding this comment.
Excellent refactoring that replaces fragile hardcoded mappings with robust dynamic directory resolution.
Key improvements:
- Fixes real bugs (aiohttp-socks, galaxy-importer, importlib-resources)
- Eliminates maintenance burden of hardcoded mapping tables
- Self-adapting as packages are added/renamed/removed
- Comprehensive test coverage (21 tests, validates against real packages/ tree)
- Correct use of PEP 503 canonicalization
Minor optimization suggestion:
Consider building the directory index once in main() rather than rebuilding on every resolve_package_dir() call when processing large requirements files. Not a blocker, but would eliminate redundant filesystem globs.
Code quality is excellent - clean, well-tested, well-documented. Ready to merge.
Addresses review feedback on #2987: resolve_package_dir() rebuilt the full packages/python-*/ glob on every call, which is wasted filesystem work when processing a full requirements list. Build it once in main()/build_package_list() and thread it through.
|
Thanks for the review! Addressed the index-rebuild suggestion in fa2928a — |
|
Nice refactor — I verified the core claim independently: all 272 current Two prefix-handling gaps I'd fix before merge (both one-liners):
Silent-failure hardening (the failure mode in all of these is a green no-op run):
Cleanups (take or leave):
|
Two real bugs flagged in review: - strip_python_prefix() ran before canonicalization and only matched the literal 'python-' spelling, so an underscore-spelled freeze line like 'python_socks' would fail to resolve at all (canonicalize now runs first). - resolve_package_dir() stripped 'python-' unconditionally with no exact-match check first, so a distinct PyPI project 'python-foo' could collapse onto an unrelated 'foo' directory (e.g. the real 'gnupg' vs. 'python-gnupg' PyPI projects). Exact canonical match is now tried before falling back to the stripped form. Additional hardening from the same review: - build_directory_index() now raises on two directories canonicalizing to the same key, instead of silently taking filesystem-order-dependent last-glob-wins (this repo has renamed directories between separator styles before: galaxy_importer -> galaxy-importer). - main() exits loudly if the packages/ directory index comes back empty (e.g. run from the wrong cwd), instead of silently no-op'ing every package as "not found" and exiting 0. - parse_package_list() skips and warns on unparseable requirements lines (VCS/URL/editable deps) instead of crashing the whole run. - Removed build_package_list(), which had no callers anywhere in the repo. - build_matrix.py now imports parse_package_list from find_package.py instead of carrying an identical copy. Not addressed here (flagged as follow-up, out of scope for this PR): - update_packages.sh has no way to detect a wrong/stale directory suffix at bump time short of rpmspec/rpmdev-vercmp failing silently under `set +e`. - update-pulp-packages.yml (the dependabot-merge bump path) calls build_matrix.py directly on a raw automation/requirements.txt diff, bypassing find_package.py's resolution entirely -- same class of bug could exist there under a different trigger. - Workflow trigger paths (test-find-package.yml, find-script-test.yml) don't include packages/** -- pushing workflow file changes needs the 'workflow' OAuth scope, which this session's token doesn't have.
|
Thanks for the thorough review — genuinely caught two real bugs. Addressed in 46072fb: Fixed:
Deferred (flagged, not fixed here):
CI is green (27 tests now, up from 21). |
ogajduse
left a comment
There was a problem hiding this comment.
ACK — thanks for the quick turnaround.
Verified 46072fb against each point of the review: all seven fixes check out in the code, each one carries a named regression test, and the suite passes locally for me as well (27 tests). The exact-match-first ordering and the canonicalize-before-strip change both resolve the cases from the review (python_socks, gnupg vs python-gnupg), and I confirmed the new skip-warning on stdout is harmless since build_matrix.py writes its matrix to $GITHUB_OUTPUT, not stdout.
The deferrals make sense to me. On your question: yes, please open a tracking issue — one issue covering all three follow-ups is fine (the update_packages.sh suffix-contract enforcement, the packages/** workflow path filters, and the update-pulp-packages.yml raw-diff bypass), so they don't get lost after this merges. The path-filter change needs someone with the workflow scope anyway, so having it written down is the main thing.
|
Opened #2988 tracking all three follow-ups (update_packages.sh suffix-contract enforcement, packages/** workflow path filters, update-pulp-packages.yml raw-diff bypass). |
Addresses review feedback on #2987: resolve_package_dir() rebuilt the full packages/python-*/ glob on every call, which is wasted filesystem work when processing a full requirements list. Build it once in main()/build_package_list() and thread it through.
Summary
find_package.pyused hand-maintainedpackage_mappings/reverse_mappings/lowercase_packagestables to guess a PyPI project name's on-disk spec directory. These tables silently break
whenever an entry is missing or goes stale:
aiohttp_socks(underscore) directory, so itwas silently dropped from every automated update run. This let
python-socksget bumped to3.0.0 while
aiohttp-socksstayed on 0.10.1 (which pinspython-socks<3.0.0), breakingrepoclosure on the python-socks 3.0.0 bump PR.
names after those directories were renamed to hyphenated form; same silent failure.
packages-to-update.txtdidn't always match theon-disk directory suffix used by
update_packages.sh's own (unmapped)packages/python-$pkg/python-$pkg.spectemplate — e.g. apoetry-corebump would fail at theactual spec-bump step even though discovery succeeded.
Change
Replaced all of this with a single directory index built from the real
packages/python-*/tree,matched via PEP 503-style canonicalization (lowercase, collapse
-/_/.runs into a singleseparator). This resolves correctly for all 272 packages currently in the repo with no
hand-maintained table, and self-adapts as packages are added, renamed, or removed — no more
manual mapping entries needed going forward.
Rewrote the test suite to validate resolution against the real
packages/tree (which catchesthe three bugs above) instead of only testing the old cosmetic name transform in isolation.
Test plan
pytest test/— 21 tests passpackages/python-*directories resolve correctly via a full sweepaiohttp-socks,galaxy-importer,importlib-resources,poetry-core,psycopg-c,opentelemetry-*,ruamel.yaml*,jaraco.*,et_xmlfile,pyasn1_modules,python-socks/python-dateutil/python-debian/python-gnupg(ownpython-prefix), mixed-case names (PyYAML,GitPython)Test find_package.pyworkflow) — pending