Skip to content

Commit 1892d6e

Browse files
authored
Cache jupytext conversion for docs builds (#603)
1 parent 7c012de commit 1892d6e

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

docs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ BUILDDIR = _build
1212
.PHONY: help clean html html-exec Makefile
1313

1414
html:
15-
@UPLT_DOCS_EXECUTE=$${UPLT_DOCS_EXECUTE:-always} $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" -E -a $(SPHINXOPTS)
15+
@UPLT_DOCS_EXECUTE=$${UPLT_DOCS_EXECUTE:-auto} $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS)
1616

1717
html-exec:
1818
@UPLT_DOCS_EXECUTE=always $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" -E -a $(SPHINXOPTS)

docs/conf.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,15 +403,19 @@ def _reset_ultraplot(gallery_conf, fname):
403403
"color-spec": ":py:func:`color-spec <matplotlib.colors.is_color_like>`",
404404
"artist": ":py:func:`artist <matplotlib.artist.Artist>`",
405405
}
406+
# Keep autodoc aliases stable across builds so incremental Sphinx caching works.
407+
autodoc_type_aliases = dict(napoleon_type_aliases)
406408

407409
# Fail on error. Note nbsphinx compiles all notebooks in docs unless excluded
408410
nbsphinx_allow_errors = False
409411

410412
# Give *lots* of time for cell execution
411413
nbsphinx_timeout = 300
412414

413-
# Add jupytext support to nbsphinx
414-
nbsphinx_custom_formats = {".py": ["jupytext.reads", {"fmt": "py:percent"}]}
415+
# Add jupytext support to nbsphinx with conversion cache.
416+
nbsphinx_custom_formats = {
417+
".py": ["sphinxext.jupytext_cache.reads_cached", {"fmt": "py:percent"}]
418+
}
415419

416420
# Keep notebook output backgrounds theme-adaptive.
417421
nbsphinx_execute_arguments = [

docs/sphinxext/jupytext_cache.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Jupytext converter with a small on-disk cache for docs builds.
3+
"""
4+
5+
import hashlib
6+
import os
7+
from pathlib import Path
8+
9+
import jupytext
10+
import nbformat
11+
12+
13+
def _get_cache_dir():
14+
"""
15+
Return cache directory for converted jupytext notebooks.
16+
"""
17+
override = os.environ.get("UPLT_DOCS_JUPYTEXT_CACHE_DIR", "").strip()
18+
if override:
19+
return Path(override).expanduser()
20+
if os.environ.get("READTHEDOCS", "") == "True":
21+
return Path.home() / ".cache" / "ultraplot" / "jupytext"
22+
return Path(__file__).resolve().parent.parent / "_build" / ".jupytext-cache"
23+
24+
25+
def reads_cached(inputstring, *, fmt="py:percent"):
26+
"""
27+
Convert Jupytext source to a notebook and cache by content hash.
28+
"""
29+
disabled = os.environ.get("UPLT_DOCS_DISABLE_JUPYTEXT_CACHE", "").strip().lower()
30+
if disabled in {"1", "true", "yes", "on"}:
31+
return jupytext.reads(inputstring, fmt=fmt)
32+
33+
key = hashlib.sha256(
34+
(fmt + "\0" + getattr(jupytext, "__version__", "") + "\0" + inputstring).encode(
35+
"utf-8"
36+
)
37+
).hexdigest()
38+
cache_file = _get_cache_dir() / f"{key}.ipynb"
39+
if cache_file.is_file():
40+
try:
41+
return nbformat.read(cache_file, as_version=4)
42+
except Exception:
43+
cache_file.unlink(missing_ok=True)
44+
45+
notebook = jupytext.reads(inputstring, fmt=fmt)
46+
try:
47+
cache_file.parent.mkdir(parents=True, exist_ok=True)
48+
nbformat.write(notebook, cache_file)
49+
except Exception:
50+
pass
51+
return notebook

0 commit comments

Comments
 (0)