[pull] master from useblocks:master - #221
Merged
Merged
Conversation
…o test-files and tests for pytest and nosetest output xml files
* added directive test-env and its supporting doc * added test-env directive * added test-env directive and supporting docs * test cases and bug fix in directive * test case for Warning * warning test case, py27-sphinx15 fix
…TEST_51 skip testsuites element if present
`test-reports convert FILE... --output needs.json` converts test-result XML into schema-conform needs.json outside Sphinx, as a cacheable build action: - no Sphinx import anywhere in the CLI chain (asserted by a test) - byte-stable output: sorted keys, fixed indent, no wall clock - deterministic case IDs, source location and synthesized source URLs - XML properties become need fields; --link-property promotes them to link fields - schema test skips on sphinx-needs versions without check_needs_data
…locks/sphinx-test-reports#146) Reverts 6e594a5 (useblocks/sphinx-test-reports#144). useblocks/sphinx-test-reports#144 was merged ahead of the declarative-configuration work unintentionally. The merge itself was not the problem — the ordering was. The intended stack is: 1. **this revert** — take the CLI back off `master` 2. **useblocks/sphinx-test-reports#145, reshaped to config-only** — the `[test_reports]` section of `ubproject.toml` and the Sphinx bridge, with no CLI surface 3. **a new CLI PR** — the converter rebased on top, reading its conversion settings from the declarative config from the start, instead of shipping flags first and growing `--config` afterwards ## What this removes The five files useblocks/sphinx-test-reports#144 added — `sphinxcontrib/test_reports/cli.py`, `needs_export.py`, `remote.py`, `docs/cli.rst` and `tests/test_cli_convert.py` — plus the `test-reports` console script, the `cli` toctree entry and the `cli.py` mypy exclude. It also takes back the PEP 562 lazy `setup` resolution in `__init__.py`, which existed so the CLI could import package submodules without pulling in Sphinx. It has no consumer without the CLI and returns with it. ## What is unaffected Nothing on the Sphinx side depends on the reverted files. `identity.py` came from useblocks/sphinx-test-reports#143, not useblocks/sphinx-test-reports#144, so `directives/test_common.py` and the deterministic-ID feature are untouched. 110 tests pass on this branch. The work is not lost: it is preserved locally on `str-needs-json-cli` at 35f26dc and comes back as step 3.
Python 3.10 reached the end of upstream support. The floor matters beyond housekeeping: `tomllib` landed in 3.11, so a 3.10 floor means carrying `tomli` as a dependency purely for older interpreters. Split out of useblocks/sphinx-test-reports#145, which needs a stdlib TOML parser, so the version bump can be reviewed and merged on its own. ## Changes - `requires-python` `>=3.10` → `>=3.11` - `noxfile.py` and the CI test matrix run 3.11/3.12 - the `3.9` and `3.10` classifiers go (3.9 was already stale — `requires-python` has excluded it for some time) - the `pre-commit` and `linkcheck` jobs pinned Python 3.9 purely to drive nox; they move to 3.12, since 3.9 is two releases past end of life and the sessions they start pick their own interpreter anyway No source change — nothing in the package uses a 3.11 feature yet. 110 tests pass. For reference, the sibling repos are already ahead of this: sphinx-codelinks and sphinx-mounts both require `>=3.12`.
…st-reports#150) Follows useblocks/sphinx-test-reports#147 (Python 3.11, merged). Split out so the version floors can be reviewed on their own;the declarative-config, plugin and converter PRs are rebased onto it and lose their remaining compatibility branches. ## Why The package now requires Python 3.11. sphinx-needs raised its own floor to 3.10 with 6.0 (#1468 dropped 3.9) and requires Sphinx 7.4 from that release on. The versions this package still carried compatibility branches for — sphinx-needs 2.x to 5.x, Sphinx below 6.1 — are ones it no longer shares an interpreter range with in practice,and 6.x is where field registration with a schema begins, which is what the extension relies on. The exact floor is **6.0.1**:that is the first release whose `add_extra_option` takes `schema=` (sphinx-needs #1527). On 6.0.0 the call raised `TypeError`, which `_register_field` swallowed under a catch-all meant for "already registered" — so no field was registered and every directive failed with `InvalidNeedException`. The first CI run of this PR caught exactly that on the 6.0.0 cells. ## Changes - `sphinx-needs>=6.0.1` and `sphinx>=7.4` (the floor sphinx-needs 6 implies anyway) - nox and CI matrices: sphinx-needs 6.0.1 / 6.3.0 / 7.0.0 / 8.0.0 × Sphinx 7.4.7 / 8.1.3 (the Sphinx 5.0 cells could not have installed sphinx-needs 6) - `sphinx_needs_update` registers fields with a schema unconditionally;the no-schema branch is gone - `_register_field` catches only `NeedsApiConfigWarning` (a duplicate registration) instead of every exception,and no longer inspects `add_extra_option`'s signature - the Sphinx version switches around `logging`, `status_iterator` and the test fixtures' path type are gone - the `needs_schema_definitions` tests no longer skip - the test fixtures' confs now switch to `needs_fields` at sphinx-needs 7.0.0 instead of 8..0.0:7.0.0 is where `needs_fields` was introduced and `needs_extra_options` deprecated (sphinx-needs #1611), so on 7.x the old option emits a `[needs.deprecated]` warning, which `test_test_file_needs_extra_options_no_warning` caught. Schema-based registration works since 6.0.1, so this only changes the 7.x path — no behaviour change for 6.x or 8.x. No behaviour change for any supported version. 110 tests pass on each of: sphinx-needs 6.0.1 / Sphinx 7.4.7, 7.0.0 / Sphinx 7.4.7,and 7.0.0 / Sphinx 8.1.3;also verified on 8.5.0 / Sphinx 9.1.
…seblocks/sphinx-test-reports#145) Stacked on useblocks/sphinx-test-reports#150 (sphinx-needs 6.0.1 / Sphinx 7.4 floor) and, through it, on the Python 3.11 floor of useblocks/sphinx-test-reports#147 — reading the section needs `tomllib`. Describe the project once, in the `[test_reports]` section of `ubproject.toml` — the declarative file sphinx-needs, sphinx-codelinks, sphinx-mounts and ubCode already read — instead of restating it in `conf.py`. **Rescoped.** This PR previously also carried the `test-reports convert` CLI integration and the conversion settings themselves. useblocks/sphinx-test-reports#144 has been reverted (useblocks/sphinx-test-reports#146) so the declarative configuration lands first; the converter returns as a follow-up PR stacked on this one, bringing its own settings under `[test_reports.build.needs]` and reading them from the start rather than shipping flags and growing `--config` afterwards. What is left here is exactly what the Sphinx extension consumes. ## What - New Sphinx-free module `projectconfig.py` parses, validates and normalises the section. It imports no Sphinx, and neither does the package `__init__` it runs through: `setup` is resolved lazily (PEP 562), so a build action can read the section without the documentation toolchain installed. The section describes the *project*, not this extension. - **Bridge**: a `config-inited` handler at `priority=100` applies the Sphinx-facing keys to their `tr_*` config values, ahead of the handlers that read them. Precedence is **`-D` > `ubproject.toml` > `conf.py` > built-in default** — the file is the source of truth for the project, the command line stays the per-invocation escape hatch. Both are announced in the log (`Applied … from …`, `Kept the -D value of …`). - **New confval `tr_config_from_toml`** (default `ubproject.toml`; `None` disables, and `NoneType` is an accepted type so the documented opt-out does not trip Sphinx's own confval check). With the default name the file is searched for upwards from the `confdir` to the repository root — the directory holding `.git`, and nothing else bounds the search: a `pyproject.toml` on the way up marks a distribution, not the project, so `docs/pyproject.toml` beside `conf.py` and a uv-workspace member (`packages/<dist>/docs/conf.py` with one `ubproject.toml` at the monorepo root) both find the file. A fruitless search says where it ended (`sphinx-build -v`). An explicitly named file is used as-is and warns if missing (`test_reports.missing_config`). - **Spellings**: `file`/`suite`/`case` accept both the positional `conf.py` list and a named table (`directive`/`type`/`name`/`prefix`/`color`/`style`). Relative `rootdir`/`report_template` anchor at the TOML file's directory; the loader joins without resolving, so the form of the path it is handed (Sphinx's already-resolved `confdir`, a converter's working directory) is kept as is. - **Error policy**: a *known* key with the wrong type is an error — that is the typo class this validation exists for — and table *values* are checked, not just the outer table, including the values inside a `file`/`suite`/`case` table. An *unknown* key is warned about and ignored (suppressible via `test_reports.unknown_key`): the file is shared with tools on independent release cadences, so a key this version does not model must not take a build down. Same posture sphinx-mounts documents for `[[source.mounts]]`. - **Foreign sub-table**: `[test_reports.build]` — and only that one — is recognised and passed through untouched. It holds the settings of the `test-reports build` command line, one sub-table per artifact it produces (`[test_reports.build.needs]` for turning test reports into a `needs.json` outside Sphinx), none of which this extension does, so it must draw neither an unknown-key warning nor a `tr_*` value. Any other sub-table is an unknown key like any other. The converter PR defines and validates the contents of `build.needs`. - `tr_rootdir` declares `types=(str, os.PathLike)`: its default is Sphinx's `confdir`, a `_StrPath`, so a plain string — the only thing TOML or a string literal in `conf.py` can supply — used to fail Sphinx's confval type check and take a `-W` build down. Pre-existing, but the documented example newly advertised it. ## Notes - The section is spelled `test_reports`, matching every other section of `ubproject.schema.json` (`build_tags`, `format_rst`, `needs_json`, …) and leaving the existing `[reports]` — report *templates* — unambiguous. ## Tests 54 tests in `tests/test_project_config.py`: loader validation, normalisation and path anchoring; upward discovery, its repository-root boundary and the layouts a `pyproject.toml` must not end (`docs/pyproject.toml`, workspace member); the foreign-sub-table passthrough; real Sphinx builds asserting the precedence chain (`-D` beats TOML, TOML beats `conf.py`), the warning paths, where a fruitless search ended, and that the documented example — read from `configuration.rst` verbatim — applies without a warning; and that `projectconfig` imports in a process where Sphinx is blocked. ## Review Reviewed in useblocks/sphinx-test-reports#145 (comment), addressed in useblocks/sphinx-test-reports#145 (comment). The CLI-side fixes from that round move with the converter to its own PR. Second round: useblocks/sphinx-test-reports#145 (review) — the three fixes, the text corrections and both unconfirmed claims are addressed in 809bd9a; the discovery-rule question across packages stays open for the monorepo. Two findings from the first review move with the conversion settings and are re-applied in the converter PR: the `need_type` / `case.type` agreement check (both name the need type of a test case, so they must not disagree), and the `link_properties` value-type validation.
…t-reports#154) Closes useblocks/sphinx-test-reports#152. Follow-up from @chrisjsewell's [review of useblocks/sphinx-test-reports#150](useblocks/sphinx-test-reports#150 (review)). ## What `packaging` was declared as a runtime dependency, but PR useblocks/sphinx-test-reports#150 removed the last import of it under `sphinxcontrib/`. It is still genuinely used outside the package, so it moves to the extras that cover those consumers rather than being deleted outright: | Consumer | Installed via | Extra it now lives in | | --- | --- | --- | | `docs/conf.py` | RTD (`.readthedocs.yaml`) and the `linkcheck` nox session | `docs` | | 9 × `tests/doc_test/*/conf.py` | the `tests` nox session | `test` | All of them use it for the same thing — the `Version(sphinx_needs.__version__) >= Version("7.0.0")` config gate. The `>=20.0` floor is dropped rather than carried over: `packaging.version.Version` predates it by years, Sphinx already pins a much higher floor, and the surrounding extras entries only carry bounds where one actually matters. ## Verification - `grep -r packaging sphinxcontrib/` → no matches (the issue's stated criterion). - AST scan over all 18 modules under `sphinxcontrib/` for `import packaging` / `from packaging …` → **zero** hits, so this is not a grep artifact. - Installed metadata after the change lists `packaging` only as `packaging ; extra == "docs"` and `packaging ; extra == "test"` — no longer a runtime requirement. - `pytest -n auto tests/` → **169 passed**, unchanged from baseline (Python 3.12, Sphinx 8.1.3, sphinx-needs 8.0.0, from a fresh `.[test]` install). - `sphinx -b html docs docs/_build/html` from a fresh `.[docs]` install → `build succeeded.`, no warnings. - `pre-commit run --all-files` → all 7 hooks pass, `taplo-format` included. - `uv sync --group dev && uv run mypy` → `Success: no issues found in 6 source files`. No lockfile in the repo, so nothing to regenerate.
Closes useblocks/sphinx-test-reports#153. ## What Adds `8.5.0` as a fifth `sphinx_needs-version` cell to the CI matrix and to the `SPHINX_NEEDS_VERSIONS` list in `noxfile.py` that mirrors it. Four new jobs (python 3.11/3.12 × sphinx 7.4.7/8.1.3), taking the `tests` matrix from 16 to 20. ## Why — and a correction to the issue's premise The issue asks for this on the grounds that the `add_field` registration branch in `sphinx_needs_update()` is reachable only from sphinx-needs ≥ 8.5 and therefore untested. **That premise does not hold.** Checking the API across the matrix range: | sphinx-needs | `sphinx_needs.api.add_field` | |---|---| | 6.0.1 | absent | | 6.3.0 | absent | | 7.0.0 | **present** | | 8.0.0 | present | | 8.5.0 | present | `add_field` was introduced in **7.0.0**, alongside `needs_fields` and the deprecation of `needs_extra_options` — the same 7.0.0 change the [PR useblocks/sphinx-test-reports#150 review](useblocks/sphinx-test-reports#150 (review)) identified as the correct threshold for the test fixtures' switch. The existing 7.0.0 and 8.0.0 cells were already exercising that branch; the `# sphinx-needs >= 8.5` comment above the import said otherwise and is the likely source of the mix-up. This PR corrects it to `>= 7.0`. The cell is still worth adding, for a different reason: the matrix topped out at 8.0.0, while 8.5.0 is the current release and the one a fresh `pip install sphinx-test-reports` resolves to today. That version was untested. It now is. ## Changes - `.github/workflows/ci.yaml`, `noxfile.py`: add `8.5.0` to the sphinx-needs matrix - `sphinxcontrib/test_reports/test_reports.py`: correct the `>= 8.5` comment to `>= 7.0` (comment only, no behaviour change) - `docs/changelog.rst`: `Testing:` entry under Unreleased ## Verification All four new cells run locally against sphinx-needs 8.5.0: ``` [py3.11 sphinx=7.4.7 sn=8.5.0] -> 169 passed [py3.11 sphinx=8.1.3 sn=8.5.0] -> 169 passed [py3.12 sphinx=7.4.7 sn=8.5.0] -> 169 passed [py3.12 sphinx=8.1.3 sn=8.5.0] -> 169 passed ``` `pre-commit run --all-files` and `mypy` are clean. CI on this PR is the real check. 8.5.0 fits the rest of the matrix without constraint changes: it requires `python >=3.10` and `sphinx >=7.4,<10`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
….json, configured from [test_reports.build.needs] (useblocks/sphinx-test-reports#148)
…locks/sphinx-test-reports#151) Based on master. Independent of the needs.json converter (useblocks/sphinx-test-reports#148, merged); rebased on top of it. ## Why A stock pytest run writes a JUnit XML this extension can only partly use. Under pytest's default `junit_family = xunit2`, no `<testcase>` carries the `file`/`line` attributes that give a test case its source location (`tr_source_file_option`/`tr_source_line_option`, and the deterministic ID of `tr_deterministic_case_ids`). Under `xunit1` pytest writes them, but counts the line from 0, keeps Bazel's runfiles prefix, and has no way to point a case at the file that drove it. And nothing records the requirements a test verifies. ## What `-p sphinxcontrib.test_reports.pytest_plugin`, with `junit_family = xunit1` (warned about otherwise), does two things to every `<testcase>`, through hooks on the test reports rather than fixtures, so a case skipped or erroring during setup gets the same treatment and pytest-xdist works: - the source location becomes the one an editor shows: line counted from 1, path relative to the rootdir with Bazel's `_main/` runfiles prefix cut at a whole path component; - the properties of an `add_test_properties(...)` decorator (or, for file-driven parameterised tests, the `apply_test_metadata(...)` runtime helper, which can also point the case at the driving file) are written as `<properties>`. Which properties exist is configuration, not code. The `test_reports_properties` ini option declares them, one `keyword [= XmlName] [, list]` per line: the keyword a test writes, the `<property>` name it is written under, and whether it takes a list (joined with `", "`, which `tr_property_link_types` splits again). A keyword not declared is written under its own name with a single value; a list under it, a list item that is not a string, or bytes, is a `TypeError` naming the option, so nothing is lost to a Python `repr` silently. The plugin ships no metamodel of its own. S-CORE's model is the worked example in the docs: ```ini [pytest] addopts = -p sphinxcontrib.test_reports.pytest_plugin junit_family = xunit1 test_reports_properties = partially_verifies = PartiallyVerifies, list fully_verifies = FullyVerifies, list test_type = TestType derivation_technique = DerivationTechnique ``` Ported from S-CORE docs-as-code's `score_pytest` attribute plugin: same two entry-point names, and with that example the XML comes out the same, so tests written against that plugin keep working when they import from here. Four deliberate differences in the XML: a case skipped or erroring at setup carries its location and properties here and pytest's stock values there; the properties keep the order of the keywords; the Bazel prefix is cut at a whole `_main` component only; and a `file` handed to `apply_test_metadata` is cut the same way. Two of that plugin's rules are not ported, as process rules of that project rather than properties of the data: the `TestType`/`DerivationTechnique` vocabularies are documented, not enforced, and a decorated test needs no docstring. The package `__init__` resolves `setup` lazily (PEP 562, useblocks/sphinx-test-reports#145), so loading the plugin into a pytest process imports neither Sphinx nor docutils; a test asserts it. A per-module mypy override lifts only the two `Any` checks the pytest API needs. A `pytest` extra (`pytest>=7.0`, where everything the plugin uses exists) pulls pytest in; it is not a dependency of the extension. pytest before 7.3.2 does not run on Python 3.12 by itself, so a `plugin_floor` nox session, wired into CI, runs the plugin's tests on the oldest pytest of each Python: 7.0.1 on 3.11, 7.3.2 on 3.12. ## Tests `tests/test_pytest_plugin.py`, 63 tests through `pytester`, each inner pytest a fresh subprocess: locations (a decorated function is located at its first decorator, as pytest does; skipped and setup-erroring cases included), the properties written from ini and pyproject declarations, the option grammar and its errors, the value rules, marker merging across class and function, the runtime helper's location override, nested in-process sessions (one failing to configure), a decorator applied before `pytest_configure`, a bad shape next to a fixture error, `--strict-markers`, the `xunit2` start-up notice under strict warning policies, `-p no:junitxml`, `legacy`, an xdist run, the Bazel path cut, and that importing the plugin imports no Sphinx. Verified on pytest 7.0.1 and 7.4.4 (Python 3.11), 7.3.2, 7.4.4 and 8.4.2 (3.12) and 9.1.1 (3.14).
… and `[pytest]` install without the toolchain (useblocks/sphinx-test-reports#159) Closes useblocks/sphinx-test-reports#155. ## What changes `pip install sphinx-test-reports` now installs `lxml` alone. Sphinx and sphinx-needs move to a `sphinx` extra: ```toml dependencies = ["lxml"] [project.optional-dependencies] sphinx = ["sphinx>=7.4", "sphinx-needs>=6.0.1"] pytest = ["pytest>=7.0"] # from useblocks/sphinx-test-reports#151, unchanged ``` So the install line of a documentation project becomes `pip install "sphinx-test-reports[sphinx]"`, and a test runner or build action that only wants `test-reports build needs` no longer pulls the toolchain. The same goes for the pytest plugin useblocks/sphinx-test-reports#151 added as the `pytest` extra: `pip install "sphinx-test-reports[pytest]"` now yields the plugin, pytest and `lxml`, nothing of the toolchain. `[test]` gains the two floors it used to inherit from the core list; `[docs]` already had them; the nox matrix pins both explicitly and is unaffected. The install page lists the three install lines side by side (`sphinx` extra for a documentation project, `pytest` extra for a test runner, bare package for a build action running the command), and the plugin's page, which said how to enable the plugin but not how to install it, names the extra. ## The version-constraint gap An extra is opt-in, so a project that keeps installing the bare package next to an already-installed older toolchain never shows pip the floors. The lazy `setup` in `__init__.py` now enforces them at load time, before the extension is imported (an outdated sphinx-needs may well import and only fail later, inside a directive): ``` Extension error: Could not load extension sphinxcontrib.test_reports: sphinx-needs 5.1.0 is installed, but sphinx-needs>=6.0.1 is required. Install the Sphinx extension's dependencies with: pip install "sphinx-test-reports[sphinx]" ``` A toolchain that is missing altogether keeps the existing "Could not import extension" path, which now names the extra too: ``` Extension error: Could not import extension sphinxcontrib.test_reports; the Sphinx extension's dependencies are an extra, install them with: pip install "sphinx-test-reports[sphinx]" (exception: No module named 'sphinx_needs') ``` One deviation from the sketch in the issue: instead of `app.require_sphinx((7, 4))` plus a hard-coded sphinx-needs floor, the new `toolchain` module reads the `sphinx` extra's requirements from the package's own metadata (`importlib.metadata.requires`) and compares each installed version against its specifier. The floors then live in `pyproject.toml` alone -- useblocks/sphinx-test-reports#150 just moved them, and a copy in code would need a lockstep test -- and a test guards that the extra the check reads is the one `pyproject.toml` declares. `packaging` does the parsing; it is imported lazily and the check stands down without it, since it arrives with Sphinx (and pytest) wherever the extension can be loaded. A distribution that is *not installed* is deliberately left to the import: the import states it more precisely, and a toolchain importable from a source tree without metadata must not be refused. On Sphinx 9 the error appears inside Sphinx's crash report, as the existing "Known" changelog entry already notes for the configuration errors; the note now covers this case too. ## Guaranteeing the property in CI A `toolchain_free` nox session (Python 3.11 and 3.12, new CI job, wired into `all_good`) installs the package with the dependencies it declares -- `pip install ".[pytest]"`, no `--no-deps`, so the declared lists themselves are what is tested -- asserts that neither `sphinx`, `sphinx_needs` nor `docutils` is importable, and runs the converter's and the pytest plugin's test modules with `-m "not toolchain"`. The tests that need a build carry the new `toolchain` mark (the issue suggested `needs_sphinx`; `sphinx` itself is taken by Sphinx's own testing fixtures). `tests/conftest.py` loads `sphinx.testing.fixtures` only where Sphinx is importable, and one top-level import of the package's Sphinx-dependent `exceptions` module in `test_project_config.py` became local to the marked test that uses it. useblocks/sphinx-test-reports#151's `plugin_floor` session (the plugin's tests on the oldest pytest per Python) installed `.[test]`, which after this change brings the toolchain back in. It now installs `.[pytest]` plus pytest-xdist and runs the same toolchain-absent guard, so the floor run doubles as the isolation proof on old pytest. It also forwards nox's posargs, which CI passes (`--full-trace`) and it ignored. The mypy CI job installs with `uv sync --group dev --extra sphinx`; without the extra it would no longer see Sphinx and docutils. ## Open questions from the issue, as resolved here - **Extra name:** `sphinx`, as proposed. It matches the `[docs]`/`[test]` style and the `[pytest]` extra from useblocks/sphinx-test-reports#151. - **1.5.0 or 2.0:** not decided here. The changelog carries a "Breaking:" entry spelling out the install-line change; `version` is untouched, so the release can decide. - **Two-distribution split:** not done, per the suggested path. - **`[pytest]` extra:** on master since useblocks/sphinx-test-reports#151; this PR is what makes it toolchain-free, and the second commit proves and documents that. ## Verification - `pytest -n auto tests/`: 407 passed (13 new in `tests/test_toolchain.py`) - `nox -s toolchain_free-3.11 toolchain_free-3.12`: 308 passed, 3 skipped (the pytest-xdist tests; xdist is not installed there), 24 `toolchain`-marked deselected, in a venv holding the package, `lxml` and pytest only; the toolchain-absent guard passes - `nox -s "plugin_floor(python='3.11', pytest_version='7.0.1')" "plugin_floor(python='3.12', pytest_version='7.3.2')"`: 63 passed each, toolchain absent - `mypy`: clean (the new module is strict-checked, not excluded) - `pre-commit run --all-files`: clean - `sphinx-build -W` of the docs: clean ## Notes for the reviewer - Rebased onto useblocks/sphinx-test-reports#151 (`pyproject.toml`, `noxfile.py`, `ci.yaml`, `tests/conftest.py` conflicted; all resolved as a union of both sides). The first commit is the original change; the second is the plugin-isolation follow-up and can be reviewed, or dropped, on its own. - `docs/install.rst` also fixes two pre-existing typos on the page it rewrites ("must to be", "can be find").
…esult vocabulary for both parsers (useblocks/sphinx-test-reports#161) ## Why S-CORE cannot adopt the `result` field as it is spelled today. A test case's `result` was never a chosen name — it was whatever the input called it. The JUnit parser passed the name of the result-carrying `<testcase>` child through verbatim, so a failure came out as `failure`, after the `<failure>` element: ```python RESULT_PART_KINDS = ("skipped", "failure", "error") # XML element names ... tc_dict["result"] = first_part["kind"] # the element name, verbatim ``` The two states with no element of their own were invented in the `else` branch, and whoever wrote it reached for participles — `passed`, and `disabled` for googletest's `status="notrun"`. The JSON parser passed its report's own value through untouched. Three consequences: * the same product said `failure` for one case's result and `failed` for the count of them on its suite (`fields.FIELDS`), and `errors` next to `error`; * the same logical outcome could arrive under two spellings depending on the input format, although `jsonparser`'s API is documented as being in sync with `junitparser`'s; * `docs/directives/test_case.rst` documented the value as **`failed`** — which it never was. The page that documents the directive was simply wrong. S-CORE's own colouring hook keys on pytest's vocabulary (`passed`/`failed`/`skipped`/`disabled`), so STR-converted needs would render uncoloured against it. ## What changed Every state is now a participle: **`passed`, `failed`, `error`, `skipped`, `disabled`**. `error` is deliberately *not* renamed. It agrees with the `errors` count beside it and with pytest, which spells that outcome `error` too. The pair that disagreed was `failure`/`failed`, and `failed` is the half that already appeared in a field name. The substance is not the rename but the **normalisation step that did not exist**: the vocabulary is declared once, in a new `results` module, and both parsers map their input onto it. A JSON report written against the JUnit dialect no longer produces a different `result` than the XML it mirrors. A state this package does not know is still passed through untouched, so a `tr_json_mapping` pointing at a report with a vocabulary of its own keeps working. The part-level `kind` stays the XML element name — it reports which element the evidence came from, and the converter capitalises it into the evidence heading ("Failure: …"). A test pins that divergence, and it is red-green verified: normalising `kind` too makes it fail. The declared `result` description now names the states, so a consumer reading the declarations out of a produced `needs.json` — a schema check, a metamodel validator, S-CORE's tooling — learns the field's domain from the file: ```json "description": "Test result status: passed, failed, error, skipped, disabled" ``` ## Breaking — what a project has to update 1. **A filter naming the value**: `'failure' == result` becomes `'failed' == result`. 2. **A custom `tr_report_template`**: it carries *two* such filters from the shipped template it was copied from. This is the dangerous one — see below. 3. **Custom CSS** targeting the generated `tr_failure` class, now `tr_failed`. The stylesheet keeps rules for both, so the colours survive either way. The value is also what `test-reports build needs` writes into `needs.json`, so a consumer of that file has to be updated with it. All of this is in the changelog entry. ## The untested coupling this surfaced The shipped `test_report_template.txt` filters on the result value twice, and it had **no test coverage at all**: every fixture exercising `test-report` pointed `tr_report_template` at a template of its own. A rename could have emptied its "Failed test cases" table and count in every project using the default, silently. A fixture now builds with the default template, and its count caught exactly that during this change — the parser started saying `failed` while the template still filtered `failure`, and the count went to zero. ## Verification * `pytest -n auto tests/` — **421 passed**, 0 failures (baseline before the change: 407) * `mypy` (strict) — **Success: no issues found in 14 source files**. The new module is type-checked rather than parked on the `useblocks/sphinx-test-reports#114` exclude list that both parsers sit on. * `nox -s toolchain_free` — 3.11 and 3.12 both successful; the new tests run with Sphinx, sphinx-needs and docutils all absent, so the normalisation stays toolchain-free. * `pre-commit run --all-files` — all hooks pass. The KOI8-encoded fixture template survived the edit (its test asserts on the decoded text). * `sphinx-build docs` — clean. The documentation's own pytest example, filtering `'failed' == result` against a real report, renders **"Failed test cases: 4"** and emits `tr_failed` 12 times, so the filter and the CSS class are verified end to end rather than by inspection. ## Follow-ups, deliberately not in this PR * `common.css` has no `tr_disabled` rule although the parser emits `disabled`, and no dark-theme variants at all. * Annotating a *reference* to a test-case need with its result (the S-CORE feature this came from) — that belongs in sphinx-needs' `need_ref`/`meta_links`, with STR supplying the `tr_<result>` palette. --- Targets `master`, adding to the `Unreleased` section; useblocks/sphinx-test-reports#160 gets rebased on top, which folds the entry into the 2.0.0 section. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Release v2.0.0 Bumps the version, retitles the version directives that were written against the planned 1.5.0, and turns the `Unreleased` changelog into the 2.0.0 section. ### Why 2.0.0 and not 1.5.0 Four changes in this cycle are breaking, so the major digit moves. The first three break an existing install; the fourth breaks project content instead: | Change | PR | What breaks | | --- | --- | --- | | Sphinx and Sphinx-Needs became the `sphinx` extra | useblocks/sphinx-test-reports#159 | A documentation project has to install `sphinx-test-reports[sphinx]`; the bare package no longer brings a toolchain | | Python 3.10 dropped | useblocks/sphinx-test-reports#147 | 3.11 is the floor | | Sphinx-Needs 6.0.1 / Sphinx 7.4 floors | useblocks/sphinx-test-reports#150 | The compatibility branches for older releases are gone | | A failed test case's `result` is `failed`, not `failure` | useblocks/sphinx-test-reports#161 | A filter naming the value (`'failure' == result`), a custom `tr_report_template` — which carries two such filters from the shipped template it was copied from — and custom CSS on the generated `tr_failure` class, now `tr_failed` | The docs were already written for 1.5.0, so every `versionadded` / `versionchanged` directive naming it is retitled here. ### Changes - **Version** → `2.0.0` in `pyproject.toml`, `docs/conf.py` (`version` = `2.0`, `release` = `2.0.0`) and `sphinxcontrib/test_reports/test_reports.py` (`VERSION`), matching the three places 1.4.0 touched. - **Version directives** → `2.0.0` in `docs/cli.rst`, `docs/pytest.rst`, `docs/install.rst` and the five in `docs/configuration.rst`. - **Changelog** → new `2.0.0` section, released 11.09.2026, with the `Unreleased` heading kept empty on top. ### Changelog notes The entry prose is unchanged; it is regrouped by category the way the 1.4.0 section is, and each entry now carries its PR link. Two deliberate edits: - The Python 3.10 drop and the Sphinx-Needs 6.0.1 / Sphinx 7.4 floors were labelled `Support:`. They are relabelled `Breaking:`, since they are what the major bump is for. - useblocks/sphinx-test-reports#154 (`packaging` dropped as a runtime dependency, moved into the `docs` and `test` extras) had no changelog line. Added as a `Support:` entry — it changes the installed dependency set, which is this release's theme. All 16 `Unreleased` entries survive; the new count is 17. Rebasing onto master after useblocks/sphinx-test-reports#161 merged folded in its two entries — the `Breaking:` rename and the `Improvement:` parser normalisation — bringing the section to 19. They are placed by the same category grouping, and the section's opening prose gained the rename as a fourth breaking change; its "Nothing else in an existing project has to change" sentence had to be inverted, since the rename makes it false. ### Verification - `uv run pytest -n auto tests/` → **421 passed** (Python 3.14, from a fresh `uv sync --all-groups --all-extras`), re-run on the rebased branch; 407 before useblocks/sphinx-test-reports#161, which added a test module. - `uv run sphinx-build -b html docs docs/_build/html` → `build succeeded.`, no warnings, so the rewritten changelog is valid reStructuredText. - `uv run pre-commit run --files <the 8 changed files>` → all hooks pass. - `grep -rn "1\.5\.0" docs/ sphinxcontrib/ pyproject.toml` → no matches left. - No test asserts the package version, so the bump needed no test changes. ### After merge Push a `2.0.0` tag. `.github/workflows/release.yaml` triggers on tags matching `[0-9].[0-9]+.[0-9]+`, which `2.0.0` does, and publishes to PyPI via trusted publishing.
…st-reports The whole of useblocks/sphinx-test-reports' `master` -- 161 commits, tip `ca74b80` (🚀 Release v2.0.0, useblocks/sphinx-test-reports#160) -- rewritten by `git filter-repo` so that every historical commit already places its files under `packages/sphinx-test-reports/`, and merged here with `--allow-unrelated-histories`. It is the third such import, after sphinx-mounts (#1850) and sphinx-codelinks (#1871). Note the qualification above, because this message is the one place the rule below could not reach: it was written by hand AFTER filtering, so nothing rewrote its references. An unqualified number here would autolink to the sphinx-needs issue that happens to carry it, which is exactly the error the cap exists to prevent -- so every sphinx-test-reports reference in this message is written out in full. `#1850` and `#1871` ARE sphinx-needs numbers and are correctly bare. Three things came with it: * **Tags.** Its ten releases arrive prefixed, `sphinx-test-reports-v0.3.6` ... `sphinx-test-reports-v2.0.0`. The prefix is load-bearing rather than hygienic: six of the ten bare names -- `1.0.1`, `1.0.2`, `1.1.0`, `1.2.0`, `1.3.0` and `2.0.0` -- are existing sphinx-needs releases, and `release_plan.py` reads every tag without `-v` in it as a sphinx-needs release. * **A scoped commit-message rewrite.** Bare `#N` became `useblocks/sphinx-test-reports#N`, 150 of them, bounded to that repository's own number range, 1 to 161. Exactly three references are deliberately left bare, all in one commit ("build: require sphinx-needs 6.0.1 and Sphinx 7.4"): `#1468`, `#1527` and `#1611` are **sphinx-needs** issues, and left bare they autolink here to the issues they actually mean. A lazy `#([0-9]+)` rule would have mangled all three into references that do not exist. * **The PlantUML jars, removed.** `--path-glob '*.jar' --invert-paths` strips three jar blobs -- 19.97 MB, 94 % of this import's permanent weight -- from every historical tree. The import adds 1.07 MiB of objects instead of 18.75 MiB. This workspace already vendors one PlantUML jar, once, at `vendor/plantuml/`, under a `verify-plantuml` gate. The imported tree is otherwise byte-for-byte the source clone at `ca74b80`; the commit map records the strip and the blob hashes. `git log packages/sphinx-test-reports/<path>` reads with no `--follow`: filter-repo rewrote the paths in every commit, so from this repository's point of view those files were always there.
**This is the arc's ONE `--no-verify` commit, and it has to be.** The merge before it made
`uv sync --frozen` fail instantly -- `[tool.uv.workspace] members = ["packages/*"]`
discovers the directory the moment it lands, so the lock is a member short and the
`uv-lock` prek hook, the `check-workspace` hook and the `ty` hook all fail together until
this commit relocks. Every commit after this one passes the hooks normally.
* **Layout.** `git mv sphinxcontrib src/sphinxcontrib`, the shape every other member has.
`sphinxcontrib/` stays a PEP 420 namespace with no `__init__.py`, which is why the
manifest has to declare `[tool.flit.module] name` at all.
* **The member manifest is truncated to what a member may own.**
`check_workspace.py` check (7) refuses `[dependency-groups]`, `[tool.ruff.lint]` and
`[tool.pytest.ini_options]` here -- the root owns all three -- and `[tool.mypy]` goes
because this workspace type-checks with ty, not because a fence says so. The `test` extra
goes with them: its contents are the root's shared `test` group, minus `nox` and
`packaging`, which the noxfile took with it. `requires-python` becomes `>=3.11,<4`
(check (3) is a string EQUALITY compare against the root, and a bare `>=3.11` fails it),
and both surviving `sphinx-needs` specifiers become `>=8.5.0,<9` (check (4)'s tight
tracking on an intra-workspace edge, `propagate_floors.py`'s to move at each release).
`[project.urls]` is repointed at this repository, and the dead lowercase `download-url`
dropped.
* **Root wiring, all eight edits**: `[project] dependencies`, the `docs-reports`
pass-through extra, `[tool.uv.sources]`, both `[tool.ruff] src` roots (isort decides
first-party by finding a module under one of them), `[tool.ty.src] include`, **the
`typecheck` task's explicit path list** -- ty intersects the CLI path with the include,
so the include alone would leave the member checked by nothing with every gate green --
the `toolchain` pytest marker relocated from the member, and `sphinx-design` into the
shared `test` group, without which three tests error at setup in every cell.
`dependabot.yml` gains the fourth `ignore` entry.
* **Poe tasks**, short name `reports`: `test-reports`, `test-reports-sphinx{7,8,9}`,
`docs-reports`, `docs-reports-clean`, `build-reports`, `import-check-reports`.
* **The lock gains one name, one entry, zero third-party distributions and zero bytes to
download**: 144 -> 145 packages, `+ sphinx-test-reports`, nothing removed and no version
moved. Both of this package's non-sibling needs were already here -- `lxml` through the
root `test` group, `sphinx-immaterial` through sphinx-needs' `theme-im` extra.
**This commit also adds `design/import-commit-map.txt`**, all 258 commits the rewrite
parsed. 161 of the right-hand hashes exist here (`master`); the other 97 lived only on the
29 branches that were not imported and resolve in the archived old repository through the
left-hand column. Its header also records what makes this import unfaithful in exactly one
respect -- the three stripped PlantUML jar blobs, by hash and byte count -- so that a
reader of the map does not have to find that out from a pull request body.
`check_workspace.py` now exits 0 with every check green.
Every file that existed because the package was its own repository.
**Retired**, each replaced by something this repository already has:
* `.github/workflows/{ci,coverage,release}.yaml` -- the four Extensions cells, the
Extensions cell's codecov upload, and this repository's one `release.yaml`. The
20-cell sphinx-needs axis that `ci.yaml` ran is the one real loss, and the changelog
entry names it rather than letting the CI diff say it.
* `noxfile.py` -- 36 sessions. Its `toolchain_free` lane does NOT retire with it: it is
the fence that keeps the converter and the pytest plugin importable with no
documentation toolchain installed, and it comes back as a scratch-venv CI job, because
no `uv sync` at this root can express it (every workspace environment installs every
member, and sphinx-needs declares sphinx at runtime). `plugin_floor` is deferred.
* `Makefile` and `docs/{Makefile,make.bat}` -- the poe tasks.
* `.pre-commit-config.yaml` -- the root's is the one config, and its `ruff` hook id is
even spelled differently here (`ruff-check`).
* `.yamlfmt` -- identical intent to the root's.
**Two things retire with NO replacement, and they are named here because nothing else in
the diff says so:**
* **`linkcheck`.** The retired `ci.yaml` ran one. The workspace's `docs.yaml` is scoped
`paths: [packages/sphinx-needs/docs/**]` and runs in that directory, so **this package's
documentation is link-checked nowhere** -- in a pull request that repointed three stale
links. Widening that job is a follow-up, not this import.
* **Three prek hooks**: `end-of-file-fixer`, `trailing-whitespace` and
`pretty-format-json`. The root's hook set is taplo-format, ruff-check, ruff-format,
yamlfmt, check-workspace, ty, uv-lock and check-readme -- there is no `pre-commit-hooks`
repository in it at all.
* `.gitignore` -- the root's covers all of it. Nothing was merged upward: its only
entries the root lacks are tox and editor leftovers (`.tox/`, `{envlogdir}`,
`.directory`, `.VSCodeCounter`), and one that would be actively wrong here, `uv.lock`.
* `CONTRIBUTING.md` -- this repository's contributing guide is
`packages/sphinx-needs/docs/contributing.rst`, which the root README links. Every
command in the retired file (`nox`, `make`, `uv sync --all-groups --all-extras`) is
wrong in a workspace; the last of those three is documented in `AGENTS.md` as a thing
that ALWAYS fails.
**Kept**, deliberately: `AUTHORS` (three named contributors -- nothing else in this
workspace records them, and a credits file deleted in an import is the kind of thing
nobody notices until someone does), `LICENSE` and `README.rst`, which every member has.
Two halves of one commit, and they cannot be separated: the `ty` prek hook fires on any
commit touching `packages/*/src/`, so a lint-only commit would be gated by a type check
that had not been done yet.
**ruff** -- the member's own `[tool.ruff.lint]` left with the manifest (check (7) refuses it,
because a member table REPLACES the root's configuration rather than extending it), so the
root's set applies here for the first time. 56 findings: 33 fixed by `--fix` (import sorting,
which appears only now that the root's isort roots name this package, plus
`UP006`/`UP035`/`UP037`), and 23 by hand:
* **`UP031` x3, deleted rather than rewritten** -- all three were
`plantuml = "java -jar %s" % os.path.join(cwd, "utils/plantuml.jar")` in the `custom_tr_*`
test projects, naming the jar the import removed. They were dead BEFORE the move: `cwd` is
`os.getcwd()`, which under pytest is the rootdir, where no `utils/` has ever existed.
* **`RUF043` x9** -- `pytest.raises(match=...)` patterns containing metacharacters, now raw
strings; every pattern was already being read as a regex and none changed.
* `SIM118` x5, `RUF015` x2, `UP007`, `RUF059`, `RUF028` (a `# fmt: skip` inside an `if (...)`
expression, which suppressed nothing), and `RUF001` on the deliberate Cyrillic in the
koi8-r fixture -- whose reason had to move OUT of the comment, or `RUF003` flags the
comment for containing the same character.
**No root per-file-ignore was needed**, unlike sphinx-codelinks. What the workspace set does
not carry that this package's did -- `FURB`, `PERF`, `PGH`, `PIE`, `SLF` -- is at zero
violations today, which is exactly why that loss is recorded in the changelog and the
package's `AGENTS.md` rather than left to a reader of the manifest diff.
**ty** -- 62 diagnostics over 9 files, now zero, and almost none of it by suppression:
* **~45 of them had ONE cause.** `TestCommonDirective.__init__` set eleven attributes to a
bare `None`, so every `self.results[0]`, `for x in self.results`, `self.extra_options[k]`
and `add_need(title=self.test_name, ...)` in the three directive subclasses was being
checked against `None`. They are now declared with the type each actually holds.
`test_file` and `test_id` start empty rather than `None`, and the two guards that enforced
their presence became falsiness checks. For `test_id` that is identical behaviour; for
`test_file` it is a fix, described below, and
`test_file_given = self.test_file[:]` stops being a crash for a directive written with no
`:file:`.
* **27 more were the three `add_need(**...)` splats**, whose keys come from configuration
(the report-path field is renameable). Each site now builds one `dict[str, Any]` first,
which is what a mapping splatted into individually-typed keyword parameters has to be.
* **The sphinx-needs < 4 branch is gone.** The import narrowed this member to
`sphinx-needs>=8.5.0,<9`, so the `make_hashed_id` fallback could no longer run -- and no
longer resolves either, the name having left `sphinx_needs.api` entirely (measured on
8.5.0). `_make_hashed_id` is the only spelling left.
* **TWO latent crashes fixed.** In `test_env.py` the `:raw:` + `:env:` branch iterated
`self.data_option_list` outside the `is not None` guard its sibling branch keeps it
inside, so a `test-env` written without `:data:` raised `TypeError`. And giving
`test_file` an empty default turns a second one into a readable error: the guard that was
meant to catch a directive written without `:file:` was DEAD CODE, because
`prepare_basic_options` runs before `load_test_file` in all four directives and
`test_file_given = self.test_file[:]` raised `TypeError` on `None` two lines before the
guard could be reached. The slice is now a no-op and the guard is live, so that mistake
raises `TestReportFileNotSetError`. The source comment says which of the two guards is
identical behaviour (`test_id`) and which is a fix (`test_file`).
* **Both come with a regression test**, in a new `tests/test_directive_regressions.py`
with three new fixture projects. Neither crash had a fixture that could reach it: no
`test-env` fixture combined `:raw:` and `:env:` without `:data:` (they carried `:data:`
with `:env:`, the same with `:raw:`, or no options at all), and every `test-file`
fixture carried a `:file:` -- which is precisely why nothing caught either. Each of the
two tests was mutation-proved against the code it covers: red on the published form,
green on the fix. The same module covers the missing-file error path, which no fixture
reached, and fences the `nodes.Text()` change below -- mutation-proved the same way --
with a warning filter scoped to docutils' exact deprecation AND to this package's module
-- without the filter an assertion on the rendered node passes on the old call too,
which was measured -- plus a positive control on the same pattern constant, so a
docutils rewording fails loudly instead of disarming the fence.
* `nodes.Text(text, text)` -> `nodes.Text(text)`. A deprecation cleanup, not a bug fix:
docutils ignores the second (`rawsource`) argument, warns that it is deprecated, and
removes it only in Docutils 2.0, so the old call works on every supported version. ty
reported it because the typing floor's docutils stubs already omit the parameter.
* **Four `# type: ignore` comments deleted** -- ty reports nothing at any of them and their
codes were mypy's. **FIVE narrow `# ty: ignore` remain**, each with its reason: two on
`schema={"type": ...}`, where sphinx-needs types the parameter as a union of TypedDicts
(a real API-shape mismatch, worth its own issue); one on the `option_spec` assignment
docutils types as optional; one on `app.tr_types`, an attribute attached to the
application object; and one on the `lxml` import, which ships no stubs and which
`allowed-unresolved-imports` cannot cover because the diagnostic is a "has no member"
rather than an unresolved module.
* The root gains `pytest`, `pluggy` and `_pytest.**` in `[tool.ty.analysis]
allowed-unresolved-imports`, exactly as `clang.**` is there: the `typing` environment
deliberately has no pytest, so this package's pytest plugin cannot resolve the runner it
plugs into. The plugin itself is still checked.
`error-on-warning = true` means every one of those suppressions must still be suppressing
something -- a stale one fails Lint, which is how the first placement of the
`add_extra_option` ignore was caught: ty reports that error on the argument, not the call.
The suite is unchanged throughout: **420 passed, 1 failed** before and after, the failure
being the pytest-playwright collision the next commit fixes.
**R7.** Three tests here spawned the build command resolved on `PATH` (`test_env.py`, `test_test_file.py`, `test_test_ctest_file.py`). The bare word is not the environment under test: from an unactivated shell, or any shell whose `PATH` carries another Sphinx first, the child process is a different Sphinx and a different sphinx-needs -- or none, and the test dies in `subprocess` with `FileNotFoundError` rather than reporting anything about the build. All three failed that way from a bare shell, which is useblocks/sphinx-test-reports#145. The fix is the workspace's, not this suite's: `sphinx_needs_testkit.sphinx_build_command` builds `[sys.executable, "-m", "sphinx", ...]`, the same entry point the console script wraps. `tests/test_subprocess_fence.py` is the one-line fence, reading SOURCE because there is no result to read -- a site that spawns the bare word passes in every environment where `PATH` happens to be right, which is every CI job here. sphinx-mounts carries the identical file; the helper's own docstring already named this package as its third consumer. **And the workspace-only failure, which is worth its own paragraph.** `test_an_inner_pytest_session_leaves_the_model_intact` drives an in-process `pytest.main()` inside a `pytester` session. In a developer's default `.venv` that process has every installed plugin loaded, and pytest-playwright -- from the root `js` group, which the default `dev` group includes -- keeps a module-global soft-assertion scope in its `pytest_runtest_call` wrapper, so the nested session dies with *nested soft assertion scopes are not supported*. Every CI cell syncs `--no-default-groups --group test --group sphinx-N`, where the plugin is absent. **So this suite would have been green in CI and red for anyone running `poe test-reports`** -- the shape the brief's own gate list warns about. The innermost session now passes `-p no:playwright`. That line has to stay a single line, and there is a comment saying so: `test_an_inner_session_that_fails_to_configure_leaves_it_intact` derives its own fixture from this one by replacing the literal `str(inner)]) == 0`, which a wrapped call breaks -- measured, it turned that sibling red. Default `.venv`: **422 passed**, from 420 passed + 1 failed (the collision) + the new fence.
`uv run poe docs-reports` is `-nW --keep-going`, like every docs task here. Five warnings,
each with its own cause.
**The renderer (3).** `docs/conf.py` pointed `plantuml` at `docs/utils/plantuml.jar`, a
second copy of the tool at a second version, which the import removed. It now resolves the
renderer through the chain the whole workspace agrees on -- `PLANTUML_JAR`, then the jar
committed at `vendor/plantuml/` that `vendor/plantuml/pin.toml` names, then a `plantuml`
executable on `PATH` -- written out here rather than imported, exactly as
`packages/sphinx-needs/docs/conf.py` writes it out and for the same reason: a docs build
must not import a test-only member. Of the three EXTENSION packages these are the only docs that render
(13 `needflow` directives across three pages -- sphinx-needs' own docs render far more), so
this is not decorative.
**The shipped template (1), and it is a user-facing bug.**
`src/sphinxcontrib/test_reports/directives/test_report_template.txt` ends with a
`literalinclude` of *itself* by a path with no `src/` in it, so the `src/` move broke it.
That file is not a docs page -- it is the DEFAULT `tr_report_template`, and it ships in the
wheel, so **every project using the default template already emits a literalinclude of a
path that exists only in this package's own checkout**. Verified in the published 2.0.0
wheel. The path is fixed here; the released bug is an issue of its own, because it affects
users and is not this pull request's to fix.
**A nitpick target (1).** `docs/pytest.rst` cross-referenced `apply_test_metadata` with
`:func:`, and these docs run no autodoc, so there is no target to resolve. It was invisible
upstream: Read the Docs' `fail_on_warning` is `-W` without `-n`, and the retired Makefile
passed neither.
Beside those, three links that the move made wrong: the issue tracker in `docs/support.rst`
(now this repository's, with a note to set the `pkg:` label), the theme's `repo_url`, and
the `README.rst` logo, which pointed at `raw.githubusercontent.com/.../sphinx-test-reports`
and would have gone stale the moment that repository was archived.
Finally, `docs/changelog.rst`'s `Unreleased` section loses `:Released: under development`.
That line is not cosmetic: `poe bump` refuses to run at all while the newest `:Released:` is
neither `DD.MM.YYYY` nor `YYYY-MM-DD`, so until it went, this member could not have been
released from this workspace.
build succeeded. (0 warnings, -nW --keep-going)
**R11.** The file keeps its name and moves nowhere -- it arrived at `packages/sphinx-test-reports/.readthedocs.yaml` with the import -- but every path inside it was relative to the old repository root and is now relative to THIS one, which is what Read the Docs' monorepo guide requires and what both sibling packages already do. Three things differ from the siblings, each for a reason: * **`apt_packages: [default-jdk, graphviz]` stays.** Both SIBLING extensions' configs deliberately install none, because those docs draw nothing; these render 13 `needflow` directives across three pages. (sphinx-needs' own documentation renders too, and far more -- 48 of them -- and its config installs the same two packages. The contrast is with sphinx-mounts and sphinx-codelinks, not with every docs build here.) The jar is not installed -- `docs/conf.py` resolves the one committed at `vendor/plantuml/`, which is outside this package but inside the checkout RTD makes, so the build reaches the network for nothing beyond its own pip install. * **Two `python.install` entries, the sibling first**, as sphinx-codelinks has: a member installed alone would resolve `sphinx-needs` from PyPI rather than from the checkout beside it, and the build could then pass or fail on code this workspace does not have. * **The `exit 183` `post_checkout` block**, so that a pull request touching nothing under this package cancels the build instead of spending a runner on it. The comment records the one input outside the package that this filter deliberately ignores -- `vendor/plantuml/` -- and why that is safe. `os` and `tools.python` move to `ubuntu-24.04` / `3.13`, matching both siblings and the root `.python-version`, from `ubuntu-22.04` / `3.12`. The old `pre_create_environment` job (`echo "DOT details"; which dot`) was a debugging aid and does not survive. **The RTD project's own settings are not in this pull request and cannot be**: the repository URL has to be repointed at `useblocks/sphinx-needs` and `readthedocs_yaml_path` set to this file, both in the project's admin page, by someone with access (the project's users are danwos and ub_marco). The pull request body carries that checklist.
**R12.** One more pytest step and one more Codecov upload in the Extensions cell, in the sphinx-codelinks shape, plus the fourth `codecov.yml` project status at `target: auto` -- this package arrived with an import and its coverage number has never been measured here. `reports.xml` joins `mounts.xml` and `codelinks.xml` in `.gitignore`: the cell runs three suites in one working directory, so each names its own report or the uploads all carry the first one's numbers under different flags. **No new tool installs.** Nothing in this suite renders a diagram, so the graphviz and PlantUML the cell already installs for sphinx-mounts are not for it, and it needs no libclang and no browser. The marginal cost is the ~14 s the suite takes, on a 1-3 minute job, times four cells. `--cov=sphinxcontrib.test_reports` is the IMPORT name, which for this member is not the distribution name with `-` -> `_`: it installs into the `sphinxcontrib` PEP 420 namespace. **A NEW job, `toolchain-free`.** The converter and the pytest plugin are meant to run where the documentation toolchain is not installed -- that is why Sphinx and sphinx-needs are an extra of this package rather than dependencies -- and the retired noxfile's `toolchain_free` session was the only thing keeping that true. It cannot become a matrix cell, and the reason is structural rather than a configuration gap: the root's `[project] dependencies` name every member, those are installed in EVERY environment, and sphinx-needs declares sphinx at runtime, so every environment a `uv sync` at this root can produce has Sphinx in it. The job therefore builds its environment outside the project with `uv pip install --no-sources`, exactly as `release.yaml`'s compat cell does, **asserts that sphinx, sphinx_needs and docutils are all absent** -- without which the job would pass while testing nothing it claims to -- and runs the ten toolchain-free modules with `-m "not toolchain"`. ~20 lines, ~40 s. `plugin_floor`, the noxfile's other lane, is deliberately not here; it belongs with the release that makes the plugin a shipped surface of its own. **A `Docs reports` job**, in the `docs-codelinks` shape plus the renderer these docs need (graphviz, and `PLANTUML_JAR` pointed at the vendored jar). R9 and R11 took this build from five warnings to zero, and without a job nothing holds that: the Read the Docs build is not a required check, it is `-W` without `-n`, and that project's settings do not point at this repository yet. sphinx-mounts has no docs job either, so this is a deliberate departure from that precedent rather than an oversight -- the difference is that these docs RENDER, which is the failure mode a build catches and review does not. The job's PlantUML step is the workspace's one capture shape -- `shell: bash`, `tr -d` on the capture, the export on the next line -- and **`tools/tests/test_ci_plantuml_steps.py` counts it**: that fence hard-codes how many CI steps run `fetch_plantuml.py` and says adding a rendering job means raising the numbers deliberately, "so that the addition is a decision someone made rather than a step nobody checked". This is that decision: eight sites, seven captures. (Both counts' prose was already one ahead of the constants -- the comment said seven captures while asserting six, and a docstring already called the Lint check "the eighth site" -- so after the bump the text and the numbers agree for the first time.) **And a fix to a hole that predates this pull request.** The Lint job's type-gate canary -- the step that plants a deliberate type error per package and demands the failure name the probe, because a stale `[tool.ty.src] include` makes ty check zero files and exit 0 -- probed only `sphinx-needs` and `sphinx-mounts`. **`sphinx-codelinks` was already missing from it**, so the gap was one package wide before this import and would have been two after. Both are added. It is three tokens on one line, and it is the difference between the gate being checked and being believed.
…t cell its extras
**R13, two defects with one fix each. Both are invisible until a release job runs**, which
is why they are fixed now rather than found at the worst possible moment.
**1. `module=${DIST//-/_}` derives a module that does not exist.** For this member that is
`sphinx_test_reports`, and the package installs into the `sphinxcontrib` PEP 420 namespace:
the real import name is `sphinxcontrib.test_reports`, declared in `[tool.flit.module]`. The
`import_check` walk would have failed with `ModuleNotFoundError` on every release of it. The
step now reads that key and falls back to the derivation for the three members that declare
no such table -- `check_workspace.Member.module` and `import_check.py` have both honoured it
from the start, so this brings the third reader into line. The comment it replaces predicted
this member by name. Verified against all four manifests: `sphinx_needs`, `sphinx_mounts`,
`sphinx_codelinks`, `sphinxcontrib.test_reports`.
**2. The compat cell installs the wheel with no extras, and 2.0.0 put Sphinx in one.**
Measured on the built wheel: `Requires-Dist: lxml`, full stop -- both Sphinx and sphinx-needs
sit behind the `sphinx` extra, so that the converter and the pytest plugin install without a
documentation toolchain. Run by hand, the cell fails the `import_check` walk
`6 of 25 modules ... No module named 'sphinx_needs'`.
The workflow already has exactly the right mechanism and needed no change for this half: a
per-member `compat-requirements.txt`, installed with `-r` before the wheel and passed again
as a `-c` constraint. Both siblings use it, each with a comment explaining why theirs exists;
this one explains what is different about this member -- it is the first here whose RUNTIME
dependencies are optional, so the file covers the package's own requirements rather than a
test-only need.
**R15.** Four lists, one new file and one shim. * **`.github/labeler.yml`** and **`.github/issue-labeler.yml`**, plus the "Package" dropdown of both issue forms. These go together and cannot be split: the issue labeler runs with `sync-labels: 1`, so an option offered by a form with no pattern behind it means the label is SYNCED AWAY rather than merely not added. The comment about no two names containing another as a `\b`-delimited word is restated for four names, since it is an invariant someone has to re-check when a fifth arrives. * **The root `README.md` table gains TWO rows**, not one: **sphinx-codelinks was already missing from it**, so the table has listed two of three packages since that import. It now lists four of four. * **`packages/sphinx-test-reports/AGENTS.md`**, the package delta, with `CLAUDE.md` as the usual one-line shim. It carries the paragraph **R14 requires**: `check_workspace.py` check (5) prints no line at all for this member, for two independent reasons — a dotted module name that `module_version()` can never resolve, and no `__version__` literal anywhere, which the check treats as "not an error" by design. Silence that nobody wrote down is the failure mode; a documented gap is not. It also records what the move COST -- the sphinx-needs axis, five ruff families, `plugin_floor`, and the theme still being sphinx_immaterial -- none of which is visible in a diff. * **The root `AGENTS.md`** gains the member's row, its two commands, its RTD path, the renderer facts (the suite needs none; the docs need both -- of the three extension packages, the only docs that render, though sphinx-needs' own render far more), the `pkg:` label, and the correction from "all three packages ship a `tests/__init__.py`" to four. The history section now names three imports rather than two, and says that this one's commit map records the stripped jars.
**R16.** The entry says what the import COSTS, not merely that the package moved, because none of it is visible in a diff and the CI diff is a poor place to learn it. * **The Sphinx-Needs axis goes from five tested versions to one.** The retired noxfile ran 6.0.1, 6.3.0, 7.0.0, 8.0.0 and 8.5.0; the workspace tests against the sibling in the tree, varying the Sphinx series instead. The declared floor narrows with it, from `sphinx-needs>=6.0.1` to `sphinx-needs>=8.5.0,<9`. Users on an older Sphinx-Needs keep 2.0.0; they do not silently get a package that no longer supports them. * Five ruff families, plus four more rules the shared set ignores (`B904`, `ICN001`, `ISC004`, `N818`); `plugin_floor`; **`linkcheck`, which retires with no replacement** because the workspace's link-check job is scoped to Sphinx-Needs' own docs; three formatting hooks; and mypy's fifteen-module `exclude`. **The shipped-template bullet does not claim a user-facing fix, because there is not one.** `literalinclude` resolves against the document doing the including, which for a user is somewhere in their own docs tree -- so no path written in the shipped template can be right for both that user and this repository. What this pull request fixed is this package's own documentation build. The bullet says exactly that, says the self-include is tracked separately, and points at the workaround: copy the template, which is what `tr_report_template` is for, and remove the `literalinclude` directive at its end -- both of its lines, or the whole Template section, since removing only the first leaves a stray `language: rst` field list. An earlier draft of this entry said "the path is fixed", which would have told users a bug was closed that is still open for them. The entry also carries the three bug fixes this pull request does make -- the `test-env` branch that raised `TypeError` without `:data:`, a directive written without `:file:` that raised `TypeError` instead of a readable error, and this repository's own template path -- and one deprecation cleanup, the second argument to docutils' `Text()`, which is deliberately NOT filed as a bug: docutils ignores that argument and removes it only in Docutils 2.0, so on every supported version the old call works. The entry also gives the four facts a user of the old repository needs: the new repository, where issues go, the prefixed tags, and that **the published package does not change** (same distribution, same import name, same command). `poe docs-reports` still builds clean with it.
The Extensions windows-latest cell, this suite's first run on Windows, failed two tests in `tests/test_project_config.py::TestLoader`. Both are POSIX assumptions in tests added with useblocks/sphinx-test-reports#145, whose CI only ever ran on ubuntu-latest. Neither is a product bug: * `test_unreadable_file_is_a_config_error` makes its file unreadable with `chmod(0o000)`. On Windows `os.chmod` only sets the read-only attribute, so the file stays readable and nothing raises ("DID NOT RAISE TomlConfigError"). Its existing skip covered only root on POSIX; it now also skips on win32, with that reason. The behaviour it covers, wrapping an `OSError` from the open as a `TomlConfigError`, is platform-independent code. * `test_absolute_paths_stay_untouched` wrote `tmp_path` into a TOML basic string. A Windows path's backslashes are escape sequences there -- `\U` starts a unicode escape -- so the file was invalid TOML ("Invalid hex value (at line 2, column 16)"), which the loader correctly reported. The value is now a TOML literal string, which processes no escapes, so the test checks the same thing on every platform. Measured locally: a Windows-shaped path in a basic string reproduces CI's exact `TOMLDecodeError`, and the same path in a literal string parses equal to itself. The module passes 78 and the package suite 426 on macOS; the Windows cell is the proof for both changes.
…st-reports (#1931) > **Merge with "Create a merge commit"** — not squash. The whole point is the preserved history, > and `merge_commit_title` is already *Pull request title*, so master's first-parent line reads > this title with no hand-editing. `allow_merge_commit` is `false` today; the steps are under > *Before merging* below. `useblocks/sphinx-test-reports` moves into this workspace as `packages/sphinx-test-reports/`, with its history, its ten release tags and its release path. It is the fourth distribution here — and the first whose runtime dependency on a sibling is **optional**, which is where most of the interesting parts below come from. ### What the import is The whole of that repository's `master` — **161 commits, tip `ca74b80`** (🚀 Release v2.0.0) — rewritten by `git filter-repo` so that every historical commit already places its files under `packages/sphinx-test-reports/`. 227 files in the merge, and nothing outside the package. - **Tags.** Its ten releases arrive prefixed, `sphinx-test-reports-v0.3.6` … `sphinx-test-reports-v2.0.0`. The prefix is load-bearing: **six of the ten bare names — `1.0.1`, `1.0.2`, `1.1.0`, `1.2.0`, `1.3.0`, `2.0.0` — are existing sphinx-needs releases**, and `release_plan.py` reads every tag without `-v` in it as a sphinx-needs release. - **A scoped commit-message rewrite.** Bare issue numbers became `useblocks/sphinx-test-reports#N`, 150 of them, bounded to that repository's own number range, 1 to 161. **Exactly three references are deliberately left bare**: #1468, #1527 and #1611, all in one commit (*build: require sphinx-needs 6.0.1 and Sphinx 7.4*), are **sphinx-needs** issues — ⬆️ Drop Python 3.9, 👌 Add schema to `add_extra_option`, ✨ Add `needs_fields`. Left bare they autolink here to exactly the issues they mean; a lazy rule would have mangled all three into references that do not exist, which was measured by running it. The merge commit's own message was written by hand after filtering, so the rule never reached it: every sphinx-test-reports reference in it is written out in full for the same reason. - **The commit map**, at `packages/sphinx-test-reports/design/import-commit-map.txt`. Ten released versions' GitHub Releases, changelog entries and PyPI page are full of the old hashes; the old repository is archived rather than deleted, and this file carries them across. - **One deliberate departure from a byte-for-byte copy — the PlantUML jars.** The filter also stripped **three jar blobs, 19.97 MB**, from every historical tree. Unstripped the import adds **+18.75 MiB** of objects to a repository whose whole object set is ~101.6 MiB; stripped, **+1.07 MiB**. This workspace already vendors one PlantUML jar, at `vendor/plantuml/`, under a `verify-plantuml` gate. **The imported tree differs from the source by exactly those jars**, and that is checkable. Run it against the **import commit**, `918f3cd3`, which is what the claim is about: ```bash git clone https://github.com/useblocks/sphinx-test-reports.git /tmp/str mkdir -p /tmp/a /tmp/b git -C /tmp/str archive ca74b80 | tar -x -C /tmp/a git archive 918f3cd:packages/sphinx-test-reports | tar -x -C /tmp/b diff -rq /tmp/a /tmp/b # Only in /tmp/a/docs: utils <- contained plantuml.jar, nothing else # Only in /tmp/a/tests/doc_test/utils: plantuml.jar ``` At HEAD the same command prints about sixty lines, because the ten commits after the merge change the package — that is the point of them, and each is described below. (A reviewer of this branch also compared **all 161 imported trees** against the source by mode and blob sha, not just the tip: zero mismatches.) ### How to read the history afterwards `git log packages/sphinx-test-reports/<path>` works **with no `--follow`** — filter-repo rewrote the paths. `git blame` needs nothing, and `git log --first-parent master` shows the import as one line. ### If you have an open pull request in the old repository **Once this pull request is merged**, its commits move across with their authorship. Not before: until then `master` here has no `packages/sphinx-test-reports/`, and the cherry-pick below stops with modify/delete conflicts — measured, on useblocks/sphinx-test-reports#68. The property that makes it work is that filter-repo's rewrite is deterministic: filtering your branch with **exactly** the rules below puts its history on the very commit this import merged, so only your own commits need applying. ```bash # clone pr-move NEXT TO your checkout of useblocks/sphinx-needs: the second half reaches it as ../pr-move git clone https://github.com/useblocks/sphinx-test-reports.git pr-move && cd pr-move git fetch origin '<your-branch>:pr-branch' # for a fork: git fetch https://github.com/<you>/sphinx-test-reports.git '<your-branch>:pr-branch' printf '%s\n' 'regex:(?<![\w/])#([1-9]|[1-9][0-9]|1[0-5][0-9]|16[01])\b==>useblocks/sphinx-test-reports#\1' > ../rules.txt uvx git-filter-repo --to-subdirectory-filter packages/sphinx-test-reports \ --tag-rename ':sphinx-test-reports-v' --replace-message ../rules.txt \ --path-glob '*.jar' --invert-paths --force # then, in your checkout of useblocks/sphinx-needs, beside pr-move: git remote add prmove ../pr-move git fetch prmove master pr-branch git checkout -b <new-branch-name> origin/master # a name not already used here, e.g. str-pr-68 git rev-list --reverse --no-merges "$(git merge-base prmove/master prmove/pr-branch)..prmove/pr-branch" \ | git cherry-pick --stdin ``` Three details differ from the sphinx-codelinks import's recipe, and each was learned by running it: - **`--path-glob '*.jar' --invert-paths` is not optional.** This import stripped the jars, so a filter without the strip produces a different history: measured, `master` comes out as `206c6494` instead of `ffdf962e`, and nothing you cherry-pick lands on the imported commits. - **The old repository's default branch is `master`**, and the branch is fetched as `pr-branch`, so that a fork whose branch is itself called `master` does not collide with the clone's own. For the same reason the branch you create here takes a **new** name: every sphinx-needs checkout already has a local `master`, and `git checkout -b master` there stops with `fatal: a branch named 'master' already exists`. - **`--no-merges`.** A plain `git cherry-pick <base>..<branch>` stops at the first merge commit in your branch. Dropping merges is safe only when they resolved nothing: check with `git show --cc <merge>`, which prints a diff only for hunks resolved by hand. If any merge in your branch did resolve a conflict, **rebase your branch onto `master` in the OLD repository first**, which linearises it, and then move it. **Proven end to end on useblocks/sphinx-test-reports#68**, the one open pull request from an outside contributor — a fork branch named `master`, one commit plus one merge of the upstream branch, and behind `master`. The merge was trivial (`git show --cc` is empty); the move applied **one commit with zero conflicts**, following the file's rename into `src/` on its own; **the original author is preserved** on it (the committer becomes whoever runs the recipe); and the net change of each of its three files is byte-identical to the pull request's. On the moved branch `ruff check`, ty, its own seven tests, the whole package suite and the subprocess fence are all green. The one thing it needs is a `ruff format` pass over `tests/test_tr_link.py`. useblocks/sphinx-test-reports#68 is still mergeable in the old repository, but that repository's `master` has not moved from `ca74b80` since this import was cut, so **merging it there now would leave it out of this import**. Moving it with the recipe is the route. Then resolve anything this import changed under your branch, and push. The list, most likely first: - **formatting and lint** — the workspace's `ruff format` and `ruff check` now apply to this package (on useblocks/sphinx-test-reports#68, one file needed formatting and nothing else); - **the member's `pyproject.toml`** — `[dependency-groups]`, `[tool.ruff.lint]`, `[tool.pytest.ini_options]` and `[tool.mypy]` no longer exist there, and there is no `test` extra; a new test dependency goes in the ROOT `test` group; - **files this import retired** — `noxfile.py`, `Makefile`, `.github/`, `.pre-commit-config.yaml`, `.yamlfmt`, `CONTRIBUTING.md` — which a branch that edits them hits as a modify/delete conflict; - **the directive modules the type-checking port rewrote**, above all `directives/test_common.py`; - **a test that spawns `sphinx-build`** must build its argv with `sphinx_needs_testkit.sphinx_build_command`, or the subprocess fence fails. A file that moved under `src/` is not on this list: the cherry-pick follows the rename. Two further pull requests there come from maintainers and **already conflict with `master`** in the old repository: useblocks/sphinx-test-reports#117 (mypy fixes, 29 commits, 21 files — much of what it touches is retired or re-done here with ty) and useblocks/sphinx-test-reports#63 (the JUnit `error` attribute). Whether to move or close them is left to their authors. ### A warning for anyone grepping the diff **`tests/doc_test/utils/*.xml` contain the literal string `sphinxcontrib/`** — for example `file="sphinxcontrib/test_reports/junitparser.py"`. They are **fixture payloads** describing a historical pytest run: test data, not paths anything opens. A `src/`-move `sed` would corrupt them silently. They are untouched, and their 23 files are byte-identical to the import. ### What changed beyond the move One commit each. 1. **Wiring and the relock** — the one `--no-verify` commit, and it has to be: `members = ["packages/*"]` discovers the directory the moment the merge lands, so `uv sync --frozen` and three hooks fail together until the lock catches up. The manifest is truncated to what a member may own; `requires-python` becomes `>=3.11,<4`; both `sphinx-needs` specifiers become `>=8.5.0,<9`. **The lock gains one name, one entry, zero third-party distributions and zero bytes to download.** This commit also adds the commit map, and deletes 22 `sys.path.insert(… "../../sphinxcontrib")` lines from fixture `conf.py` files: the `src/` move is what made that path false. 2. **Retirements.** The noxfile, Makefiles, standalone CI, pre-commit and yamlfmt configs, `.gitignore`, `CONTRIBUTING.md`. `AUTHORS` is kept. **Two things retire with no replacement**, and are named because nothing else in the diff says so: `linkcheck` (the workspace's `docs.yaml` is scoped to sphinx-needs' docs) and three prek hooks (`end-of-file-fixer`, `trailing-whitespace`, `pretty-format-json`). 3. **ruff + ty.** 56 ruff findings and **62 ty diagnostics, now zero**, ported here rather than excluded, because an unchecked member is invisible. ~45 of the 62 had one cause: eleven directive attributes initialised to a bare `None`. **Five** narrow `# ty: ignore` remain, each with its reason. The regression tests below land here, beside the fixes they cover. 4. **The subprocess fence.** Three tests spawned `sphinx-build` resolved on `PATH`; they now go through `sphinx_needs_testkit.sphinx_build_command`, with the one-line fence sphinx-mounts carries. **And a failure that would have been green in CI and red for every developer**: an in-process `pytest.main()` collides with pytest-playwright in the default `.venv`, which no CI cell has. Fixed with `-p no:playwright` on the inner session. 5. **Docs** — clean under `-nW --keep-going`, from five warnings. 6. **Read the Docs config**, re-rooted at the repository. 7. **CI** — one pytest step and one Codecov upload in the Extensions cell (flag `reports`); a new **`toolchain-free`** job, the fence that keeps the converter and the pytest plugin importable with no documentation toolchain (it cannot be a matrix cell, because every workspace environment installs every member and sphinx-needs declares sphinx at runtime); and a new **`Docs reports`** job, since nothing else would hold the zero-warning build. That job's PlantUML step is counted by `tools/tests/test_ci_plantuml_steps.py`, whose site counts go 7 → 8 and 6 → 7 — the fence asks for exactly that decision to be made explicitly. 8. **The release workflow.** The compat cell installs a bare wheel whose `Requires-Dist` is `lxml` alone, so this member gets a `compat-requirements.txt`; and `module=${DIST//-/_}` derived a module that does not exist, so the step now reads `[tool.flit.module] name`. **Exercised by hand**: without the file the import walk fails 6 of 25 modules; with it, 25 of 25 and the suite passes against published dependencies. 9. **The lists that name the packages**: labeler, issue labeler, both issue forms, the root README (which was **already missing sphinx-codelinks** — it now lists four of four), and `AGENTS.md`. 10. **The changelog.** 11. **Two loader tests made portable to Windows**, after CI's first Windows run of this suite (see below). ### Three bug fixes, one deprecation cleanup, and one thing that is NOT fixed - 🐛 **A `test-env` with `:raw:` and `:env:` but no `:data:` raised `TypeError`** — one branch iterated outside the guard its sibling keeps it inside. No fixture had that shape. - 🐛 **A `test-file` written without `:file:` raised an unhandled `TypeError`.** The guard meant to catch it was dead code, reached only after a slice of the missing value. It now raises `TestReportFileNotSetError`. No fixture had that shape either. - 🐛 **This repository's own docs build** could not resolve the report template's `literalinclude` after the `src/` move. - 👌 **`nodes.Text(text, text)` → `nodes.Text(text)`.** Not a bug: docutils ignores the second argument, deprecates it, and removes it only in Docutils 2.0. -⚠️ **The shipped default report template's self-include is still broken for users, and this pull request does not fix it.** `literalinclude` resolves against the *including* document — for a user, somewhere in their own docs — so no path written in the shipped file can be right for both them and this repository. Filed as #1932. Until it is fixed a project can copy the template and remove the `literalinclude` directive at its end — **both** lines, or the whole *Template* section; removing only the first leaves a stray `language: rst` field list. **Each of the three code changes above — the two crashes and the `Text()` cleanup — comes with a mutation-proved regression test**, in a new `tests/test_directive_regressions.py` with three new fixture projects: red on the published code, green on the fix. The fourth item, the docs build's own template path, is not a pytest; the `Docs reports` job holds it, and it fails on the unfixed path. The `Text()` test needed a warning filter scoped to docutils' exact deprecation message **and** to this package's module — without it an assertion on the rendered error node passes on the old call too, which was measured — and a positive control on the same pattern constant, so that a docutils rewording fails loudly instead of disarming the fence. ### Two pre-existing gaps fixed on the way past - **The Lint job's type-gate canary probed only two of the packages.** `sphinx-codelinks` was already missing, so the hole was one package wide before this import. Both are now probed. - **The root README table was missing `sphinx-codelinks`.** ### What this costs, stated rather than left to the CI diff - **The sphinx-needs axis goes from five tested versions to one**, and the declared floor narrows with it, from `sphinx-needs>=6.0.1` to `>=8.5.0,<9`. Accepted. - **Five ruff families** (`FURB`, `PERF`, `PGH`, `PIE`, `SLF`) are no longer enforced, and four more rules this package enabled are ignored by the shared set (`B904`, `ICN001`, `ISC004`, `N818`). All nine are at zero violations today. - **`linkcheck`** and three formatting hooks retire with no replacement. - **`plugin_floor`** has no replacement yet. - The docs keep `sphinx_immaterial`; converging on furo has its own issue. ### One effect on a sibling suite, which pass counts alone would hide Putting `sphinx-design` in the shared `test` group moves one **sphinx-needs** test from **skipped to passing**: `test_needreport.py::test_sphinx_design_dropdown_output_is_unchanged`, guarded by `skipif(not SPHINX_DESIGN_INSTALLED)`. The direction is good; it is stated because a pass count cannot show a skip becoming a pass. | | before this branch | this branch | |---|---|---| | `poe test-needs` | 1804 passed, **12 skipped** | 1805 passed, **11 skipped** | | `poe test-mounts` | 944 passed, 2 deselected | 944 passed, 2 deselected | ### Windows Everything was measured locally on macOS and three Linux-shaped cells, so the Extensions `windows-latest × sphinx-9` cell was this suite's first run on Windows. The old repository's CI ran on `ubuntu-latest` only. That first run failed two tests in `tests/test_project_config.py::TestLoader`, both added with useblocks/sphinx-test-reports#145, and **both test defects rather than product bugs**: - `test_unreadable_file_is_a_config_error` makes its file unreadable with `chmod(0o000)`. On Windows `os.chmod` only sets the read-only attribute, so the file stays readable and nothing raised. It now skips on Windows, as it already did for root on POSIX. - `test_absolute_paths_stay_untouched` wrote `tmp_path` into a TOML basic string, where a Windows path's backslashes are escape sequences, so the file was invalid TOML — which the loader correctly reported. It now uses a TOML literal string and checks the same thing on every platform. Fixed in `d68d10df`. On it the Windows cell's sphinx-test-reports step reports: | run | passed | skipped | failed | |---|---|---|---| | `f4075034` | 423 | 1 | **2** | | `d68d10df` | 424 | 2 | 0 | The three converted subprocess sites, the part expected to be Windows-sensitive, passed on the first run. ### Gates Every figure below was measured locally on `f4075034`. The one commit after it, `d68d10df`, changes two tests for Windows and nothing else (see *Windows* above), and CI runs the full matrix on it. ``` poe lint 8/8 hooks poe test-reports 426 passed poe typecheck All checks passed cells sphinx-7 / 8 / 9 426 passed each poe docs-reports 0 warnings pytest tools/tests 313 passed pytest --collect-only 2175, exit 0 check-workspace all green import_check --walk 25 modules compat cell (by hand) sphinx-needs 8.5.0 from PyPI, 25 modules, 426 passed mutation proofs each proof red when its fix is reverted, green restored; the positive control red when its pattern is broken ``` `check_workspace.py` check (5) prints **no line** for this member, by design and for two independent reasons (a dotted module name, and no `__version__` literal); the package's `AGENTS.md` says so, and the release that renames the module dissolves both. ### Follow-up candidates, not in this pull request - the shipped template's self-include — #1932; - converging the docs theme on furo; - widening `docs.yaml`'s linkcheck to this package; - `schema={"type": …}` against sphinx-needs' TypedDict union — a real API-shape mismatch, suppressed here with a reason; - `plugin_floor`; - three things no gate currently fences: a typo in the labeler glob (which is what applies `pkg: sphinx-test-reports` to every future pull request), the `[project.urls]` values, and the `toolchain` marker line — removing any of them leaves every gate green. ### Before merging - [x] Create the label **`pkg: sphinx-test-reports`**, and **apply it to this pull request by hand**. The `pull_request_target` labeler reads the default branch's configuration, which does not know this package until this merges, so it cannot apply the label here however early the label exists. It must exist before the merge and before any issue is transferred. - [x] File the follow-up issue for the shipped template's self-include, carrying that label — #1932. - [ ] Flip `allow_merge_commit` **on** immediately before the merge, merge with **Create a merge commit**, and flip it **off** immediately after. ### After the merge - [ ] Push the ten `sphinx-test-reports-v*` tags, **before** creating any Read the Docs automation rule (rules run on version creation). - [ ] Read the Docs: repoint the project's repository URL at `useblocks/sphinx-needs` and set `readthedocs_yaml_path` to `packages/sphinx-test-reports/.readthedocs.yaml`. **Needs danwos or ub_marco.** - [ ] Transfer the open issues, each with the new label; close useblocks/sphinx-test-reports#149 with a pointer here; point the useblocks/sphinx-test-reports#68 contributor at the recipe above; pin a "this repository has moved" issue; archive rather than delete the old repository. - [ ] Delete the superseded `import/sphinx-test-reports` branch. `Closes` nothing — useblocks/sphinx-test-reports#149 is closed by hand in the other repository. --------- Co-authored-by: Daniel Woste <daniel.woste@useblocks.com> Co-authored-by: saifsayyad <44859469+saifsayyad@users.noreply.github.com> Co-authored-by: Peter Tillemans <pti@melexis.com> Co-authored-by: Lasse Benn Nørregaard <lassenorregaard@gmail.com> Co-authored-by: James Barr <James.Matthew.Barr@gmail.com> Co-authored-by: Bence Bognar <beni20180722@gmail.com> Co-authored-by: Martin <54633070+mawieland@users.noreply.github.com> Co-authored-by: Martin <martin.wieland@gmx.de> Co-authored-by: Duodu Randy <duodurandy19@gmail.com> Co-authored-by: haiyangToAI <haiyang.zhang@tutanota.com> Co-authored-by: Joerg Kreuzberger <j.kreuzberger@procitec.de> Co-authored-by: Maximilian Sören Pollak <maximilian.pollak@expleogroup.com> Co-authored-by: Gabriel Fougeron <gabriel.fougeron@hotmail.fr> Co-authored-by: Nick Touran <ntouran@terrapower.com> Co-authored-by: Marco Heinemann <marco.heinemann@useblocks.com> Co-authored-by: korbi-web-215 <149482188+korbi-web-215@users.noreply.github.com> Co-authored-by: Patrick Dahlke <patrick.dahlke@pichu.io> Co-authored-by: Christian Polzer <119361+cpolzer@users.noreply.github.com> Co-authored-by: ~chrstian polzer <~c.polzer@hai-fai.de> Co-authored-by: Christian Polzer <Christian.Polzer@gmail.com> Co-authored-by: christian polzer <c.polzer@hai-fai.de> Co-authored-by: Miro <natsubeloud@gmail.com> Co-authored-by: Miro <200482516+Mirochill@users.noreply.github.com>
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 10.0.1 to 10.1.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/setup-uv/releases">astral-sh/setup-uv's releases</a>.</em></p> <blockquote> <h2>v10.1.0 🌈 New output <code>python-runtime-id</code>and respect NO_PROXY</h2> <h2>Changes</h2> <p>This release adds more bheind the scene security improvements and also 2 small improvements.</p> <h3>NO_PROXY</h3> <p>This action now respects <code>no_proxy/NO_PROXY</code> environment variables which were previously ignored.</p> <h3>New output <code>python-runtime-id</code></h3> <p>The new output <code>python-runtime-id</code> can be used to know which python version exactly was installed if you use <code>activate-environment</code>. See <a href="https://redirect.github.com/pyca/cryptography/pull/15572#discussion_r3913508686">pyca/cryptography#15572</a> for details on why this can be useful.</p> <h2>🐛 Bug fixes</h2> <ul> <li>fix: respect no proxy directive <a href="https://github.com/mj0nez"><code>@mj0nez</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1037">#1037</a>)</li> <li>Use JSON + a typed wrapper instead of TS codegen <a href="https://github.com/woodruffw"><code>@woodruffw</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1025">#1025</a>)</li> </ul> <h2>🚀 Enhancements</h2> <ul> <li>Expose a Python "identity" output <a href="https://github.com/woodruffw"><code>@woodruffw</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1036">#1036</a>)</li> <li>Verify downloads with astral-sh/versions checksums <a href="https://github.com/zaniebot"><code>@zaniebot</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1033">#1033</a>)</li> </ul> <h2>🧰 Maintenance</h2> <ul> <li>chore: update known checksums for 0.12.12 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1041">#1041</a>)</li> <li>chore: update known checksums for 0.12.10/0.12.11 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1038">#1038</a>)</li> <li>chore: update known checksums for 0.12.9 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1035">#1035</a>)</li> <li>chore: update known checksums for 0.12.7/0.12.8 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1031">#1031</a>)</li> <li>chore: update known checksums for 0.12.6 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1030">#1030</a>)</li> <li>chore: update known checksums for 0.12.5 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1020">#1020</a>)</li> <li>Use self-repo syntax for all in-repo actions/reusable workflows <a href="https://github.com/woodruffw"><code>@woodruffw</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1024">#1024</a>)</li> <li>Pin one-shot tools <a href="https://github.com/woodruffw"><code>@woodruffw</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1022">#1022</a>)</li> <li>ci: remove obsolete direct push attempts <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1019">#1019</a>)</li> </ul> <h2>📚 Documentation</h2> <ul> <li>docs: update version references to v10.0.1 @<a href="https://github.com/apps/github-actions">github-actions[bot]</a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1018">#1018</a>)</li> </ul> <h2>⬆️ Dependency updates</h2> <ul> <li>chore(deps-dev): roll up Dependabot updates <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1043">#1043</a>)</li> <li>Harden npm install defaults <a href="https://github.com/zaniebot"><code>@zaniebot</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1026">#1026</a>)</li> <li>Add dependency cooldowns <a href="https://github.com/woodruffw"><code>@woodruffw</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1021">#1021</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/astral-sh/setup-uv/commit/bec219d24cd3e171d82865faccec33120bb574f4"><code>bec219d</code></a> chore(deps-dev): roll up Dependabot updates (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1043">#1043</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/b90ec40d15bfa44c33c6700196eb6efcdddb4373"><code>b90ec40</code></a> fix: respect no proxy directive (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1037">#1037</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/421feb646df5262e7dd93bc54161edfa30372417"><code>421feb6</code></a> chore: update known checksums for 0.12.12 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1041">#1041</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/f634bf473ad85bf3e23a613f52c5fa9f363874fc"><code>f634bf4</code></a> Expose a Python "identity" output (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1036">#1036</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/a6772c8f0a09dc9e3582c70a994b0c55af921803"><code>a6772c8</code></a> chore: update known checksums for 0.12.10/0.12.11 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1038">#1038</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/e105c8fb1d7b13074b851babdaef4185243c6a07"><code>e105c8f</code></a> chore: update known checksums for 0.12.9 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1035">#1035</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/cd13f9217092d43a771cf9ba7b09bdd3da8d7c4d"><code>cd13f92</code></a> Verify downloads with astral-sh/versions checksums (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1033">#1033</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/3aef7b92c52cec135792ea1e95f4c77683d39e61"><code>3aef7b9</code></a> chore: update known checksums for 0.12.7/0.12.8 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1031">#1031</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/d08d816a1ea176d61a318eff45abd3dffef415b1"><code>d08d816</code></a> chore: update known checksums for 0.12.6 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1030">#1030</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/19b4d1e990bec64818914c40230bde93a0de300b"><code>19b4d1e</code></a> Harden npm install defaults (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/1026">#1026</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/setup-uv/compare/v10.0.1...v10.1.0">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )