|
| 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` |
0 commit comments