Skip to content

docs: user guide for the substrait.dataframe API (Zensical)#214

Open
nielspardon wants to merge 8 commits into
substrait-io:mainfrom
nielspardon:docs/dataframe-guide
Open

docs: user guide for the substrait.dataframe API (Zensical)#214
nielspardon wants to merge 8 commits into
substrait-io:mainfrom
nielspardon:docs/dataframe-guide

Conversation

@nielspardon

@nielspardon nielspardon commented Jul 14, 2026

Copy link
Copy Markdown
Member

What

Adds user-facing documentation for the ergonomic native DataFrame/Expr API
(substrait.dataframe) introduced in #204, plus a README introduction pointing
to it. There was previously no guide for the new API — the README still led with
the verbose raw-proto construction.

Built with Zensical (Material-style, zensical.toml) and
mkdocstrings for the generated API reference.
The guide's code is not inline: every snippet is pulled from a runnable script
that CI executes, so a documented example that goes stale against the API fails
CI instead of silently rendering.

Contents

  • docs/ — a multi-page guide: overview, getting started, data sources,
    types & schemas, expressions, the f function namespace, transformations,
    joins, aggregations, set operations, window functions, subqueries, parameters
    & context, DDL & writes, custom extensions, consuming plans, and a
    mkdocstrings-generated API reference.
  • examples/guide/ + tests/docs/ — the guide's snippets live in runnable
    scripts, not in the Markdown. Each ```python fence is a pymdownx.snippets
    include (--8<-- "examples/guide/<page>.py:<section>") pulling from a script
    under examples/guide/, and tests/docs/test_guide_snippets.py executes every
    one of those scripts end to end. Snippets that build a DataFrame call
    .to_plan(), so the test validates construction and resolution — this
    surfaced and fixed several previously-broken examples (joins on non-existent
    columns, user_defined(...), a nullable-struct schema). A my_functions.yaml
    fixture backs the custom-extension snippets. The two engine-handoff snippets
    (the DuckDB/ADBC tabs) stay inline since they need network + an external engine;
    they are exercised by examples/{duckdb,adbc}_example.py.
  • zensical.toml — theme, nav, site_url, the mkdocstrings Python handler
    (pointed at src/), the mike version provider for multi-version docs, and
    pymdownx.snippets (with check_paths = true, so a bad include path or
    section name fails the build).
  • pyproject.toml — a docs dependency group (zensical,
    mkdocstrings-python) plus a pixi docs environment with docs-build /
    docs-serve tasks; a per-file ruff ignore for examples/guide/ (its imports
    intentionally follow the docs rather than module conventions).
  • CI.github/workflows/docs.yml runs a build-check on every PR (which now
    also validates every --8<-- include). The existing test.yml picks up the
    new tests/docs/ suite automatically. example.yml gains dataframe_example.py
    (it was previously omitted from the matrix). docs-deploy.yml publishes
    versioned docs to the gh-pages branch via mike, on release tags (v*.*.*)
    and via workflow_dispatch.
  • README.md — new "Building plans with the DataFrame API" section; the
    raw-proto examples are reframed as the low-level API.
  • CONTRIBUTING.md — a Documentation section (local preview/build, docstring
    convention) plus a Guide code snippets section describing the
    pymdownx.snippets workflow; also corrects the dev-setup command (uv sync,
    not the non-existent --extra test).
  • Docstring pass — converts the substrait.dataframe docstrings from
    reStructuredText (:: literal blocks, :class:/:meth: roles) to Markdown so
    the generated reference renders cleanly. Docstrings only; no behavior change.

Building locally

pixi run docs-serve   # or: uv run --group docs zensical serve

Enabling GitHub Pages (one-time, after merge)

Versioned publishing needs the gh-pages branch to exist before Pages can point
at it:

  1. Actions → Deploy documentationRun workflow (creates gh-pages).
  2. Settings → Pages → Source → Deploy from a branch → gh-pages / (root).
  3. From then on, each v*.*.* release tag publishes that minor series and
    updates the latest alias. Adjust site_url in zensical.toml if the site
    is served from a custom domain.

Verification

  • zensical build completes with no issues (includes link validation); with
    check_paths, a wrong --8<-- path or section name fails the build.
  • The full test suite passes, including tests/docs/, which runs every guide
    snippet against the real API. A deliberately broken snippet fails that suite.

🤖 Generated with AI

Adds a multi-page guide for the ergonomic DataFrame/Expr API (from substrait-io#204),
built with Zensical + mkdocstrings, plus a README introduction pointing to it.

- docs/: 16-page guide (data sources, types, expressions, the `f` namespace,
  transformations, joins, aggregations, set ops, window functions, subqueries,
  parameters/context, DDL & writes, custom extensions, consuming plans) and a
  mkdocstrings-generated API reference. Every code example is verified against
  the real API.
- zensical.toml: Material theme, nav, and the mkdocstrings Python handler
  pointed at src/.
- pyproject.toml: `docs` dependency group (zensical, mkdocstrings-python) and a
  pixi `docs` environment with `docs-build`/`docs-serve` tasks.
- .github/workflows/docs.yml: build-check on PRs (no deploy; a ready-to-enable
  GitHub Pages deploy job is included as a comment).
- README.md: new "Building plans with the DataFrame API" section; the raw-proto
  examples are reframed as the low-level API.

🤖 Generated with AI
The substrait.dataframe docstrings used reStructuredText constructs that render
as literal text under mkdocstrings' Markdown renderer. Convert them so the
generated API reference reads well:

- RST literal blocks (`::` + indented code) -> fenced ```python blocks.
- Inline roles (:class:/:meth:/:func:/:mod:`...`) -> backticked names.
- Fix two illustrative examples that would not resolve
  (`f.substring(..., 1, 3)` -> `f.upper(...)`; substring needs i32 offsets).

Docstrings only; no behavior change. The 295 dataframe tests still pass and the
93 verified guide snippets are unaffected.

🤖 Generated with AI
Adds multi-version documentation publishing using the Zensical-compatible fork
of mike, which commits each version to the gh-pages branch as a subdirectory of
site_url.

- zensical.toml: set site_url (required for versioning) and add
  [project.extra.version] (provider = "mike") to render the version selector.
- .github/workflows/docs-deploy.yml: deploy job that runs on release tags
  (v*.*.*) and via workflow_dispatch. The manual trigger bootstraps the
  gh-pages branch so Pages can be pointed at it; tag pushes then publish each
  release as its minor series with a `latest` alias + root redirect. mike is
  installed ad-hoc (git-only), so uv.lock is untouched.
- docs.yml: the PR build-check now points at docs-deploy.yml for publishing
  instead of the old single-version artifact-deploy note.

Enabling it needs a one-time manual run (creates gh-pages) plus flipping
Settings -> Pages -> Source to the gh-pages branch.

🤖 Generated with AI
Add a Documentation section covering local preview/build (pixi run docs-serve / docs-build), the Markdown-over-RST docstring convention, and where docs are published.

🤖 Generated with AI
The setup step referenced 'uv sync --extra test', but there is no 'test' extra (it errors). Test dependencies live in the default 'dev' dependency group, so plain 'uv sync' is the correct setup command.

🤖 Generated with AI
Keep the workflow header to a concise description of what it does; the Pages/gh-pages bootstrap runbook doesn't belong in the workflow file.

🤖 Generated with AI
Previously every ```python fence in the guide was inline text. `zensical
build` only renders those fences, so a snippet that went stale against the
API would render fine while being broken — the guide's code was never
executed.

Move each snippet into a runnable script under examples/guide/ and include
it into the page with pymdownx.snippets (`--8<-- "examples/guide/<page>.py:
<section>"`), mirroring the substrait-java approach. Then:

- Enable pymdownx.snippets in zensical.toml with check_paths=true, so a bad
  include path or section name fails the docs build.
- Add tests/docs/test_guide_snippets.py, which executes every
  examples/guide/*.py end to end. Snippets that build a DataFrame call
  .to_plan(), so the test validates plan construction *and* resolution;
  this surfaced and fixed several previously-broken examples (e.g. joins
  referencing non-existent columns).
- Document the convention in CONTRIBUTING.md.
- The two engine-handoff snippets (DuckDB/ADBC tabs) stay inline: they need
  network + an external engine and are exercised by examples/{duckdb,adbc}_
  example.py (run by example.yml). Also add dataframe_example.py to that
  workflow's matrix (it was omitted).
Resolve the pixi.lock conflict from rebasing the docs branch onto the
updated main (pixi bumped to 0.73.0, substrait-io#229) by regenerating the lock with
`pixi lock` so it carries both main's dependencies and the docs
environment. uv.lock needed no change.
@nielspardon
nielspardon force-pushed the docs/dataframe-guide branch from 8031207 to 895a1a6 Compare July 21, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant