fix(extensions): resolve core-command dirs via _assets helpers (#3274)#3287
Merged
mnriem merged 1 commit intoJul 1, 2026
Merged
Conversation
…b#3274) `_load_core_command_names()` computed its candidate command dirs with bespoke `Path(__file__)` arithmetic. The github#3014 move of this module from `specify_cli/extensions.py` to `specify_cli/extensions/__init__.py` pushed the file one directory deeper but left the `.parent` counts unchanged, so both candidates resolved to non-existent paths: wheel -> specify_cli/extensions/core_pack/commands (real: specify_cli/core_pack/commands) source -> src/templates/commands (real: repo-root templates/commands) Neither exists, so every call silently fell through to `_FALLBACK_CORE_COMMAND_NAMES`. Discovery is latent-dead: the fallback happens to equal the real stems today, but the shadowing guard (github#1994) that depends on it now relies on someone hand-editing the fallback on every core-command add/remove (as already happened for `converge`, github#3001). Delegate path resolution to the canonical `_locate_core_pack` / `_repo_root` resolvers in `_assets` — the same ones the presets and bundle loaders use. They are anchored to the package root, so discovery survives future module moves. Add regression tests that point the resolvers at a temp tree with *different* command names, proving discovery reads from disk rather than returning the fallback (they fail on the pre-fix code). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes core-command template discovery for extensions by switching _load_core_command_names() to use the canonical asset path resolvers, preventing future module moves from silently breaking discovery and desyncing the anti-shadowing guard.
Changes:
- Resolve core command directories via
_assets._locate_core_pack()(wheel) and_assets._repo_root()(source checkout) instead ofPath(__file__)parent-counting. - Add regression tests that prove discovery reads from disk (and that fallback behavior only triggers when nothing is found).
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/extensions/__init__.py |
Fixes core command discovery by delegating path resolution to _assets helpers and handling missing candidates cleanly. |
tests/test_extensions.py |
Adds targeted tests that distinguish a live loader from a “dead” loader returning the baked-in fallback. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Low
Collaborator
|
Thank you! |
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.
Closes #3274.
Problem
specify_cli.extensions._load_core_command_names()never discovers the bundled command templates. It computed its candidate command dirs with bespokePath(__file__)arithmetic that was correct when the code lived atsrc/specify_cli/extensions.py, but the #3014 move into the package (extensions.py→extensions/__init__.py) pushed the file one directory deeper without updating the.parentcounts. Both candidates now resolve to non-existent paths:specify_cli/extensions/core_pack/commandsspecify_cli/core_pack/commandssrc/templates/commandstemplates/commandsNeither exists, so
commands_dir.is_dir()is always false and every call silently returns_FALLBACK_CORE_COMMAND_NAMES.Why it matters
Latent today (the fallback happens to equal the 10 real command stems), but the shadowing guard from #1994 that consumes
CORE_COMMAND_NAMESnow depends entirely on someone hand-editing the fallback whenever a core command is added or removed — which already happened once (convergewas manually appended in #3001). A future add/remove that forgets the fallback would silently desync the guard.This is the same off-by-one class @mnriem fixed for the presets loader, but in the extensions module.
Fix
Delegate path resolution to the canonical
_locate_core_pack/_repo_rootresolvers in_assets— the same ones the presets and bundle loaders already use. They are anchored to the package root, so discovery survives future module moves. (_assetsis stdlib-only with zero internal imports, so no cycle risk.)Tests
The existing
test_core_command_names_match_bundled_templatescan't catch this — the fallback equals the live tree, so a dead loader and a working one look identical. Added three tests that point the resolvers at a temp tree with different command names, proving discovery reads from disk:test_load_core_command_names_discovers_from_source_checkouttest_load_core_command_names_prefers_wheel_core_packtest_load_core_command_names_falls_back_when_nothing_foundThe first two fail on the pre-fix code (verified by reverting the source change).
🤖 Generated with Claude Code