Skip to content

fix: file_api/ast/metadata bug fixes and simplifications#1319

Merged
henryiii merged 4 commits into
mainfrom
fix/fileapi-ast-metadata
Jun 10, 2026
Merged

fix: file_api/ast/metadata bug fixes and simplifications#1319
henryiii merged 4 commits into
mainfrom
fix/fileapi-ast-metadata

Conversation

@henryiii

@henryiii henryiii commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Autogenerated from #1317.

🤖 AI text below 🤖

This fixes a reviewed batch of bugs and simplifications in file_api/, ast/,
and metadata/, each with a regression test. All findings were verified against
the code before fixing; none were skipped.

Fixes

  1. metadata/regex.py + metadata/template.py — wrong superset operator.
    if settings.keys() > KEYS: is a proper-superset check, so it only fired
    when the user supplied all valid keys plus an extra; a typo'd key (e.g.
    removes) passed silently and was ignored. Changed to
    if settings.keys() - KEYS: in both. Added regression tests that a bogus key
    raises.

  2. file_api/reply.py + file_api/_cattrs_converter.py — toolchains
    jsonFile never followed.
    The toolchains-v1 reply is requested and
    modeled, but make_class/the cattrs converter only followed jsonFile for
    {CodeModel, Target, Cache, CMakeFiles}. Toolchains was missing, so the
    populated toolchains-v1-*.json was never read and silently structured into
    an empty Toolchains stub. Added Toolchains to the jsonFile-following set
    in both converters; strengthened test_included_dir to assert the actual
    toolchain content loads (and that both converters agree).

  3. file_api/model/codemodel.pyLink.commandFragments had no default.
    Per the CMake File API spec it's optional (sibling Archive.commandFragments
    already defaults), so a target whose link object omitted it failed the whole
    codemodel conversion. Gave it the same default_factory=list. Added a test
    structuring a minimal link dict without commandFragments.

  4. ast/ast.py — comments leaked into argument values. The
    argument-accumulation loop appended COMMENT / BRACKET_COMMENT token values,
    so cmake_minimum_required(VERSION 3.15 # why not\n) yielded value
    "3.15 # why not", which flowed into Version(...)/SpecifierSet(...) and
    raised uncaught InvalidVersion during settings parsing. Comment tokens are
    now skipped; since a bracket comment's regex also swallows leading
    whitespace, it is replaced with a single space to keep tokens separated.
    Added find_min_cmake_version regression tests with in-parens comments
    (including a leading bracket comment).

  5. ast/ast.py + settings/skbuild_read_settings.py — opaque traceback on
    malformed CMakeLists.
    An unterminated block made contents[-1] raise a
    bare IndexError, and the missing-open-paren case raised AssertionError;
    the call site only caught FileNotFoundError, so a CMakeLists.txt the parser
    chokes on produced an opaque traceback instead of the graceful fallback the
    surrounding code anticipates. Added a dedicated ParseError raised by the
    ast module and caught at the find_min_cmake_version call site, which now
    falls through to the existing warning path. Added a settings test.

  6. metadata/__init__.pyauthors/maintainers validation crashed on bad
    input.
    The _LIST_DICT_FIELDS check called .items() on each list element
    before checking it was a dict, so ["string"] raised AttributeError
    instead of the intended RuntimeError; the message also said "dictionary of
    strings" but should be "list of dictionaries of strings". Both fixed. Also
    tightened the optional-dependencies check to verify list elements are
    strings. Added regression tests.

  7. ast/tokenizer.py — dead LEGACY regex alternative removed. The second
    alternative (|"(?:[^"\\]*(?:\\.[^"\\]*)*)*") was unreachable: the preceding
    QUOTED alternative matches a strict superset at every position. Removed it;
    ast tests confirm no behavior change.

  8. file_api/reply.py — simplified if origin is not None and origin is list: to if origin is list:.

Tests

uv run pytest tests/test_cmake_ast.py tests/test_fileapi.py tests/test_dynamic_metadata_unit.py tests/test_skbuild_settings.py tests/test_dynamic_metadata.py tests/test_auto.py -q -n auto131 passed.

prek -a --quiet: ruff, ruff-format, and mypy pass. (The check-sdist hook
flags only pre-existing untracked uv.lock / .claude/settings.local.json
worktree artifacts, unrelated to this change.)

🤖 Generated with Claude Code

@henryiii henryiii marked this pull request as draft June 10, 2026 04:46
henryiii added 4 commits June 10, 2026 14:48
The argument-accumulation loop appended COMMENT and BRACKET_COMMENT token
values into the argument string, so a trailing comment inside
`cmake_minimum_required(VERSION 3.15 # why not)` produced the value
`"3.15 # why not"`, which raised an uncaught InvalidVersion during settings
parsing. Comment tokens are now skipped (a bracket comment's regex also
swallows leading whitespace, so it is replaced with a single space to keep
tokens separated).

Malformed input (a missing open paren or an unterminated block) now raises a
dedicated `ParseError` instead of a bare AssertionError/IndexError. The
auto-cmake-version call site in skbuild_read_settings.py catches it and falls
through to the existing graceful warning path with a 3.15 fall-back.

Also remove the unreachable second alternative of the LEGACY tokenizer regex,
which was a strict subset of the preceding QUOTED alternative.

Assisted-by: ClaudeCode:claude-opus-4-8
…gments

The toolchains-v1 reply is requested and modeled, but neither converter
followed its `jsonFile` indirection, so the populated toolchains-v1-*.json was
never read and the index silently structured into an empty `Toolchains` stub.
`Toolchains` is now added to the jsonFile-following set in both the built-in
and cattrs converters, and the test asserts the actual toolchain content loads.

`Link.commandFragments` is optional per the CMake File API spec (its sibling
`Archive.commandFragments` already defaults), but lacked a default, so a target
whose link object omitted it failed the whole codemodel conversion. It now
defaults to an empty list.

Also simplify `if origin is not None and origin is list` to `if origin is list`.

Assisted-by: ClaudeCode:claude-opus-4-8
The regex and template plugins used a proper-superset check
(`settings.keys() > KEYS`), which only fired when the user supplied every valid
key plus an extra; a typo'd key passed silently and was ignored. Switched to
`settings.keys() - KEYS` so any unrecognized key raises.

The `authors`/`maintainers` validation called `.items()` on each list element
before checking it was a dict, so `["string"]` raised AttributeError instead of
the intended RuntimeError; the dict check is now performed first and the message
corrected to "list of dictionaries of strings". The optional-dependencies check
now also verifies list elements are strings.

Assisted-by: ClaudeCode:claude-opus-4-8
CMake < 3.20 does not support the toolchains-v1 File API object, so it
returns an error reply (kind/version/error) with no jsonFile to follow.
The cattrs converter indexed with_path["jsonFile"] directly, raising
KeyError on those versions; structure the inline dict instead, matching
the built-in converter's "jsonFile" in data guard.

Assisted-by: ClaudeCode:claude-opus-4.8
@henryiii henryiii force-pushed the fix/fileapi-ast-metadata branch from 90ba5b7 to 04118f1 Compare June 10, 2026 18:48
@henryiii henryiii marked this pull request as ready for review June 10, 2026 19:15
@henryiii henryiii merged commit 29ed4c8 into main Jun 10, 2026
61 checks passed
@henryiii henryiii deleted the fix/fileapi-ast-metadata branch June 10, 2026 19:17
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