✨ sphinx-codelinks: read ubproject.toml through ub-project, and let -D override the TOML - #2005
Merged
Merged
Conversation
Red against d0edd0e (22): a `-D src_trace_<key>` value beats the TOML for the seven scalar keys and `config_from_toml`, and in the -D / TOML / conf.py three-way; a corrupt default ubproject.toml (syntax, non-UTF-8, a directory, `codelinks` not a table) warns `codelinks.config`; an explicit file without `[codelinks]` says so; the reader's warnings are suppressible with `codelinks.config`; the CLI shows the parser's reason and ub-project's "[codelinks] must be a table" words. Pins, green at d0edd0e and proven by mutation later (8): a bare-name `-D` and a refused `-D src_trace_projects=x` leave the TOML alone; conf.py vs TOML is unchanged; an empty or missing `[codelinks]` keeps the CLI's words; a 5000-deep TOML is still rc 2; a symlinked TOML anchors at the link's directory; a TOML-set `config_from_toml` moves the anchor.
A runtime dependency, declared directly: PyPI's sphinx-needs 8.5.0 does not bring ub-project, so the workspace's own sphinx-needs making it importable would hide a missing declaration from every local test. The lock is master's plus the two lines of the sphinx-codelinks block -- not a relock, which would rewrite 224 unrelated marker lines. `uv lock --check` passes on uv 0.12.15 and on the hook's 0.12.9.
…D override the TOML One loader, `config.load_codelinks_table(path)` = ub-project's `load_toml` + `select_table(..., "codelinks", source=path)`, returning the table's raw values; both readers call it and `src/` no longer imports tomllib. The Sphinx reader keeps its discovery as it was. A file that exists but cannot be read or parsed now warns for the default ubproject.toml too, as every configured file did at 1.4.0 (the silence was unreleased); a missing file or a missing `[codelinks]` table stays silent for the default file. Its three warnings are typed `codelinks.config`, the load failure no longer repeats the path ub-project's message already names, and an explicit file without the table says so instead of printing a KeyError repr. `set_config_to_sphinx` skips a TOML key given as `-D src_trace_<key>`: only the full confval name (a bare `-D set_local_url=0` is refused by Sphinx and must not suppress the TOML), and never `projects` (Sphinx refuses a whole-dict override but keeps it in `config.overrides`). `-D src_trace_outdir` is refused by Sphinx in the same way, and the TOML's `outdir` is skipped with it: inert, the extension never reads that value -- pinned by its own test, which replaces the `outdir` row of the "-D wins" parametrization. The CLI re-raises ub-project's error in its own words (a BadParameter, now showing the reason), keeps a catch-all for the RecursionError tomllib can raise, and keeps its falsy "No 'codelinks' section" test. The path joins go through ub-project's `anchor`, at the use sites as before and with every `.resolve()` kept: identical to `/` by construction.
…ng and the ub-project reader configuration.rst: `-D` overrides both conf.py and the TOML, `src_trace_projects` only comes from conf.py or the TOML (no dotted `-D`), Sphinx refuses `-D src_trace_outdir`; a file that exists but cannot be read or parsed warns, the default one included, as `codelinks.config`. changelog `Unreleased`: the default-ubproject.toml bullet is amended in place (it is unreleased text), plus a bullet for the `-D` rule and one for the ub-project reader and its messages. AGENTS.md: config.py holds the loader, `load_codelinks_table`; integration point 2 names the `-D` exception; the stale click/typer caps section and the test counts (393; 337 + 26 skipped without libclang) are corrected.
…the outdir exemption Red at b1ac8d4 (4): a 5000-deep TOML, default or explicit, warns naming the file (the Sphinx side's non-ProjectConfigError warning dropped the path); `-D src_trace_outdir` leaves the TOML's `outdir` standing (the pin, inverted); the keys exempt from the -D skip are exactly the codelinks confvals Sphinx refuses from -D, detected per confval by Sphinx's own "cannot override" warning. Pins, green at b1ac8d4: `-D` on one key leaves the other TOML keys and the projects; an explicit syntax error names the file once; the default name written in conf.py is still the default (missing file, missing table: silent); the CLI's ProjectConfigError messages are not the catch-all's.
…er crash names the file `outdir` joins `projects` in one named tuple, `NOT_OVERRIDABLE_FROM_D`: Sphinx refuses a `-D` for both (a dict; a Path-typed default) yet keeps the key in `config.overrides`, so skipping the TOML value would honour an override that was never applied. A TOML `outdir` now stands under `-D src_trace_outdir=...`, as it did before this branch. The Sphinx reader's catch-all keeps ub-project's message for a ProjectConfigError (which names the file) and says `... from <file>: <error>` for anything else -- a RecursionError's text names no file.
…counts that go stale A file named ubproject.toml is the shared default whether or not conf.py spells the name: missing, or without `[codelinks]`, it is silent; a file that exists but cannot be read or parsed warns, as any configured file did at 1.4.0; any other name is explicit and warns in all three cases. The earlier text said an "explicitly configured" missing file warns, which is false for the default name written out -- corrected in configuration.rst, both docstrings and the amended changelog bullet, which now also says what a 1.4.0 project that wrote the default name loses. The -D changelog bullet and AGENTS.md name `outdir` beside `projects`, and the bullet says an invalid -D value is now Sphinx's own error rather than being replaced by the TOML. `load_codelinks_table` names the RecursionError it lets through. Counts: the package AGENTS.md says 17 test modules (it was already 17), and it, the root AGENTS.md, the root CLAUDE.md and compat-requirements.txt describe the libclang-gated tests without numbers that go stale with every test added.
…entences The default is the value `ubproject.toml` exactly -- left unset, or written in conf.py as that string -- because the reader compares strings, not files: `./ubproject.toml` (or an absolute path to the same file) is an explicit file and warns when missing or without `[codelinks]`. The sentences in configuration.rst, both docstrings and the changelog bullet said the file's name decided; they now say what the code does, and a test pins it (red if the default were decided by `Path(value).name`). AGENTS.md: "the two numbers" / "check the number" referred to counts the block no longer shows.
Documentation build overview
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2005 +/- ##
==========================================
+ Coverage 91.69% 91.79% +0.09%
==========================================
Files 129 129
Lines 18155 18125 -30
==========================================
- Hits 16648 16638 -10
+ Misses 1507 1487 -20
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…el output typer forces a colour terminal when GITHUB_ACTIONS is set, so on the runners the error panel carries ANSI escape codes, including between the halves of a wrapped line, and `_normalize_output` only collapsed box characters and whitespace: a phrase split by the wrap stayed split. Which test broke depends on the panel width (on CI: test_analyse_toml_syntax_error_shows_the_reason). The escapes are now removed first. Reproduced locally with GITHUB_ACTIONS=1.
This was referenced Sep 30, 2026
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.
What
sphinx-codelinks now reads
ubproject.tomlthroughub-project, the shared reader of the sphinx-needs family, and gains it as a runtimedependency (
ub-project>=1.1.0,<2). One loader,config.load_codelinks_table(path)— ub-project'sload_toml+select_table(…, "codelinks")— serves both readers, the Sphinx extension'sconfig-initedhook andcodelinks analyse;src/no longer importstomllib. The loader returns raw values: relative paths are still anchored where they are used, now through ub-project'sanchor(identical to the
/join it replaces, by construction), with every.resolve()kept.Two behaviour fixes ride on it: a
-Dvalue is no longer overwritten by the TOML, and a defaultubproject.tomlthat exists but is brokenwarns again, as any configured file that could not be read did at 1.4.0.
Behaviour (enumerated)
-D src_trace_<key>beats the TOML. Forset_local_url,set_remote_url,local_url_field,remote_url_field,debug_measurement,debug_filtersandconfig_from_toml, "the TOML sets it and-Dis given" now yields the-Dvalue. The order is-D> TOML > conf.py > default, as in sphinx-needs. Only the full confval name counts — a bare-D set_local_url=0, which Sphinx rejectsas unknown, leaves the TOML value alone.
projectsandoutdirare never skipped (NOT_OVERRIDABLE_FROM_D): Sphinx refuses a-Dforboth (a dict; a
Path-typed default) yet keeps the key inconfig.overrides, so skipping would honour an override that was neverapplied — a TOML
projectsoroutdirstands, as before. A-Dvalue Sphinx cannot convert (-D src_trace_set_local_url=yes, Sphinx ≥8.2) is now reported by Sphinx (rc 2) instead of being silently replaced by the TOML — as it already was without a TOML.
ubproject.tomlwarns (invalid TOML, not UTF-8, a directory,codelinksnot a table, a pathologically nested file):under
-Wthese builds fail, where master's (unreleased) behaviour built silently without the[codelinks]table.codelinks.config— a[codelinks.config]suffix on Sphinx ≥ 8 — sosuppress_warnings = ["codelinks.config"]silences them.<p>: invalid TOML: …,not valid UTF-8 TOML,cannot be read,[codelinks] must be a table, got str; a nested file:… from <p>: maximum recursion depth exceeded); an explicit filewithout the table says
… has no [codelinks] table. Using configuration from conf.py.instead of'codelinks';codelinks analyseshows why a file could not be loaded instead of only that it could not, and reports
codelinks = 0/[]/falseas "must be a table" ratherthan "No 'codelinks' section". Exit codes are unchanged in every cell except the ones in items 1 and 2: every CLI exit code is unchanged,
including rc 2 for a pathologically nested file.
ub-project>=1.1.0,<2is a runtime dependency — declared directly, because PyPI's sphinx-needs 8.5.0 does not bring it.No existing test or snapshot expectation moved.
The default file (what 1.4.0 did, what master does, what this ships)
src_trace_config_from_toml = "ubproject.toml", missing, without[codelinks], or broken.ubproject.toml, recognised by NAME (a string comparison), so any failure of a fileof that name was silent — whether the name was left at its default or written in conf.py.
ubproject.tomlexactly — left unset, or written in conf.py as that string (the reader comparesstrings, not files). That file may be absent or have no
[codelinks]table — both silent (the intent of Need layout system #102, kept: other tools sharethe file). Any other value,
./ubproject.tomlor an absolute path to the same file included, is an explicit file: missing or without thetable, it warns. A file that exists but cannot be read or parsed warns (
codelinks.config) either way, as 1.4.0 warned for a corruptconfigured file. The trade, stated: a 1.4.0 project that wrote
"ubproject.toml"out keeps the corrupt-file warning but nolonger gets one for a missing file or a missing table. The Need layout system #102 changelog bullet is amended in place to say so.
Not in this PR
src_trace_projects(no TOML at all) crashes every build that usessrc-tracewithKeyError: 'source_discover_config'— pre-existing on master and unrelated (the conversion only runs on the TOML path); to be filed separately.
[codelinks]: the extension skips them silently, the CLI refuses them — the two readers disagree today, and afollow-up decides both together.
config_from_tomlremains a key the TOML itself may set (it moves the anchor without reading the named file); pinned by a test so thefollow-up that retires it changes a fenced thing.
check_sphinx_configuration's bareraise Exceptionfor a schema error (and the "filed" typo in its message) — validation, not reading.Docs
components/configuration.rst: the-Dprecedence rule (withsrc_trace_projectsandsrc_trace_outdiras the two keys-Dcannotset), the default-name rule, and the
codelinks.configtype.changelog.rstUnreleased: the default-file bullet amended, plus a bulleteach for the
-Dfix and the ub-project reader. The packageAGENTS.md: the loader's home, the-Dexception, the stale click/typer capssection, 17 test modules. The root
AGENTS.md, rootCLAUDE.mdandcompat-requirements.txtdescribe the libclang-gated tests withoutcounts that go stale.
Tests
39 new cases (31 Sphinx-level through
make_app, 8 CLI throughCliRunner); 28 of them fail against master. The other 11 pass thereand are pins: a bare-name
-D; a refused-D src_trace_projects; a refused-D src_trace_outdir; the conf.py-vs-TOML order; the CLI'sempty table; the CLI's missing table; the CLI's nested file; a symlinked TOML anchoring at the link's directory; a TOML-set
config_from_toml; and the default name written out, missing and without the table (2). Each pin is proven by a mutation that turns itred. One test asserts that the keys exempt from the
-Dskipare exactly the codelinks confvals Sphinx refuses from
-D, detected per confval from Sphinx's own warning, on 7.4 / 8.2 / 9.1.402 passed with libclang, 0 skipped (the sphinx-7 and sphinx-8 cells were run at 400, one commit earlier; the last commit adds two test
cases and changes no reader logic).
Toolchain
The lock is master's plus the two lines of the
sphinx-codelinksblock (not a relock);uv lock --checkpasses on uv 0.12.15 and 0.12.9.poe check-workspace,poe import-check-codelinks(24 modules,ub-project==1.1.0andsphinx-needs==8.5.0from PyPI) andpoe docs-codelinks(-nW) pass. The version (1.4.0) is unchanged; no release.