fix(integrations): refuse scaffold keys that shadow a module or name a keyword - #4540
Open
jawwad-ali wants to merge 2 commits into
Open
jawwad-ali wants to merge 2 commits into
jawwad-ali wants to merge 2 commits into
Conversation
…a keyword
`scaffold_integration` validates the key's *shape* (`_KEY_RE`, kebab-case) but
never checks what the derived package name would collide with. Two ordinary
keys produce broken output:
1. A key matching one of this package's own modules. The scaffold creates a
PACKAGE, `integrations/<key>/`, and a package shadows a same-named module in
the same directory:
both 'base.py' and 'base/' present
-> import demopkg.base resolves to: scaffolded package base/
Every integration does `from ..base import MarkdownIntegration`, so
scaffolding a key named `base` would silently redirect all of them to the
empty scaffold. The existing-file guard cannot catch this: it only checks
`<key>/__init__.py`, never `<key>.py`. `base`, `catalog` and `manifest` are
all currently accepted.
2. A reserved Python keyword. `integrations/class/` cannot be named by any
import statement, so the generated package is unreachable. `class`, `import`,
`return` and `lambda` are all currently accepted.
Both are refused up front now, before anything is written.
Soft keywords are deliberately NOT rejected -- they are contextual and
`import match` is valid, so `match`, `case` and `_` remain usable keys. This
was verified rather than assumed:
soft keyword 'match' -> importable? YES iskeyword=False issoftkeyword=True
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The implementation and regression coverage are sound; only a minor documentation correction remains.
Pull request overview
Prevents integration scaffolds from creating invalid or module-shadowing Python packages.
Changes:
- Rejects Python keyword keys and module-name collisions.
- Adds regression and valid-key coverage.
File summaries
| File | Description |
|---|---|
| tests/integrations/test_integration_scaffold.py | Tests rejected collisions, keywords, and accepted keys. |
| src/specify_cli/integration_scaffold.py | Adds keyword and module-collision validation. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
mnriem
requested changes
Sep 23, 2026
mnriem
left a comment
Collaborator
There was a problem hiding this comment.
Please address Copilot feedback and resolve conflicts
…ffold key
Addresses review feedback. The keyword-guard comment said soft keywords
"(``match``, ``case``, ``_``)" are deliberately not rejected. `_` never
reaches this guard: `_clean_key` runs first, and its lowercase kebab-case
pattern (`^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$`) requires a leading letter, so it
rejects `_` outright:
_clean_key('_' ) -> REJECTED by the kebab-case pattern
_clean_key('match') -> ACCEPTED
_clean_key('case' ) -> ACCEPTED
The comment now names only the soft keywords `_clean_key` actually admits. The
PR description is corrected to match. Comment-only; the acceptance test already
covered just `match` and `case`, so it needed no change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
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.
Problem
scaffold_integrationvalidates the key's shape (_KEY_RE, lowercase kebab-case) but never checks what the derived package name would collide with. Two ordinary keys produce broken output.1. A key that shadows one of this package's own modules
The scaffold creates a package —
integrations/<key>/__init__.py— and in Python a package shadows a same-named module in the same directory:Every integration in the repo does
from ..base import MarkdownIntegration/SkillsIntegration, so scaffolding a key namedbasesilently redirects all of them to the empty scaffold.The existing guard cannot catch this — it only checks
<key>/__init__.pyand the test file:integrations/base.pyis never consulted. Currently accepted and collidable:base,catalog,manifest.2. A key that is a reserved Python keyword
integrations/class/cannot be named by any import statement, so the generated package is unreachable and its generated test (from specify_cli.integrations.class import ClassIntegration) is aSyntaxError. Currently accepted:class,import,return,lambda, …Reproduction on current
main(c173bf1)Every one of these passes
_clean_key:Fix
Refuse both up front, before anything is written:
Soft keywords are deliberately allowed
My first cut rejected soft keywords too. I checked rather than assumed, and that would have been wrong — soft keywords are contextual and
import matchis perfectly valid:So the guard uses
keyword.iskeywordonly, andmatch/caseremain usable keys. (_is not a usable key at all:_clean_key's lowercase kebab-case pattern requires a leading letter and rejects it before this guard runs.)Verification
upstream/main→ 25 passed, 1 skipped with the fix.uvx ruff@0.15.0 check src tests→ cleanBehaviour change, disclosed: five key values that previously scaffolded now raise. All five produced output that was broken on arrival — an unimportable package, or one that shadows a module the whole package depends on.
Written with assistance from Claude Code. Bug found, reproduced, and verified by me on current
main.🤖 Generated with Claude Code