Skip to content

Commit 880dd34

Browse files
committed
whenxuan: update read the docs
1 parent 9b70fe5 commit 880dd34

41 files changed

Lines changed: 1905 additions & 623 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/docs-build.md‎

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
# Building the PySDKit documentation
2+
3+
This note is the rebuild checklist for the Sphinx site
4+
([pysdkit.readthedocs.io](https://pysdkit.readthedocs.io/)).
5+
Use it when you clone the repo on another machine or host.
6+
7+
Docs language is **English**. Example sources are **Python files**, not notebooks.
8+
9+
---
10+
11+
## Stack
12+
13+
| Piece | Choice |
14+
|---|---|
15+
| Builder | Sphinx ≥ 8 (`docs/source/conf.py`) |
16+
| Theme | [pydata-sphinx-theme](https://pydata-sphinx-theme.readthedocs.io/) ≥ 0.16 |
17+
| Hosting | Read the Docs (`.readthedocs.yaml`) |
18+
| API | `autodoc` + `autosummary` + Napoleon (NumPy/Google docstrings) |
19+
| Examples | [Sphinx-Gallery](https://sphinx-gallery.github.io/) (scikit-image style) |
20+
| Extra extensions | `sphinx-copybutton`, `sphinx_design`, `sphinx_gitstamp`, `sphinx-github-changelog` |
21+
22+
Python **3.10** is what RTD uses. Locally 3.10+ is fine. The package itself claims `>=3.8`.
23+
24+
---
25+
26+
## Install and build (local)
27+
28+
From the **repository root**:
29+
30+
```bash
31+
python -m pip install -U pip
32+
pip install -e .
33+
pip install -r docs/requirements.txt
34+
35+
# Windows PowerShell: avoid GUI backends hanging on plt.show()
36+
# $env:MPLBACKEND = "Agg"
37+
38+
sphinx-build -b html docs/source docs/build/html
39+
```
40+
41+
Open `docs/build/html/index.html`.
42+
43+
`docs/build/` is covered by the repo `build/` gitignore. Do not commit HTML output.
44+
45+
`conf.py` already calls `matplotlib.use("agg")` and sets `savefig.dpi = 100`.
46+
47+
---
48+
49+
## Files you must have
50+
51+
### Always needed to *configure* a build
52+
53+
```
54+
.readthedocs.yaml # RTD: OS, Python 3.10, pip install . + docs reqs
55+
docs/requirements.txt # Sphinx and theme extras (not runtime deps)
56+
docs/source/conf.py # Sphinx + gallery + theme options
57+
docs/source/index.rst # Hidden toctree = top navbar
58+
pyproject.toml # pip install . / -e .
59+
pysdkit/ # autodoc imports this package
60+
```
61+
62+
### Hand-written pages
63+
64+
```
65+
docs/source/user_guide/index.rst
66+
docs/source/API/modules.rst # API toctree
67+
docs/source/API/pysdkit*.rst # one thin page per public family
68+
docs/source/release_notes/index.rst # changelog:: pulls GitHub Releases
69+
docs/source/development/index.rst
70+
docs/source/about/index.rst
71+
```
72+
73+
Navbar order comes from the hidden toctree in `docs/source/index.rst`:
74+
75+
`user_guide` → `auto_examples` → `API/modules` → `release_notes` → `development` → `about`
76+
77+
### Theme / branding
78+
79+
```
80+
docs/source/_static/logo.png # html_logo
81+
docs/source/_static/logo-pypi.svg # PyPI icon in the header
82+
docs/source/_static/custom.css # pydata CSS variables (header blue, table zebra)
83+
docs/source/_static/theme_overrides.css # code-block gray, active tab, gallery cards
84+
docs/source/_static/version_switcher.json # unused (version switcher is off)
85+
```
86+
87+
`html_css_files` loads **overrides after** `custom.css`. Put high-specificity tweaks in `theme_overrides.css`.
88+
89+
Light-theme tokens that matter:
90+
91+
- Header / “on-background”: `--pst-color-on-background` (`#9cccf8`)
92+
- Active nav item: `--pst-color-secondary` (`#8045e5`) — primary `#91a9f6` is too close to the bar
93+
- Inline code / tables: `#f3f4f5`
94+
95+
API module pages hide the left sidebar (`html_sidebars` for `API/pysdkit.*`). Gallery pages keep it.
96+
97+
### Examples (Sphinx-Gallery)
98+
99+
**Input (commit these):**
100+
101+
```
102+
examples/GALLERY_HEADER.rst # landing-page title
103+
examples/<section>/GALLERY_HEADER.rst
104+
examples/<section>/*.py # one script per card; start with a title docstring
105+
docs/ipynb_to_gallery.py # one-shot: old .ipynb → gallery .py
106+
```
107+
108+
**Output (gitignored — not on the main git tree):**
109+
110+
```
111+
docs/source/auto_examples/ # rst, png, md5, downloadable .py/.ipynb/.zip
112+
docs/source/sg_execution_times.rst
113+
```
114+
115+
Those files are packed as `auto_examples.tar.gz` and stored on a rolling
116+
**GitHub Release** tagged `gallery-cache` (a prerelease, not a software
117+
version). Read the Docs downloads the tarball in `pre_build`, Sphinx-Gallery
118+
skips examples whose `.md5` still matches, and the built HTML (including
119+
PNGs) is what RTD actually serves.
120+
121+
A generic cloud drive does **not** help by itself: Sphinx needs the PNG +
122+
`.md5` files on the **build machine**, not a public image URL in the
123+
browser.
124+
125+
Gallery config (in `conf.py`):
126+
127+
```python
128+
sphinx_gallery_conf = {
129+
"examples_dirs": "../../examples", # relative to docs/source/
130+
"gallery_dirs": "auto_examples",
131+
"filename_pattern": r"\.py$", # execute all .py, not only plot_*
132+
"nested_sections": True, # each subfolder = sidebar chapter
133+
"download_all_examples": True,
134+
"reset_modules": ("matplotlib",),
135+
"abort_on_example_error": False,
136+
}
137+
```
138+
139+
Sphinx-Gallery only supports **one** level of subfolders under `examples/`.
140+
141+
---
142+
143+
## Read the Docs
144+
145+
`.readthedocs.yaml` does:
146+
147+
1. Ubuntu 24.04, Python 3.10
148+
2. `pre_build`: download `auto_examples.tar.gz` from the `gallery-cache` GitHub Release (if it exists)
149+
3. `pip install .` (the checkout, **not** PyPI `pysdkit`)
150+
4. `pip install -r docs/requirements.txt`
151+
5. HTML only (`formats: []` — skip PDF/ePub)
152+
6. Sphinx with `docs/source/conf.py` (only examples whose source `.md5` changed are re-run)
153+
154+
Community RTD is **15 minutes** per build. A cold gallery run of all 73
155+
examples can exceed that. The GitHub Release cache is what keeps ordinary
156+
docs builds inside the limit (same idea as committing `auto_examples/`,
157+
without putting 90 MB of PNGs on the default git clone).
158+
159+
Refresh the cache after example (or plot-changing) edits:
160+
161+
```bash
162+
# local, if docs/source/auto_examples/ already exists
163+
tar -czf auto_examples.tar.gz docs/source/auto_examples docs/source/sg_execution_times.rst
164+
gh release create gallery-cache auto_examples.tar.gz --prerelease \
165+
--title "Sphinx-Gallery cache" \
166+
--notes "Generated example figures for Read the Docs. Not a software release."
167+
# later refreshes:
168+
gh release upload gallery-cache auto_examples.tar.gz --clobber
169+
```
170+
171+
Or on GitHub: **Actions → Gallery cache → Run workflow**
172+
(`.github/workflows/gallery-cache.yml` also runs on pushes that touch
173+
`examples/`). That workflow can take ~20+ minutes; RTD does not wait for it.
174+
If you change one example and push before the cache job finishes, RTD still
175+
downloads the *previous* tarball and re-executes only the changed script.
176+
177+
If the `gallery-cache` release is missing, RTD falls back to a full gallery
178+
run (and may time out). Email support@readthedocs.org only if you need a
179+
longer timeout for that cold path.
180+
181+
`emd/emd_forecasting.py` needs **scikit-learn** (listed in `docs/requirements.txt`). Runtime package deps (numpy, scipy, matplotlib, …) come from `pip install .`.
182+
183+
---
184+
185+
## Incremental vs full gallery rebuild
186+
187+
| Situation | What happens |
188+
|---|---|
189+
| Local rebuild, unchanged `examples/**/*.py`, existing `auto_examples/*.md5` | Skip execution; reuse figures |
190+
| RTD with a current `gallery-cache` tarball, unchanged examples | Skip execution; reuse figures |
191+
| Edited one example `.py` | Re-run that file only (md5 mismatch) |
192+
| Fresh clone with no local `auto_examples/` and no Release | Full gallery run |
193+
| Force one local file | Delete its `.md5` under `auto_examples/`, or change a newline in the `.py` |
194+
195+
Skip execution entirely (layout only, no figures):
196+
197+
```bash
198+
sphinx-build -D sphinx_gallery_conf.plot_gallery=False -b html docs/source docs/build/html
199+
```
200+
201+
---
202+
203+
## Adding or changing content
204+
205+
### New algorithm → API page
206+
207+
1. Implement and export from the subpackage `__init__.py`.
208+
2. Re-export from `pysdkit` and add to `__all__`.
209+
3. Add a thin `autoclass` / `autofunction` line on the matching `docs/source/API/pysdkit._*.rst` (or a new family page + entry in `API/modules.rst`).
210+
4. Demo data: `pysdkit.data` loaders; `.npy` files live in `pysdkit/data/real_world`.
211+
212+
Do **not** import `repo/` (gitignored MATLAB/paper tree) from docs or examples.
213+
214+
### New gallery example
215+
216+
Write `examples/<section>/my_algo.py` (not a notebook):
217+
218+
```python
219+
r"""
220+
My algorithm
221+
============
222+
223+
Theory paragraph. Use RST (``:math:`x` ``, ``.. math::``).
224+
"""
225+
226+
# %%
227+
# More explanation
228+
# ----------------
229+
230+
import numpy as np
231+
from pysdkit import EMD
232+
```
233+
234+
- First block **must** be a title docstring with an underline (`====`).
235+
- Use a **raw** docstring (`r"""`) so LaTeX backslashes are valid Python.
236+
- Do **not** inject `sys.path` and do **not** use `__file__` or a `ROOT = Path.cwd()` hack. Sphinx-Gallery `exec`s the script; `__file__` is undefined.
237+
- Prefer `pysdkit.data` over local `.mat` files.
238+
- New folder: add `examples/<section>/GALLERY_HEADER.rst` with a title.
239+
240+
Legacy notebooks:
241+
242+
```bash
243+
python docs/ipynb_to_gallery.py # writes .py and deletes .ipynb
244+
python docs/ipynb_to_gallery.py --keep-ipynb
245+
```
246+
247+
### Release notes
248+
249+
Do not hand-edit `docs/source/release_notes/index.rst` for each version.
250+
Write the notes in the **GitHub Release** body (Markdown) for that tag.
251+
Sphinx pulls them at build time via `.. changelog::`.
252+
253+
On Read the Docs: **Admin → Environment variables** →
254+
`SPHINX_GITHUB_CHANGELOG_TOKEN`.
255+
256+
### Theme tweaks
257+
258+
| Want to change | File |
259+
|---|---|
260+
| Header / table / token colors | `docs/source/_static/custom.css` |
261+
| Code-block background, active tab, gallery cards | `docs/source/_static/theme_overrides.css` |
262+
| Logo | `docs/source/_static/logo.png` + `html_logo` in `conf.py` |
263+
| Navbar labels / order | titles of the rst files in `index.rst` toctree |
264+
265+
---
266+
267+
## Pitfalls
268+
269+
1. **Installing PyPI `pysdkit` instead of the checkout** — autodoc and examples will see an old package. Always `pip install -e .` (local) or `pip install .` (RTD).
270+
2. **`plt.show()` without Agg** — hangs a local smoke-test of a gallery script. Docs builds set Agg in `conf.py`.
271+
3. **Huge figures** — new runs use `savefig.dpi = 100`. If a card image is enormous, shrink the `figsize` in that script. Do not commit `auto_examples/` to GitHub.
272+
4. **Gallery failure still fails the Sphinx build** — `abort_on_example_error` is `False` so later examples still run, but Sphinx-Gallery raises `ExtensionError` at the end if any script failed unexpectedly. Fix the traceback (see `docs/build/html` log or the Sphinx temp log).
273+
5. **RST warnings** from markdown→RST conversion (`unindent`, unmatched `` ` ``) do not fail the build unless you pass `-W`.
274+
6. **`autosectionlabel`** uses `autosectionlabel_prefix_document = True` because many examples share headings like “1. Imports”.
275+
7. **Release notes need GitHub** — `sphinx-github-changelog` calls the Releases API at build time. Public anonymous access works until you hit the rate limit (60/hour). On Read the Docs, set environment variable `SPHINX_GITHUB_CHANGELOG_TOKEN` (or `GITHUB_TOKEN`) to a PAT. Do not commit the token.
276+
277+
---
278+
279+
## `docs/requirements.txt`
280+
281+
```
282+
sphinx>=8.0
283+
sphinx-copybutton
284+
sphinx_design>=0.5
285+
pydata-sphinx-theme>=0.16
286+
sphinx_gitstamp
287+
sphinx-gallery>=0.18
288+
ipython
289+
nbformat
290+
pillow
291+
scikit-learn
292+
sphinx-github-changelog>=2.3
293+
```
294+
295+
`ipython` + `nbformat` are for generated notebook downloads. `pillow` is for gallery image handling. `scikit-learn` is only for the EMD forecasting example. `sphinx-github-changelog` fills Release notes from GitHub. Pre-releases (including the `gallery-cache` asset) are omitted (`sphinx_github_changelog_include_prereleases = False`).
296+
297+
---
298+
299+
## Quick command card
300+
301+
```bash
302+
# editable install + docs deps
303+
pip install -e . -r docs/requirements.txt
304+
305+
# normal HTML
306+
sphinx-build -b html docs/source docs/build/html
307+
308+
# treat warnings as errors (strict; gallery RST noise may fail)
309+
sphinx-build -W -b html docs/source docs/build/html
310+
311+
# convert leftover notebooks (if any)
312+
python docs/ipynb_to_gallery.py
313+
314+
# pack and upload the RTD gallery cache (after a local sphinx-build)
315+
tar -czf auto_examples.tar.gz docs/source/auto_examples docs/source/sg_execution_times.rst
316+
gh release upload gallery-cache auto_examples.tar.gz --clobber
317+
```
318+
319+
Preview: `docs/build/html/index.html`
320+
Gallery: `docs/build/html/auto_examples/index.html`
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Pack Sphinx-Gallery output and upload it as a GitHub Release asset
2+
# (tag gallery-cache). Read the Docs downloads that tarball in pre_build
3+
# so unchanged examples are skipped and the 15-minute limit is enough.
4+
#
5+
# This is not a software release. The tag is a prerelease so it does not
6+
# become GitHub's "latest" version.
7+
#
8+
# First upload (already have a local gallery):
9+
# tar -czf auto_examples.tar.gz docs/source/auto_examples docs/source/sg_execution_times.rst
10+
# gh release create gallery-cache auto_examples.tar.gz --prerelease \
11+
# --title "Sphinx-Gallery cache" \
12+
# --notes "Generated example figures for Read the Docs. Not a software release."
13+
#
14+
# Or: Actions → Gallery cache → Run workflow.
15+
16+
name: Gallery cache
17+
18+
on:
19+
workflow_dispatch:
20+
push:
21+
branches: [main, master]
22+
paths:
23+
- "examples/**"
24+
- "docs/source/conf.py"
25+
- "docs/requirements.txt"
26+
- ".github/workflows/gallery-cache.yml"
27+
28+
permissions:
29+
contents: write
30+
31+
jobs:
32+
pack:
33+
name: Build and upload gallery cache
34+
runs-on: ubuntu-latest
35+
timeout-minutes: 90
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@v4
39+
40+
- name: Set up Python
41+
uses: actions/setup-python@v5
42+
with:
43+
python-version: "3.10"
44+
cache: pip
45+
46+
- name: Install package and docs extras
47+
run: |
48+
python -m pip install --upgrade pip
49+
pip install -e .
50+
pip install -r docs/requirements.txt
51+
52+
- name: Build HTML (executes Sphinx-Gallery)
53+
env:
54+
MPLBACKEND: Agg
55+
run: sphinx-build -b html docs/source docs/build/html
56+
57+
- name: Pack gallery tree
58+
run: |
59+
tar -czf auto_examples.tar.gz \
60+
docs/source/auto_examples \
61+
docs/source/sg_execution_times.rst
62+
63+
- name: Publish rolling GitHub Release
64+
env:
65+
GH_TOKEN: ${{ github.token }}
66+
run: |
67+
if gh release view gallery-cache >/dev/null 2>&1; then
68+
gh release upload gallery-cache auto_examples.tar.gz --clobber
69+
else
70+
gh release create gallery-cache auto_examples.tar.gz \
71+
--title "Sphinx-Gallery cache" \
72+
--notes "Generated example figures for Read the Docs incremental builds. Not a software release." \
73+
--prerelease
74+
fi

0 commit comments

Comments
 (0)