From 1f474e3f47e9dae5d2b85964513492900f536ae8 Mon Sep 17 00:00:00 2001 From: arpan Date: Fri, 11 Sep 2026 04:14:47 +0530 Subject: [PATCH] State the Pythons CI tests, and read them rather than typing them The readiness block said "Python 3.11 and later" as a literal. It now says "Python 3.11 and later, tested on 3.11 to 3.14", with the floor read from the library's requires-python and the tested list from its CI check matrix, so adding or dropping a version there drifts this block until it is regenerated. A floor CI does not test is refused. The install page says which Pythons CI runs the suite on. Only the new python field was added to generated/readiness.json; the recorded test count is unchanged. --- docs.mdx | 2 +- docs/get-started/install.mdx | 3 +- docs/production/index.mdx | 2 +- generated/readiness.full.mdx | 2 +- generated/readiness.json | 9 ++++++ generated/readiness.mdx | 2 +- generated/readiness.readme.md | 2 +- tools/docs_audit/render_readiness.py | 42 ++++++++++++++++++++++++++-- 8 files changed, 55 insertions(+), 9 deletions(-) diff --git a/docs.mdx b/docs.mdx index a63c25a..ba981a9 100644 --- a/docs.mdx +++ b/docs.mdx @@ -218,7 +218,7 @@ the framework's own interrupt, and a framework with no such primitive does not n ## Where it stands {/* generated from the suite, pyproject and the soak (mdx) — run the generator */} -- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later. +- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later, tested on 3.11 to 3.14. - **4,408 tests**, every version specified before it was written and every requirement mutation-tested. - **11 guarantees you can check in your own setup**, with `ctrlrun verify` against your policy, on your store's backend, in a scratch store it creates. - **One host: a file.** SQLite, no server, no ops. **Many hosts: Postgres**, the same guarantees, graded by the same suite. diff --git a/docs/get-started/install.mdx b/docs/get-started/install.mdx index 176076e..a6916fb 100644 --- a/docs/get-started/install.mdx +++ b/docs/get-started/install.mdx @@ -5,7 +5,8 @@ description: "pip install ctrlrun puts one package, pyyaml and click on your mac `pip install ctrlrun` installs the kernel and exactly two dependencies, `pyyaml` and `click`. It needs Python 3.11 or later and nothing running: no server, no database, no dashboard. State is a -SQLite file the library creates beside your policy. +SQLite file the library creates beside your policy. CI runs the full suite on Python 3.11, 3.12, +3.13 and 3.14. ```bash pip install ctrlrun diff --git a/docs/production/index.mdx b/docs/production/index.mdx index 5729bb6..86f1fb2 100644 --- a/docs/production/index.mdx +++ b/docs/production/index.mdx @@ -27,7 +27,7 @@ need. `test_the_first_line_of_the_section_says_which_store_and_why` asserts the ## Where it stands {/* generated from the suite, pyproject and the soak (full) — run the generator */} -- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later. +- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later, tested on 3.11 to 3.14. - **4,408 tests**, every version specified before it was written and every requirement mutation-tested. [Read more](/docs/how-this-is-built). - **11 guarantees you can check in your own setup**, with `ctrlrun verify` against your policy, on your store's backend, in a scratch store it creates. [Read more](/docs/security/verify-guarantees). - **One host: a file.** SQLite, no server, no ops. **Many hosts: Postgres**, the same guarantees, graded by the same suite. [Read more](/docs/production/postgres). diff --git a/generated/readiness.full.mdx b/generated/readiness.full.mdx index ce38e6e..91888d1 100644 --- a/generated/readiness.full.mdx +++ b/generated/readiness.full.mdx @@ -1,5 +1,5 @@ {/* generated from the suite, pyproject and the soak (full) — run the generator */} -- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later. +- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later, tested on 3.11 to 3.14. - **4,408 tests**, every version specified before it was written and every requirement mutation-tested. [Read more](/docs/how-this-is-built). - **11 guarantees you can check in your own setup**, with `ctrlrun verify` against your policy, on your store's backend, in a scratch store it creates. [Read more](/docs/security/verify-guarantees). - **One host: a file.** SQLite, no server, no ops. **Many hosts: Postgres**, the same guarantees, graded by the same suite. [Read more](/docs/production/postgres). diff --git a/generated/readiness.json b/generated/readiness.json index 50c7738..58e08de 100644 --- a/generated/readiness.json +++ b/generated/readiness.json @@ -1,5 +1,14 @@ { "guarantees": 11, + "python": { + "floor": "3.11", + "tested": [ + "3.11", + "3.12", + "3.13", + "3.14" + ] + }, "released": "0.6.1", "soak": { "actions": 889735, diff --git a/generated/readiness.mdx b/generated/readiness.mdx index 109613d..c613933 100644 --- a/generated/readiness.mdx +++ b/generated/readiness.mdx @@ -1,5 +1,5 @@ {/* generated from the suite, pyproject and the soak (mdx) — run the generator */} -- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later. +- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later, tested on 3.11 to 3.14. - **4,408 tests**, every version specified before it was written and every requirement mutation-tested. - **11 guarantees you can check in your own setup**, with `ctrlrun verify` against your policy, on your store's backend, in a scratch store it creates. - **One host: a file.** SQLite, no server, no ops. **Many hosts: Postgres**, the same guarantees, graded by the same suite. diff --git a/generated/readiness.readme.md b/generated/readiness.readme.md index cdb8fde..e02ccc9 100644 --- a/generated/readiness.readme.md +++ b/generated/readiness.readme.md @@ -1,5 +1,5 @@ -- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later. +- **Version 0.6.1**, on [PyPI](https://pypi.org/project/ctrlrun/), Python 3.11 and later, tested on 3.11 to 3.14. - **4,408 tests**, every version specified before it was written and every requirement mutation-tested. - **11 guarantees you can check in your own setup**, with `ctrlrun verify` against your policy, on your store's backend, in a scratch store it creates. - **One host: a file.** SQLite, no server, no ops. **Many hosts: Postgres**, the same guarantees, graded by the same suite. diff --git a/tools/docs_audit/render_readiness.py b/tools/docs_audit/render_readiness.py index 3762dd9..87eae89 100644 --- a/tools/docs_audit/render_readiness.py +++ b/tools/docs_audit/render_readiness.py @@ -4,6 +4,7 @@ every number in it comes from something that measures itself: - the version and the dependency floor from `pyproject.toml`; +- the Python floor from `requires-python`, and the Pythons tested from CI's `check` matrix; - the test count from `pytest --collect-only`, recorded in `generated/readiness.json`; - the soak's duration, actions and unattributed count from `research/soak/results/*.json`; - what `ctrlrun verify` checks from the guarantee catalogue itself. @@ -32,6 +33,8 @@ import tomllib from pathlib import Path +import yaml + from _core import CORE_ROOT from _files import REPO_ROOT, relative @@ -58,6 +61,37 @@ def _version() -> str: return str(tomllib.load(handle)["project"]["version"]) +def _python() -> dict: + """The Python floor the package declares, and the Pythons CI's `check` job runs the suite on. + + The block said *"Python 3.11 and later"* as a literal, which stays true when CI adds a + version and so never drifted into a failure. The tested list is read from the matrix rather + than the classifiers because the matrix is what runs the suite; a classifier is a label. + """ + with (CORE_ROOT / "pyproject.toml").open("rb") as handle: + requires = str(tomllib.load(handle)["project"]["requires-python"]) + if not re.fullmatch(r">=3\.\d+", requires): + raise SystemExit(f"requires-python is {requires!r}; the block can only state a >=3.N floor") + workflow = CORE_ROOT / ".github" / "workflows" / "ci.yml" + matrix = yaml.safe_load(workflow.read_text(encoding="utf-8"))["jobs"]["check"]["strategy"] + tested = matrix["matrix"]["python-version"] + # Unquoted in YAML, `3.10` is the float 3.1. Refused rather than repaired. + if not all(isinstance(version, str) for version in tested): + raise SystemExit(f"CI's python-version matrix must be quoted strings, got {tested!r}") + floor = requires.removeprefix(">=") + if floor not in tested: + raise SystemExit(f"requires-python is {requires} and CI's check job does not test {floor}") + return {"floor": floor, "tested": sorted(tested, key=lambda v: tuple(map(int, v.split("."))))} + + +def _tested(versions: list[str]) -> str: + """`3.11 to 3.14` for a contiguous run of minors, `3.11, 3.12 and 3.14` otherwise.""" + minors = [int(version.split(".")[1]) for version in versions] + if len(versions) > 2 and minors == list(range(minors[0], minors[-1] + 1)): + return f"{versions[0]} to {versions[-1]}" + return versions[0] if len(versions) == 1 else f"{', '.join(versions[:-1])} and {versions[-1]}" + + #: A changelog heading: `## [0.6.0] - unreleased — Durable runtime`, or with a date. The file #: uses a hyphen and an em dash interchangeably as the separator, so both are accepted. _HEADING = re.compile(r"^## \[(?P[^\]]+)\]\s*[-—]\s*(?P[^\s—]+)", re.M) @@ -141,6 +175,7 @@ def measure() -> dict: run = soak() return { "version": _version(), + "python": _python(), "released": released(), "tests": collected(), "guarantees": _guarantees(), @@ -167,6 +202,7 @@ def _lines(data: dict, *, full: bool) -> list[str]: version, tests, guarantees = data["version"], data["tests"], data["guarantees"] run, published = data["soak"], data.get("released") link = "https://pypi.org/project/ctrlrun/" + python = f"Python {data['python']['floor']} and later, tested on {_tested(data['python']['tested'])}." def where(text: str, page: str) -> str: """`full` adds a Read-more link; the other formats carry the sentence alone. @@ -183,10 +219,10 @@ def where(text: str, page: str) -> str: lines = [ ( - f"- **Version {version}**, on [PyPI]({link}), Python 3.11 and later." + f"- **Version {version}**, on [PyPI]({link}), {python}" if published == version else f"- **Version {version} is in development**; [PyPI]({link}) has {published}. " - "Python 3.11 and later." + + python ), where( f"- **{tests:,} tests**, every version specified before it was written and every " @@ -278,7 +314,7 @@ def check(data: dict, pages: list[Path] | None = None) -> list[str]: f"the block claims {recorded['tests']:,} tests and the suite collects {current:,}; " "run --write" ) - for key in ("version", "released", "guarantees", "soak"): + for key in ("version", "python", "released", "guarantees", "soak"): if recorded.get(key) != data.get(key): drift.append(f"{key} changed since the block was generated; run --write") for fmt in FORMATS: