Skip to content

fix(presets): reject falsy non-mapping catalog config shapes - #4320

Open
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/preset-catalog-config-falsy-shape
Open

fix(presets): reject falsy non-mapping catalog config shapes#4320
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/preset-catalog-config-falsy-shape

Conversation

@Noor-ul-ain001

Copy link
Copy Markdown
Contributor

Summary

PresetCatalog._load_catalog_config (src/specify_cli/presets/__init__.py) has two instances of the same "emptiness check runs before the shape check" bug, both of which mask a corrupted preset-catalogs.yml as an empty/no-op config instead of raising a clear error:

  1. Top level: data = yaml.safe_load(config_path.read_text(...)) or {} coerces a FALSY non-mapping document ([], false, 0, '') to {} before the isinstance(data, dict) guard runs — silently swallowed as "no config", while a TRUTHY non-mapping (a bare string) correctly raises "expected a mapping at root".
  2. One level down: catalogs_data = data.get("catalogs", []) followed by if not catalogs_data: return None checks emptiness before checking isinstance(catalogs_data, list). A FALSY non-list catalogs: value ({}, '', 0, false) is silently treated as "no catalogs", while a TRUTHY non-list (catalogs: "not-a-list") correctly raises "must be a list".

WorkflowCatalog._load_catalog_config and StepCatalog._load_catalog_config (src/specify_cli/workflows/catalog.py) already guard against exactly both cases, with explanatory comments (# Do NOT coerce with or {} here... / # Same asymmetry ... one nesting level down). This preset sibling wasn't updated to match. Fix reuses the same pattern and comments.

Test plan

  • Added test_load_catalog_config_rejects_falsy_non_mapping_root (parametrized over [], false, 0, '') and test_load_catalog_config_rejects_falsy_non_list_catalogs (parametrized over catalogs: {}, '', 0, false) to TestPresetCatalogMultiCatalog.
  • Verified all 8 new parametrized cases fail without the fix (stashed only the source change) and pass with it.
  • Ran TestPresetCatalogMultiCatalog in full — 33 passed, no regressions.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FW9fAYsCBCAgdKWovtSyqt

@Noor-ul-ain001
Noor-ul-ain001 requested a review from mnriem as a code owner August 25, 2026 14:53
`PresetCatalog._load_catalog_config` had two "shape check runs after
an emptiness check" bugs, both masking a corrupted preset-catalogs.yml
as an empty/no-op config instead of raising:

- Top level: `yaml.safe_load(...) or {}` coerced a FALSY non-mapping
  document (`[]`, `false`, `0`, `''`) to `{}` before the
  `isinstance(data, dict)` guard ran, so it was silently treated as
  "no config" — while a TRUTHY non-mapping (a bare string) already
  raised "expected a mapping at root".
- One level down: `catalogs_data = data.get("catalogs", [])` followed
  by `if not catalogs_data: return None` ran the emptiness check
  *before* the `isinstance(catalogs_data, list)` check, so a FALSY
  non-list `catalogs:` value (`{}`, `''`, `0`, `false`) was silently
  swallowed as "no catalogs" — while a TRUTHY non-list
  (`catalogs: "not-a-list"`) already raised "must be a list".

`WorkflowCatalog._load_catalog_config` and
`StepCatalog._load_catalog_config` (workflows/catalog.py) already
guard against both cases correctly, with the same explanatory
comments reused here. This preset sibling was missed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FW9fAYsCBCAgdKWovtSyqt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant