Repository navigation
Conversation
…ption `extension search` and `extension info` indexed a catalog entry's name, version and description directly, so an entry without one of them made both commands exit with a KeyError, and search never listed the entries after it. Catalog entries are not validated field by field on purpose, and `extension update` already skips a versionless entry (github#5108). Read the three fields with .get(): `unknown` for a missing version (as `extension add` and installed-extension info show it), the extension ID for a missing name, and an empty description. Assisted-by: Codex CLI (model: gpt-6.1-sol, autonomous) Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The changes address missing catalog metadata with safe fallbacks while preserving search behavior and Rich escaping, alongside regression coverage for incomplete entries.
No open issues 🎉
What changed in this PR
Updates specify extension search and specify extension info to handle catalog entries missing name, version, or description without raising KeyError.
Changes:
- Added fallbacks for missing metadata: extension ID for names,
unknownfor versions, and an empty description. - Preserved Rich markup escaping and search filtering behavior.
- Added cached catalog fixtures and parameterized CLI regression tests.
| File | Description |
|---|---|
tests/specify_cli/extensions/test_command_search.py |
Verifies incomplete entries display correctly and filtering continues to work. |
tests/specify_cli/extensions/test_command_info.py |
Verifies incomplete and complete catalog entries display successfully. |
tests/specify_cli/extensions/conftest.py |
Adds cached catalog fixtures covering missing fields and Rich markup. |
src/specify_cli/extensions/command_search.py |
Safely renders incomplete catalog metadata during search. |
src/specify_cli/extensions/command_info.py |
Safely renders incomplete catalog metadata in extension details. |
Review effort: Lite (auto)
4 of 5 tasks
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.
Description
specify extension searchandspecify extension infoindexed a catalog entry'sname,versionanddescriptiondirectly, so an entry missing one of them made both commands exit 1 with aKeyError(#5307). Search stopped at that entry, so the extensions after it were never listed. Catalog entries are kept without per-field validation on purpose (_get_merged_extensions: "one malformed entry shouldn't poison an otherwise valid catalog"), and #5108 madeextension updateskip a versionless entry for the same reason.The two display paths now read those fields with
.get(), using the fallbacks the extensions code already has:unknownfor a missing version (as inextension addandinfofor an installed extension), the extension ID for a missing name, and an empty description (assearch()already treats it when filtering). Values are still escaped for Rich as before.Repro with the real CLI: a project whose cached catalog holds one complete entry (
healthy) and one entry (example) missing a single field. Steps are in #5307.exampleis missingmain(c438494)versionextension searchKeyError: 'version'Example (vunknown)andHealthy (v2.0.0)versionextension info exampleKeyError: 'version'Example (vunknown)nameextension searchKeyError: 'name'example (v1.0.0)andHealthy (v2.0.0)nameextension info exampleKeyError: 'name'example (v1.0.0)descriptionextension searchKeyError: 'description'descriptionextension info exampleKeyError: 'description'Fixes #5307
Testing
uv run specify --helpuv sync && uv run pytestA new fixture in
tests/specify_cli/extensions/conftest.py,incomplete_catalog_entry, writes a fresh catalog cache with the incomplete entry and a complete sibling, and fails the test if the catalog is fetched from the network. It runs four cases: no version, no name, no description, and a version holding Rich markup.TestExtensionSearchCLI.test_search_shows_incomplete_catalog_entry: unfiltered search lists both entries with the expected headers, andsearch Healthystill filters the incomplete one out.TestExtensionInfoCLI.test_info_shows_incomplete_catalog_entry:info exampleandinfo healthyboth exit 0 with the expected headers.With
command_search.pyandcommand_info.pyreverted tomain, the three missing-field cases of each test fail (6 of 8). The markup cases pass onmaintoo, since escaping already worked.Full suite on Linux, Python 3.13: 13961 passed, 449 skipped, 1 failed (
pytest -n 2, 7 min). The failure,test_version_execution.py::TestWindowsUvToolEnvironment::test_non_windows_uv_tool_dry_run_still_previews_installer, fails the same way onmainhere: Rich wraps the dry-run output, and my checkout's venv path is long enough to be split mid-path. Ruff 0.15.0 (ruff check src tests) passes. I haven't run it on Windows.AI Disclosure
AI disclosure: Codex CLI (model: gpt-6.1-sol, reasoning effort xhigh, autonomous, offline sandbox) found the missing-
versioncrash and wrote the first fix and tests. Claude Code (model: claude-opus-5-5, autonomous) extended the fix and tests tonameanddescription, reran the repro with the real CLI onmainand the branch, ran the full suite and lint, and drafted this description. The commit carriesAssisted-by:trailers for both.