Skip to content

Commit f5b6946

Browse files
authored
feat(docs): add API reference generation and PR CI (#1)
* build(docs): add pydoc-markdown dev dep and generation config Pin generation config from a discovery spike: single-file markdown renderer to docs/api/reference.md, filter expression dropping imported-name Indirections, github source_linker (normalized downstream). Static docspec parsing avoids TYPE_CHECKING import errors. Task: 1789724353 * feat(docs): deterministic pydoc-markdown generator and committed api reference scripts/generate-docs.py wraps pydoc-markdown (console script), wipes+regenerates docs/api/reference.md, and normalizes source links to blob/main/<path> (no SHA, no line anchor). Two runs on the same commit are byte-identical. Task: 1789724353 * feat(docs): docs-check drift gate with source-link and coverage sanity Task: 1789724353 * docs: document make docs / docs-check entry points Task: 1789724353 * style: apply ruff format to pre-existing drifted test files Surfaced by the new make lint / CI gate; mechanical line-wrapping and trailing commas only, no logic change. Task: 1789724353 * ci: add PR workflow and extend lint/typecheck to scripts Task: 1789724353 * fix(docs): normalize generated file ending * fix(docs): sort generated module sections * test(conformance): add offline Anthropic shared public-contract baseline Task: 1789897105 * test(conformance): remove unused package import
1 parent e21adfd commit f5b6946

16 files changed

Lines changed: 32251 additions & 11 deletions

‎.github/workflows/ci.yml‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
check:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
python-version: ['3.10', '3.12', '3.13']
18+
steps:
19+
- uses: actions/checkout@v4
20+
- uses: astral-sh/setup-uv@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
- run: uv sync --extra dev --locked
24+
- run: uv run make lint
25+
- run: uv run make typecheck
26+
- run: uv run make test
27+
28+
docs-check:
29+
runs-on: ubuntu-latest
30+
steps:
31+
- uses: actions/checkout@v4
32+
- uses: astral-sh/setup-uv@v5
33+
with:
34+
python-version: '3.12'
35+
- run: uv sync --extra dev --locked
36+
- run: uv run make docs-check

‎Makefile‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@ PYTHON ?= python3
22
LIVE_ENV_FILE ?= .env.live
33
.DEFAULT_GOAL := test
44

5-
.PHONY: test lint typecheck build test-live test-live-managed test-live-all
5+
.PHONY: test lint typecheck build docs docs-check test-live test-live-managed test-live-all
66

77
test:
88
$(PYTHON) -m pytest -q
99

10+
docs:
11+
uv run --python 3.12 --extra dev --locked python scripts/generate-docs.py
12+
13+
docs-check:
14+
uv run --python 3.12 --extra dev --locked python scripts/docs-check.py
15+
1016
lint:
11-
$(PYTHON) -m ruff check src tests examples
12-
$(PYTHON) -m ruff format --check src tests examples
17+
$(PYTHON) -m ruff check src tests examples scripts
18+
$(PYTHON) -m ruff format --check src tests examples scripts
1319

1420
typecheck:
15-
$(PYTHON) -m mypy src/qca
21+
$(PYTHON) -m mypy src/qca scripts
1622

1723
build:
1824
$(PYTHON) -m build

‎README.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ python -m pip install -e '.[dev]' # development environment
2323

2424
Python 3.10 or newer. The runtime dependencies are `httpx`, `pydantic` v2, `anyio`, and `typing-extensions`; the package is typed and ships `py.typed`.
2525

26+
## Generating documentation
27+
28+
The API reference under `docs/api/` is generated from the public source and
29+
committed. Regenerate and verify it with:
30+
31+
```bash
32+
make docs # regenerate docs/api/ from src/qca
33+
make docs-check # regenerate + drift/link/snippet checks (offline)
34+
```
35+
36+
Under the hood these run `pydoc-markdown` via `uv` on a pinned Python 3.12:
37+
38+
```bash
39+
uv run --python 3.12 --extra dev --locked pydoc-markdown pydoc-markdown.yml
40+
```
41+
2642
## Usage
2743

2844
```python

‎docs/README.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Documentation
2+
3+
The API reference under `docs/api/` is **generated** from the public source of
4+
`src/qca` and committed to the repository. Do not hand-edit files under
5+
`docs/api/` — they are overwritten on every regeneration.
6+
7+
Regenerate and verify:
8+
9+
```bash
10+
make docs # regenerate docs/api/ from src/qca
11+
make docs-check # regenerate + drift/link/snippet checks (offline)
12+
```
13+
14+
## How it works (pinned from the Task 1 spike)
15+
16+
- Tool: [`pydoc-markdown`](https://pypi.org/project/pydoc-markdown/) `>=4,<5`
17+
(locked in `uv.lock`), run through `uv` on a pinned Python 3.12 for a
18+
reproducible interpreter.
19+
- Loader `python` uses **static docspec parsing** — it never imports `src/qca`,
20+
so `if TYPE_CHECKING:` forward-ref field types cannot cause import errors.
21+
- The `markdown` renderer emits a **single** `docs/api/reference.md` (its
22+
`filename:` option), not a multi-page tree.
23+
- `filter.documented_only: false` is required: the Stainless-style client
24+
classes (`Forward`/`AsyncForward`/`Managed`/`AsyncManaged`) and the field-only
25+
pydantic models carry no class docstring, so `documented_only: true` would drop
26+
the entire real surface. The filter `expression` drops imported-name
27+
`Indirection`s (`datetime`, `Optional`, `TYPE_CHECKING`, ...) that would
28+
otherwise render as spurious `## <name>` headers on every module page.
29+
- The GitHub `source_linker` natively emits `blob/<HEAD-sha>/<path>#L<line>`.
30+
`scripts/generate-docs.py` normalizes every link to `blob/main/<path>` (no
31+
commit SHA, no line anchor) so regenerating after a commit produces no churn.
32+
- Determinism: on a fixed commit + pinned version, two raw runs are
33+
byte-identical; the only per-commit variance is the source-link SHA / line
34+
anchor, which normalization collapses. This is what the `docs-check` drift gate
35+
(`git diff --exit-code -- docs/api`) relies on.

0 commit comments

Comments
 (0)