Skip to content

Commit b2e59ee

Browse files
authored
Merge pull request #2042 from mikedh/docs/examples
Docs: Render Examples
2 parents 3191ea7 + 08a59e7 commit b2e59ee

17 files changed

Lines changed: 131 additions & 72 deletions

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ RUN make
105105

106106
### Copy just the docs so we can output them
107107
FROM scratch as docs
108-
COPY --from=build_docs /home/user/docs/_build/html/ ./
108+
COPY --from=build_docs /home/user/docs/built/html/ ./
109109

110110
### Make sure the output stage is the last stage so a simple
111111
# "docker build ." still outputs an expected image

docs/Makefile

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,45 @@ SPHINXGEN ?= sphinx-apidoc
66
NBCONVERT ?= jupyter
77
PYTHON ?= python
88
PIP ?= pip
9-
SOURCEDIR = .
10-
BUILDDIR = _build
9+
BUILDDIR = built
1110
TEMPLATESDIR = templates
12-
STATICDIR = _static
11+
# where to put generated RST files
12+
GENDIR = generate
13+
CONTENT = content
14+
STATICDIR = static
15+
1316

1417
example_notebooks := $(wildcard ../examples/*.ipynb)
1518
example_notebooks := $(filter-out ../examples/save_image.ipynb, $(example_notebooks))
16-
1719
example_names = $(foreach path, $(example_notebooks), $(basename $(notdir $(path))))
18-
example_htmls = $(foreach name, $(example_names), $(STATICDIR)/examples/$(name).html)
1920
example_rsts = $(foreach name, $(example_names), examples.$(name).rst)
2021

21-
html: conf.py index.rst *.md README.rst trimesh.rst examples.md $(example_rsts) $(example_htmls) .deps
22-
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
23-
touch "$(BUILDDIR)/html/.nojekyll"
22+
23+
html: conf.py $(CONTENT)/index.rst trimesh.rst README.rst $(example_rsts) examples.rst .deps
24+
@$(SPHINXBUILD) -M html "$(GENDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
2425
echo "trimesh.org" > "$(BUILDDIR)/html/CNAME"
25-
mv "$(BUILDDIR)/html/_static/examples" "$(BUILDDIR)/html/examples" || true
26-
mv "$(BUILDDIR)/html/_static/images" "$(BUILDDIR)/html/images" || true
27-
cp "$(STATICDIR)/favicon.ico" "$(BUILDDIR)/html/favicon.ico" || true
26+
touch "$(BUILDDIR)/html/.nojekyll"
27+
cp -R "$(STATICDIR)/images" "$(BUILDDIR)/html/images" || true
28+
cp "$(STATICDIR)/favicon.ico" "$(BUILDDIR)/html/favicon.ico" || true
2829

2930
.deps: requirements.txt
3031
$(PIP) install -r requirements.txt
3132
$(PIP) freeze > .deps
33+
mkdir -p $(GENDIR)
34+
mkdir -p $(BUILDDIR)
35+
cp conf.py $(CONTENT)/* $(GENDIR)
3236

33-
$(STATICDIR)/examples/%.html: ../examples/%.ipynb .deps
34-
mkdir -p "$(STATICDIR)/examples"
35-
$(NBCONVERT) nbconvert --execute --to html --output $(abspath $@) $<
36-
37-
examples.%.rst: $(STATICDIR)/examples/%.html examples.template
38-
$(PYTHON) -c "open('$@', 'w').write(open('examples.template').read().format(name='$(*F)', url='$<'))"
37+
examples.%.rst : ../examples/%.ipynb .deps
38+
$(NBCONVERT) nbconvert --execute --to rst --output-dir $(GENDIR) $<
3939

40-
examples.md: .deps
41-
$(PYTHON) "examples.py"
40+
examples.rst: .deps
41+
$(PYTHON) examples.py --source=../examples --target=$(GENDIR)/examples.rst
4242

4343
trimesh.rst: .deps
44-
$(SPHINXGEN) -eTf -t "$(TEMPLATESDIR)" -o "$(SOURCEDIR)" ../trimesh
44+
$(SPHINXGEN) -eTf -t "$(TEMPLATESDIR)" -o "$(GENDIR)" ../trimesh
4545

4646
README.rst: ../README.md .deps
47-
pandoc --from=gfm --to=rst --output=README.rst ../README.md
47+
pandoc --from=gfm --to=rst --output="$(GENDIR)/README.rst" ../README.md
4848

4949
clean:
50-
rm -rvf "$(BUILDDIR)" "$(STATICDIR)/examples" examples.*.rst trimesh*.rst .deps
50+
rm -rvf "$(BUILDDIR)" "$(GENDIR)" .deps

docs/conf.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,12 @@ def abspath(rel):
8080
html_theme = "furo"
8181

8282
# options for furo
83-
html_theme_options = {
84-
"display_version": True,
85-
}
83+
html_theme_options = {}
8684

8785
# Add any paths that contain custom static files (such as style sheets) here,
8886
# relative to this directory. They are copied after the builtin static files,
8987
# so a file named "default.css" will overwrite the builtin "default.css".
90-
html_static_path = ["_static"]
88+
html_static_path = ["static"]
9189
html_logo = "images/trimesh-logo.png"
9290

9391
# custom css
File renamed without changes.
File renamed without changes.

docs/index.rst renamed to docs/content/index.rst

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,28 @@ Install
1010
.. toctree::
1111
:maxdepth: 2
1212

13-
guides/install.md
14-
15-
Examples
16-
==========
17-
.. toctree::
18-
:maxdepth: 2
19-
20-
examples.md
13+
install.md
2114

2215
Contributing
2316
==========
2417
.. toctree::
2518
:maxdepth: 1
2619

27-
Contributing <guides/contributing.md>
20+
Contributing <contributing.md>
2821

2922
Docker
3023
==========
3124
.. toctree::
3225
:maxdepth: 1
3326

34-
Docker <guides/docker.md>
27+
Docker <docker.md>
28+
29+
Examples
30+
==========
31+
.. toctree::
32+
:maxdepth: 1
33+
34+
Examples <examples.rst>
3535

3636
API Reference
3737
=============
File renamed without changes.
File renamed without changes.

docs/examples.py

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
examples.py
33
------------
44
5-
Generate `examples.md` from the contents
5+
Convert `ipynb` to a web-renderable format from the contents
66
of `../examples/*.ipynb`
77
"""
88

9-
import json
109
import logging
1110
import os
1211
import sys
@@ -18,12 +17,6 @@
1817
# current working directory
1918
pwd = os.path.abspath(os.path.expanduser(os.path.dirname(__file__)))
2019

21-
# where are our notebooks to render
22-
source = os.path.abspath(os.path.join(pwd, "..", "examples"))
23-
24-
# which index file are we generating
25-
target = os.path.abspath(os.path.join(pwd, "examples.md"))
26-
2720

2821
def extract_docstring(loaded):
2922
"""
@@ -50,27 +43,104 @@ def extract_docstring(loaded):
5043
return " ".join(i.strip() for i in source[1:-1])
5144

5245

53-
if __name__ == "__main__":
54-
markdown = [
55-
"# Examples",
46+
base = """
47+
{title} </{file_name}.html>
48+
"""
49+
50+
51+
def generate_index(source: str, target: str) -> str:
52+
"""
53+
Go through a directory of source `ipynb` files and write
54+
an RST index with a toctree.
55+
56+
Also postprocesses the results of `jupyter nbconvert`
57+
"""
58+
59+
lines = [
60+
"Examples",
61+
"===========",
5662
"Several examples are available as rendered IPython notebooks.",
5763
"",
64+
".. toctree::",
65+
" :maxdepth: 2",
66+
"",
5867
]
5968

69+
target_dir = os.path.dirname(target)
70+
6071
for fn in os.listdir(source):
6172
if not fn.lower().endswith(".ipynb"):
6273
continue
63-
path = os.path.join(source, fn)
64-
with open(path) as f:
65-
raw = json.load(f)
66-
doc = extract_docstring(raw)
67-
log.info(f'`{fn}`: "{doc}"\n')
68-
link = f'examples.{fn.split(".")[0]}.html'
69-
70-
markdown.append(f"### [{fn}]({link})")
71-
markdown.append(doc)
72-
markdown.append("")
73-
74-
final = "\n".join(markdown)
74+
75+
name = fn.rsplit(".")[0]
76+
title = name.replace("_", " ").title()
77+
# notebook converted to RST
78+
convert = os.path.join(target_dir, f"{name}.rst")
79+
if not os.path.exists(convert):
80+
print(f"no RST for {name}.rst")
81+
continue
82+
83+
with open(convert) as f:
84+
doc, post = postprocess(f.read(), title=title)
85+
with open(convert, "w") as f:
86+
f.write(post)
87+
88+
lines.append(f" {name}")
89+
# lines.append(doc)
90+
lines.append("")
91+
92+
return "\n".join(lines)
93+
94+
95+
def postprocess(text: str, title: str) -> str:
96+
"""
97+
Postprocess an RST generated from `jupyter nbconvert`
98+
"""
99+
lines = str.splitlines(text)
100+
101+
# already has a title so exit
102+
if "===" in "".join(lines[:4]):
103+
return "", text
104+
105+
head = []
106+
index = 0
107+
ready = False
108+
for i, L in enumerate(lines):
109+
if "parsed-literal" in L:
110+
ready = True
111+
continue
112+
if ready:
113+
if "code::" in L:
114+
index = i
115+
break
116+
else:
117+
head.append(L)
118+
119+
# clean up the "parsed literal"
120+
docstring = (
121+
" ".join(" ".join(head).replace("\\n", " ").split()).strip().strip("'").strip()
122+
)
123+
124+
# add a title and the docstring as a header
125+
clip = f"{title}\n=============\n{docstring}\n\n" + "\n".join(lines[index:])
126+
127+
return docstring, clip
128+
129+
130+
if __name__ == "__main__":
131+
import argparse
132+
133+
parser = argparse.ArgumentParser()
134+
parser.add_argument(
135+
"--source", type=str, help="a directory containing `ipynb` files", required=True
136+
)
137+
parser.add_argument(
138+
"--target", type=str, help="Where the generated .rst file goes", required=True
139+
)
140+
args = parser.parse_args()
141+
142+
source = os.path.abspath(args.source)
143+
target = os.path.abspath(args.target)
144+
75145
with open(target, "w") as f:
76-
f.write(final)
146+
f.write(generate_index(source=source, target=target))

docs/examples.template

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)