Skip to content

Give the build.toml reader a well-defined API - #123

Merged
tchristiansen-aquaveo merged 32 commits into
masterfrom
toml-refactor
Aug 31, 2026
Merged

Give the build.toml reader a well-defined API#123
tchristiansen-aquaveo merged 32 commits into
masterfrom
toml-refactor

Conversation

@tchristiansen-aquaveo

Copy link
Copy Markdown
Contributor

The various tools all read the build.toml into a plain dict, then do their own validation and extraction on it. Some of them hand-roll their own readers and/or forget to validate. Validation is piecemeal and some tools will accept things others won't, even though they should all work on the same file. The expected content of the build.toml is also spread out all over the place.

This centralizes reading build.toml in one place and converts it into a dataclass before handing it off to anything else. That ensures everything reads and validates it the same way. It also gives all the tools a well-defined API to the file, with all its contents and defaults explicitly spelled out so they know exactly what is (and isn't) there.

Only basic validation is centralized at this point. The reader checks for low level problems like unknown keys, but doesn't handle high-level things like making sure there's at least one valid build.

tchristiansen-aquaveo and others added 26 commits August 28, 2026 10:40
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The module only reads build.toml now, so the name should say so instead of
suggesting it also handles ~/.xmsconan.toml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
load_build_filter, ci_python_versions, and ci_filter_effects now take the
typed BuildToml instead of the raw parsed dict, so they can stop reaching for
filter/matrix/ci defaults by hand. The two callers (gen, ci) convert the
loaded toml_data right after validating it and pass the dataclass through;
everything else in those callers stays dict-based for now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One-shot reader that runs load_toml, validate_top_level_keys, and
toml_to_dataclass in sequence and names the file on parse errors, plus
read_optional_build_toml for the VS2019 driver's missing-file case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the load_toml / validate_top_level_keys / toml_to_dataclass trio
with a single read_build_toml call at every read site. vs2019_build's
_library_build_toml collapses to read_optional_build_toml, since the
reader now names the file itself on parse errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
load_toml, validate_top_level_keys, toml_to_dataclass, and KNOWN_KEYS were
only ever meant as stepping stones toward read_build_toml and
read_optional_build_toml. Now that every caller goes through those two
functions, rename the internals to underscore-private names so the public
surface of build_toml.py is just the two readers and the four dataclasses.

Delete test_build_toml_fields_match_known_keys: once _KNOWN_KEYS is defined
as frozenset(f.name for f in fields(BuildToml)), that test compares a set
against its own definition and can never fail. The real guard is
test_known_keys_matches_the_documented_option_table, which now derives its
expected key set from dataclasses.fields(BuildToml) instead of importing
the constant, and still compares against docs/USAGE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update docs/USAGE.md's build.toml intro: drop the stale setdefault
mention (there is no setdefault in the reader anymore) and note that
the [ci] table's key/type check now runs in every tool that reads the
file, not only xmsconan ci. Note in the xms_dependencies row that each
entry must be a table with name and version, with any other key
rejected rather than ignored. Reword three test docstrings that still
referenced the now-private KNOWN_KEYS constant to describe what the
tests actually compare: the BuildToml field set against USAGE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
read_build_toml's bare except FileNotFoundError: raise was a no-op --
FileNotFoundError is an OSError, not a ValueError, so it never hit the
except ValueError clause it sat beside. Drop it; the docstring already
documents that FileNotFoundError propagates. Add one-line contract
docstrings to the four private helpers that lacked them. Make a
non-numeric [coverage] threshold name its file and key instead of
raising the bare float() message. Rename render_template_with_toml's
toml_data local to context to match what it holds after
_render_context runs, and drop a comment that only restated the next
line. Annotate ci_python_versions's return as Optional[list] since it
returns None for an empty python_versions with no platform overrides.
Soften _library_build_toml's docstring: [ci] validation errors do not
name the file, only parse errors do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build_toml imported validate_ci_table from ci_options, so ci_options could
only name BuildToml under a TYPE_CHECKING guard -- the one such guard in
the package. The key/type table now lives in build_toml next to CiTable,
which also lets [ci] errors name the file like every other reader error.
ci_options keeps only the wheel-repair decision and imports normally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Where one call fed another directly -- read then parse, resolve then
check -- the value now gets a name first. _warn_filter_conflicts also
stops importing its own module's _resolve_coverage_python_version back
through coverage_generator; the function is defined a few screens up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tests built a BuildToml inline inside the call under test; the fixture
now gets a name first. The tests also str()-wrapped every tmp_path only
because the callees were annotated str while forwarding to functions
that already take a Path, so the callees now say str | Path and the
conversions are gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tchristiansen-aquaveo and others added 2 commits August 28, 2026 11:28
The reader checked key names for both sub-tables but coerced or passed
through their values, so `cpp_threshold = true` read as 1.0, a scalar
`filters` string was iterated into one-character gcovr patterns, an
unquoted `python_version = 3.10` became "3.1", and `no_python = "false"`
dropped a dependency from the wheel. Each is now rejected naming the file
and key. The top-level key check reuses _reject_unknown_keys instead of
duplicating it, and the coverage rejection test now fails loudly if the
run ever shells out before validating.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
make_build_toml fed a dict through the private converter, so every test
module depended on a name the reader had declared implementation detail.
The tests now construct BuildToml, CiTable and CoverageTable themselves;
only test_build_toml.py, which tests the converter, still reaches past
the underscore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tchristiansen-aquaveo

Copy link
Copy Markdown
Contributor Author

Review — toml-refactor vs master

Branch: toml-refactor at 0b084e3 (26 commits, 26 ahead / 0 behind origin/master)

Commit
c1d192c Read build.toml through load_toml in publish and docker_run
d5402f1 Validate top-level keys everywhere build.toml is read
36099a1 Rename toml_utils to build_toml
727a506 Add the BuildToml dataclasses
6e5c95a Add toml_to_dataclass for build.toml
10cfc10 Add a BuildToml factory for tests.
ad293ee Read the [filter] inputs from BuildToml
6bed15e Resolve the Windows wheel repair switch from BuildToml
4a1503e Resolve coverage settings from BuildToml
0e7185b Render the generated build files from BuildToml
7c08ae5 Annotate the xms_dependencies parameter with its element type
94d6cb0 Generate CI files from BuildToml
d527eaa Generate profiles from BuildToml
73cbf22 Run coverage from BuildToml
1f1f59e Pick the Docker image from BuildToml
f9ebaac Read build.toml once in publish
b5ed1c7 Read each library's build.toml as BuildToml in the VS2019 driver
f2d4cea Add read_build_toml
83981d8 Read build.toml through read_build_toml everywhere
c284693 Make the build.toml reader's building blocks private
c6e28b4 Document the build.toml checks every reader now makes
4811277 Tidy the build.toml reader after review
60a38e6 Keep the [ci] schema with its dataclass
c367ab3 Name intermediate results instead of nesting calls
5b40308 Name the joined key lists in error messages
0b084e3 Name test fixtures before use and accept Path arguments
 docs/USAGE.md                                    |   6 +-
 tests/test_build_file_generator.py               |  20 +-
 tests/test_build_filter.py                       | 101 ++++---
 tests/test_build_toml.py                         | 239 ++++++++++++++++
 tests/test_ci_options.py                         |  90 +------
 tests/test_coverage_generator.py                 |  56 ++--
 tests/test_docker_run.py                         |   8 +
 tests/test_publish.py                            |  44 +--
 tests/test_utils.py                              |   9 +-
 tests/test_vs2019_build.py                       |  16 +-
 tests/utils.py                                   |  14 +
 xmsconan/build_toml.py                           | 329 +++++++++++++++++++++++
 xmsconan/build_tools/vs2019_build.py             |  40 ++-
 xmsconan/ci_options.py                           | 104 ++-----
 xmsconan/ci_tools/docker_run.py                  |  16 +-
 xmsconan/ci_tools/publish.py                     |  44 ++-
 xmsconan/coverage_tools/coverage_generator.py    |  20 +-
 xmsconan/generator_tools/build_file_generator.py | 107 +++-----
 xmsconan/generator_tools/build_filter.py         |  31 ++-
 xmsconan/generator_tools/ci_file_generator.py    | 126 ++++-----
 xmsconan/generator_tools/profile_generator.py    |  20 +-
 xmsconan/toml_utils.py                           | 102 -------
 22 files changed, 919 insertions(+), 623 deletions(-)

Review: origin/master..HEAD — introduce xmsconan/build_toml.py and read build.toml through one typed reader

What changed. 26 commits, 22 files, +919/-623. The branch replaces xmsconan/toml_utils.py with xmsconan/build_toml.py, which parses build.toml into frozen BuildToml / CiTable / CoverageTable / XmsDependency dataclasses, validates top-level and [ci] keys, and is now the only path every generator, CI tool, coverage runner, and the VS2019 driver uses to read the file. ci_options.py shrinks to one function, the [ci] schema moves next to its dataclass, and a make_build_toml test factory plus a new tests/test_build_toml.py (239 lines) are added. docs/USAGE.md is updated. This is a wide but mechanical refactor with no security-sensitive surface.

Notably, xmsconan/build_toml.py:229-231 guards the [ci] integer keys against TOML booleans with a comment explaining the non-obvious reason (bool subclasses int), so test_shards = true fails loudly instead of becoming 1.

Verdict: BLOCK (C=0 M=0 m=15)

The BLOCK is carried solely by writer-reviewer's discipline verdict: no DESIGN: or EVIDENCE: marker anywhere in the range. The validator dropped both marker findings, reasoning that no tracked or untracked file in this repository defines that convention, and it also dropped both test-smell MAJORs as pre-existing style or test-quality preference. No Critical or Major code finding survives. On code findings alone the state would be APPROVE WITH MINOR ITEMS; if the discipline markers are not a requirement for this project, treat the verdict as that.

Minor items

  • tests/test_build_filter.py:176-184, 189-192 and tests/test_build_toml.py:21-22 — two exercise-verify cycles per test (raise then accept; default then override), so the name describes half of what can fail; split into one test per cycle.
  • tests/test_build_toml.py:25-40, 49-65 — 13 and 15 bare assertions enumerate every default; lines 52-53 and-join two checks and line 54 slips CiTable(linux=False) into a defaults test; compare against an expected-object literal, split line 54.
  • tests/test_build_toml.py:210-211 (also test_coverage_generator.py:1769-1770, test_docker_run.py:293-294, test_publish.py:47-48, test_vs2019_build.py:1361-1363) — the same library_name = "xmscore"\nhas_test_files = true write and unknown-key regex copy-pasted into five modules; add a shared write_build_toml(tmp_path, ...) helper in tests/utils.py.
  • tests/test_coverage_generator.py:1767-1774 — nothing doubles subprocess; if the reader ever stops raising, run_coverage shells out from tmp_path instead of failing cleanly; patch subprocess.run as a guard.
  • tests/utils.py:5,44 (also tests/test_build_toml.py:7) — shared factory and reader tests import private _toml_to_dataclass, _load_toml, _CI_KEY_TYPES, undercutting the "make building blocks private" commit; and because the factory skips _validate_top_level_keys, make_build_toml(bogus=1) raises a dataclass TypeError rather than the reader's ValueError; expose one public dict-to-BuildToml function or route the factory through read_build_toml. (writer-reviewer, test-smell-reviewer)
  • xmsconan/build_toml.py:172_validate_top_level_keys (182-186) duplicates _reject_unknown_keys (215-219) with a slightly different message; call _reject_unknown_keys(toml_data, _KNOWN_KEYS, ...) instead.
  • xmsconan/build_toml.py:245-263_coverage_table checks key names but not the types of filters/excludes; a scalar filters = "xmscore/" passes and list(...) at ci_file_generator.py:193-194 splits it into one-character gcovr filters; require list for both.
  • xmsconan/build_toml.py:251-258float(values[key]) accepts cpp_threshold = true as 1.0, while the [ci] validator at 229-231 deliberately rejects bool-for-int; reject bool before calling float. (writer-reviewer, silent-failure-reviewer)
  • xmsconan/build_toml.py:259-262str(values["python_version"]) turns unquoted python_version = 3.10 (TOML float 3.1) into "3.1", which ci_file_generator.py:145-153 then rejects as a version the user never wrote; reject non-str with an error that names the quoting problem.
  • xmsconan/build_toml.py:266-274_xms_dependency checks key presence but not types; no_python = "false" is a truthy string that pyproject.toml.jinja:19 reads as "exclude", silently dropping a runtime requirement from the wheel; type-check name/version as str and no_python as bool. (writer-reviewer, silent-failure-reviewer)
  • xmsconan/ci_options.py:19-41 — module now holds one 3-line function whose only inputs live on BuildToml; consider a BuildToml property and delete the module.
  • xmsconan/ci_tools/docker_run.py:46 — the resolve_docker_image branch returning config.ci.docker_image has no test; add one that writes [ci] docker_image = "..." to build.toml.
  • xmsconan/ci_tools/publish.py:118publish() takes 10 parameters; url/username/password are one credentials concept (pre-existing; range only annotated toml_path); group credentials into a parameter object.
  • xmsconan/coverage_tools/coverage_generator.py:617if config.ci.xvfb: _reexec_under_xvfb() is never reached by a run_coverage test; add one with [ci] xvfb = true and _reexec_under_xvfb patched.
  • xmsconan/generator_tools/ci_file_generator.py:242generate_ci body runs ~195 lines (257-452) mixing validation, version resolution, a 44-line context dict, and rendering (pre-existing; range shortened it by ~14 lines); extract _validate_ci_config(config) and _ci_context(...).

Dropped in validation (not counted above): the two discipline-marker MAJORs, the test_build_filter.py:125-128 for-loop finding (loop is a pre-existing context line), and the test_publish.py:283-324 _check_xvfb temp-file setup finding (correct tests; style preference).

@tchristiansen-aquaveo

Copy link
Copy Markdown
Contributor Author

Response — review of toml-refactor

Addressed in two commits on top of the reviewed head 0b084e3:

  • 4ce5659 — Type-check the [coverage] and xms_dependencies values
  • 92e8b08 — Build test configurations directly instead of through the reader
docs/USAGE.md                    |  4 +--
tests/test_build_filter.py       | 60 ++++++++++++++++++++--------------------
tests/test_build_toml.py         | 28 ++++++++++++++++++-
tests/test_ci_options.py         |  8 +++---
tests/test_coverage_generator.py | 39 ++++++++++++++------------
tests/test_utils.py              |  9 +-----
tests/utils.py                   | 14 ----------
xmsconan/build_toml.py           | 54 ++++++++++++++++++++++++------------
8 files changed, 122 insertions(+), 94 deletions(-)

Seven fixed (m1 in part, m4–m10), eight declined (m1 in part, m2, m3, m11–m15).
The rule applied throughout: fix what this branch introduced; leave what it
merely touched. Every "pre-existing" call below was checked against
origin/master, not assumed. The four findings the validator dropped are
listed at the end so nothing from the review goes unaccounted for.

Finding Outcome
m1 Two exercise-verify cycles per test Fixed for test_build_toml.py; declined for test_build_filter.py (pre-existing)
m2 Bare-assert defaults tests Declined
m3 Unknown-key test copy-pasted into five modules Declined
m4 Coverage rejection test could shell out Fixed
m5 Test factory imports the private converter Fixed, by removing the factory
m6 _validate_top_level_keys duplicates _reject_unknown_keys Fixed
m7 [coverage].filters/excludes not type-checked Fixed
m8 Boolean threshold read as 1.0 Fixed
m9 Unquoted python_version coerced to a wrong version Fixed
m10 xms_dependencies values not type-checked Fixed
m11 ci_options.py is one function Declined
m12 docker_image branch untested Declined (pre-existing)
m13 publish() has 10 parameters Declined (pre-existing)
m14 xvfb dispatch in run_coverage untested Declined (pre-existing)
m15 generate_ci is ~195 lines Declined (pre-existing)

Fixed

m1 (part) — test_build_toml.py:21-22 split in two

test_build_toml_derives_python_namespaced_dir now checks only the derived
default; test_build_toml_keeps_an_explicit_python_namespaced_dir checks the
override. The two test_build_filter.py sites in the same finding are declined
below.

m4 — the coverage rejection test fails loudly if the run shells out

test_run_coverage_rejects_an_unknown_top_level_key patches
coverage_generator._run with side_effect=AssertionError(...). If the
reader ever stops raising on an unknown key, the test now fails with
"run_coverage shelled out before rejecting build.toml" instead of trying to run
xmsconan gen from a temp directory.

m5 — tests build BuildToml directly; the factory is gone

The finding was right about the coupling but the suggested fix (a public
dict-to-BuildToml function) would have widened the reader's surface to serve
tests. Instead, make_build_toml was deleted from tests/utils.py along with
its _toml_to_dataclass import, and the 28 call sites in
test_build_filter.py, test_ci_options.py, and test_coverage_generator.py
construct the value they need:

config = BuildToml(
    library_name="xmscore",
    ci=CiTable(python_versions=["3.10", "3.13"]),
    filter={"options": {"python_version": "3.10"}},
)

tests/test_build_toml.py still imports _load_toml, _toml_to_dataclass,
and _CI_KEY_TYPES on purpose — it is the converter's own test module. No
other test file reaches past the underscore.

m6 — one unknown-key check

_validate_top_level_keys now calls
_reject_unknown_keys(toml_data, _KNOWN_KEYS, str(toml_path), kind="top-level key").
The kind parameter keeps the message the seven existing tests match on
(... has unknown top-level key(s) ...).

m7, m8, m9, m10 — the sub-table values are checked, not just the keys

All four are in xmsconan/build_toml.py, which is new on this branch, so all
four are this branch's to fix. Each rejection names the file and key.

Input Before Now
m7 filters = "xmscore/" passed through; iterated into one-character gcovr patterns [coverage].filters must be a list, got str
m8 cpp_threshold = true float(True)1.0 [coverage].cpp_threshold must be a number, got a boolean
m9 python_version = 3.10 (unquoted) str(3.1)"3.1", rejected downstream as a version nobody wrote [coverage].python_version must be a quoted string like "3.13", got 3.1
m10 no_python = "false" truthy string; the template dropped the dependency from the wheel no_python must be true or false, got 'false'; name/version must be strings

The threshold logic moved into _coverage_threshold so _coverage_table stays
flat. The python_version coercion is gone rather than tightened — the
converter no longer guesses at what an unquoted number meant.

New tests: test_toml_to_dataclass_rejects_a_boolean_threshold,
test_toml_to_dataclass_rejects_a_scalar_coverage_pattern_list (parametrized
over filters/excludes),
test_toml_to_dataclass_rejects_an_unquoted_coverage_python_version, and two
new cases in test_toml_to_dataclass_rejects_malformed_xms_dependencies
(numeric-version, quoted-boolean). docs/USAGE.md §5.3 and §5.7 gained one
sentence each describing the new checks.


Declined

m1 (part) — test_build_filter.py:176-184, 189-192

Both test shapes exist on origin/master
(test_load_build_filter_checks_the_filter_against_the_narrowed_matrix was
already raise-then-accept; test_ci_python_versions_defaults_to_none already
had two asserts). This branch only swapped their dict literals for dataclass
construction.

m2 — bare asserts in the defaults tests

An expected-object literal would restate the same 13 and 15 values in a
different shape without making a failure easier to read. Line 54
(CiTable(linux=False).linux_enabled is False) is the one non-default case in
that test and belongs there.

m3 — the repeated unknown-key test

Five three-line tests share a write_text call and a regex. The repetition is
small enough that a shared helper would cost more indirection than it saves.

m11 — ci_options.py

The module was already this thin before the branch; the branch removed the
[ci] schema from it, it did not shrink a larger module down. Turning
repairs_windows_wheel into a BuildToml property would put a CI-publishing
decision on the configuration dataclass, which is the wrong home for it.

m12, m14 — untested branches in docker_run.py:46 and coverage_generator.py:617

Both branches and both test gaps exist on origin/master (toml_image = ci_config.get("docker_image"), if ci_config.get("xvfb"):). The branch
changed how the value is read, not whether it is tested. Worth tests, but not
this PR's.

m13 — publish()'s parameter list

Pre-existing. The branch only annotated toml_path: str | Path.

m15 — generate_ci length

Pre-existing, and the branch shortened it by ~14 lines. The extraction the
review suggests is a reasonable follow-up on its own.


Dropped in validation

Listed so the review reads as fully answered.

  • DESIGN: / EVIDENCE: markers absent from the range. These are
    conventions of the review tooling, not of this repository. The design
    artifact is docs/superpowers/plans/2026-08-27-build-toml-reader.md; the
    evidence is the Gates section below.
  • test_build_filter.py:125-128 for loop around exercise and assertion.
    The loop is a context line in the diff; this branch rewrote only its body.
  • test_publish.py _check_xvfb tests still write a temp file and call
    read_build_toml.
    Left as-is. The tests are correct; if the direct-
    construction style from m5 should extend here, it is a four-line change per
    test (BuildToml(library_name="xmscore", ci=CiTable(xvfb=True))).

Gates

At 92e8b08:

$ .venv/Scripts/python.exe -m pytest tests -q -p no:cacheprovider
2153 passed, 2 skipped in 31.17s

$ .venv/Scripts/python.exe -m flake8 xmsconan tests
0

The count is six higher than at 0b084e3 (2147 → 2154 after 4ce5659's seven
new tests → 2153 after 92e8b08 removed the factory's smoke test).

tchristiansen-aquaveo and others added 4 commits August 28, 2026 17:17
master's #126 reversed the [coverage].parallel default to off, after the
overlap raced conan's local cache and broke xmsvtk's Coverage stage, and
measured 1.57x slower once the legs contended for CPU. Take that decision:
CoverageTable.parallel defaults to False.

master's inline bool check in _coverage_context is dropped in favor of the
read-time check in build_toml, which every tool that reads build.toml
reaches rather than only the two that build a coverage context. The
parallel tests are consolidated into TestConcurrentCoverageBuilds against
the typed config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tchristiansen-aquaveo
tchristiansen-aquaveo merged commit d3b0c78 into master Aug 31, 2026
8 checks passed
@tchristiansen-aquaveo
tchristiansen-aquaveo deleted the toml-refactor branch August 31, 2026 16:33
wdolinar added a commit that referenced this pull request Aug 31, 2026
The rebase onto master brought in #123, which replaced the ad-hoc dict
reader with read_build_toml() and a typed BuildToml. Three sites on this
branch predate that and the merge could not have caught them: the code they
conflict with was deleted rather than changed, so git took both sides
cleanly and left calls to a function that no longer exists.

_report_coverage is the substantive one. It was split out of run_coverage
by this branch, so it never had a master-side counterpart to merge with and
kept calling _load_toml, then indexing the result as a dict. It now reads
config.coverage and config.library_name the way _collect_coverage already
does, which is the same pattern master left there.

Both phase helpers also widen to str | Path. That is not cosmetic: master
widened run_coverage, and these two receive whatever it was handed and pass
it to Path() unchanged.

The last site is the coverage-pin test, which still built a filter as a
bare dict.

This lands as its own commit rather than folded into the three it repairs
because interactive rebase is not available here; the branch is green at
the tip, and the intermediate commits are the pre-#123 originals.
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.

2 participants