Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- run: uv sync --extra dev --locked
- run: uv run make lint
- run: uv run make typecheck
- run: uv run make test

docs-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: '3.12'
- run: uv sync --extra dev --locked
- run: uv run make docs-check
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ PYTHON ?= python3
LIVE_ENV_FILE ?= .env.live
.DEFAULT_GOAL := test

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

test:
$(PYTHON) -m pytest -q

docs:
uv run --python 3.12 --extra dev --locked python scripts/generate-docs.py

docs-check:
uv run --python 3.12 --extra dev --locked python scripts/docs-check.py

lint:
$(PYTHON) -m ruff check src tests examples
$(PYTHON) -m ruff format --check src tests examples
$(PYTHON) -m ruff check src tests examples scripts
$(PYTHON) -m ruff format --check src tests examples scripts

typecheck:
$(PYTHON) -m mypy src/qca
$(PYTHON) -m mypy src/qca scripts

build:
$(PYTHON) -m build
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ python -m pip install -e '.[dev]' # development environment

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

## Generating documentation

The API reference under `docs/api/` is generated from the public source and
committed. Regenerate and verify it with:

```bash
make docs # regenerate docs/api/ from src/qca
make docs-check # regenerate + drift/link/snippet checks (offline)
```

Under the hood these run `pydoc-markdown` via `uv` on a pinned Python 3.12:

```bash
uv run --python 3.12 --extra dev --locked pydoc-markdown pydoc-markdown.yml
```

## Usage

```python
Expand Down
35 changes: 35 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Documentation

The API reference under `docs/api/` is **generated** from the public source of
`src/qca` and committed to the repository. Do not hand-edit files under
`docs/api/` — they are overwritten on every regeneration.

Regenerate and verify:

```bash
make docs # regenerate docs/api/ from src/qca
make docs-check # regenerate + drift/link/snippet checks (offline)
```

## How it works (pinned from the Task 1 spike)

- Tool: [`pydoc-markdown`](https://pypi.org/project/pydoc-markdown/) `>=4,<5`
(locked in `uv.lock`), run through `uv` on a pinned Python 3.12 for a
reproducible interpreter.
- Loader `python` uses **static docspec parsing** — it never imports `src/qca`,
so `if TYPE_CHECKING:` forward-ref field types cannot cause import errors.
- The `markdown` renderer emits a **single** `docs/api/reference.md` (its
`filename:` option), not a multi-page tree.
- `filter.documented_only: false` is required: the Stainless-style client
classes (`Forward`/`AsyncForward`/`Managed`/`AsyncManaged`) and the field-only
pydantic models carry no class docstring, so `documented_only: true` would drop
the entire real surface. The filter `expression` drops imported-name
`Indirection`s (`datetime`, `Optional`, `TYPE_CHECKING`, ...) that would
otherwise render as spurious `## <name>` headers on every module page.
- The GitHub `source_linker` natively emits `blob/<HEAD-sha>/<path>#L<line>`.
`scripts/generate-docs.py` normalizes every link to `blob/main/<path>` (no
commit SHA, no line anchor) so regenerating after a commit produces no churn.
- Determinism: on a fixed commit + pinned version, two raw runs are
byte-identical; the only per-commit variance is the source-link SHA / line
anchor, which normalization collapses. This is what the `docs-check` drift gate
(`git diff --exit-code -- docs/api`) relies on.
Loading
Loading