diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 593a343e1..0063b2183 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -552,9 +552,10 @@ jobs: run: | set -x cd docs - curl -O https://gist.githubusercontent.com/ritchie46/cac6b337ea52281aa23c049250a4ff03/raw/89a957ff3919d90e6ef2d34235e6bf22304f3366/pokemon.csv - curl -O https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2021-01.parquet - uv run --no-project make html + # build.sh downloads the example data, registers the Jupyter kernel + # myst-nb needs, symlinks the data next to each executed page, and + # runs sphinx. Using it here keeps CI identical to a local build. + uv run --no-project bash ./build.sh - name: Copy & push the generated HTML if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') diff --git a/AGENTS.md b/AGENTS.md index 632d6ebc0..fda08b23c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -84,9 +84,9 @@ Every Python function must include a docstring with usage examples. When adding or updating an aggregate or window function, ensure the corresponding site documentation is kept in sync: -- **Aggregations**: `docs/source/user-guide/common-operations/aggregations.rst` — +- **Aggregations**: `docs/source/user-guide/common-operations/aggregations.md` — add new aggregate functions to the "Aggregate Functions" list and include usage examples if appropriate. -- **Window functions**: `docs/source/user-guide/common-operations/windows.rst` — +- **Window functions**: `docs/source/user-guide/common-operations/windows.md` — add new window functions to the "Available Functions" list and include usage examples if appropriate. diff --git a/docs/build.sh b/docs/build.sh index f73330323..e8409ee72 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -36,6 +36,21 @@ rm -rf build 2> /dev/null rm -rf temp 2> /dev/null mkdir temp cp -rf source/* temp/ + +# myst-nb executes each page as a notebook from the directory that page +# lives in, so the example data files must sit alongside every page that +# loads them by relative name (e.g. `ctx.read_csv("pokemon.csv")`). Symlink +# them into each directory that has such a page rather than copying the +# 20 MB parquet repeatedly. +for d in temp temp/user-guide temp/user-guide/common-operations; do + ln -sf "$script_dir/pokemon.csv" "$d/pokemon.csv" + ln -sf "$script_dir/yellow_tripdata_2021-01.parquet" "$d/yellow_tripdata_2021-01.parquet" +done + +# myst-nb runs `{code-cell}` blocks against a Jupyter kernel named "python3". +# Register the active environment's interpreter as that kernel (idempotent). +python -m ipykernel install --sys-prefix --name python3 --display-name "Python 3" + make SOURCEDIR=`pwd`/temp html cd "$original_dir" || exit diff --git a/docs/source/_static/images/original_dark.svg b/docs/source/_static/images/original_dark.svg new file mode 100644 index 000000000..fbdf20ea7 --- /dev/null +++ b/docs/source/_static/images/original_dark.svg @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/docs/source/_static/theme_overrides.css b/docs/source/_static/theme_overrides.css index 661454b12..9eab5b03c 100644 --- a/docs/source/_static/theme_overrides.css +++ b/docs/source/_static/theme_overrides.css @@ -63,3 +63,48 @@ html[data-theme="dark"] .table tbody tr:nth-of-type(odd) { white-space: normal !important; } } + + +/* Hideable right-hand "On this page" sidebar. + * toc-toggle.js adds the button and toggles `pst-secondary-hidden` on
; + * hiding the sidebar lets the flex article container reclaim the width. */ + +body.pst-secondary-hidden .bd-sidebar-secondary { + display: none; +} + +/* Let the article use the freed space rather than just re-centering. */ +body.pst-secondary-hidden .bd-article-container { + max-width: none; +} + +/* Floating toggle button, pinned to the top-right under the navbar. */ +#pst-secondary-toggle { + position: fixed; + top: 4.5rem; + right: 0.75rem; + z-index: 1020; + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + padding: 0; + border: 1px solid var(--pst-color-border, #ccc); + border-radius: 0.25rem; + background-color: var(--pst-color-surface, #fff); + color: var(--pst-color-text-base, #333); + cursor: pointer; +} + +#pst-secondary-toggle:hover { + color: rgb(var(--pst-color-link-hover)); +} + +/* The toggle is only meaningful where the sidebar is shown (wide screens); + * below the theme's lg breakpoint the sidebar is already collapsed away. */ +@media (max-width: 959.98px) { + #pst-secondary-toggle { + display: none; + } +} diff --git a/docs/source/_static/toc-toggle.js b/docs/source/_static/toc-toggle.js new file mode 100644 index 000000000..a5cc65e54 --- /dev/null +++ b/docs/source/_static/toc-toggle.js @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Adds a button that hides the right-hand "On this page" sidebar so the + * article can use the full page width. The choice is remembered across + * pages via localStorage. */ +(function () { + "use strict"; + var KEY = "pst-secondary-hidden"; + + function apply(hidden, btn) { + document.body.classList.toggle("pst-secondary-hidden", hidden); + if (btn) { + btn.setAttribute("aria-pressed", String(hidden)); + btn.title = hidden ? "Show page contents" : "Hide page contents"; + } + } + + document.addEventListener("DOMContentLoaded", function () { + // Only offer the toggle on pages that actually have the sidebar. + if (!document.querySelector(".bd-sidebar-secondary")) { + return; + } + + var btn = document.createElement("button"); + btn.id = "pst-secondary-toggle"; + btn.type = "button"; + btn.setAttribute("aria-label", "Toggle page contents sidebar"); + btn.innerHTML = ''; + document.body.appendChild(btn); + + btn.addEventListener("click", function () { + var hidden = !document.body.classList.contains("pst-secondary-hidden"); + try { + localStorage.setItem(KEY, hidden ? "1" : "0"); + } catch (e) { + /* localStorage may be unavailable; toggle still works for this page. */ + } + apply(hidden, btn); + }); + + var stored = null; + try { + stored = localStorage.getItem(KEY); + } catch (e) { + /* ignore */ + } + apply(stored === "1", btn); + }); +})(); diff --git a/docs/source/conf.py b/docs/source/conf.py index bb1473546..22bace809 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,16 +48,40 @@ extensions = [ "sphinx.ext.mathjax", "sphinx.ext.napoleon", - "myst_parser", - "IPython.sphinxext.ipython_directive", + # myst_nb is a superset of myst_parser: it provides the MyST markdown + # parser plus executable `{code-cell}` notebook directives. Do NOT also + # list "myst_parser" — myst_nb activates it internally and listing both + # raises an extension conflict. + "myst_nb", "autoapi.extension", ] +# NOTE: .rst stays alongside .md because sphinx-autoapi generates RST +# under autoapi/ and Sphinx needs the suffix to parse it. The human- +# authored docs are all MyST .md now. ".md" is routed through myst-nb so +# pages carrying jupytext/kernelspec front matter execute their +# `{code-cell}` blocks; pages without that front matter render as plain +# MyST markdown. The ".rst" entry is only for the autoapi build artifacts. source_suffix = { ".rst": "restructuredtext", - ".md": "markdown", + ".md": "myst-nb", } +# Execute notebook code cells at build time and fail the build if any cell +# raises — this replaces the old IPython sphinx directive, whose executed +# examples are now `{code-cell}` blocks. "force" re-executes every build so +# stale cached output can never ship. +nb_execution_mode = "force" +nb_execution_timeout = 120 +nb_execution_raise_on_error = True + +# Prefer the plain-text repr of a cell's last expression over its rich +# `_repr_html_`. A DataFrame's HTML repr is a self-contained widget (inline +# styles + an injected