sphinx-examples-as-code 0.6.1__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sphinx_examples_as_code-0.8.0/.github/dependabot.yml +22 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/.github/workflows/ci.yml +20 -4
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/PKG-INFO +108 -29
- sphinx_examples_as_code-0.8.0/README.md +252 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/pyproject.toml +3 -1
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code/__init__.py +490 -57
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code/_version.py +3 -3
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/PKG-INFO +108 -29
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/requires.txt +4 -0
- sphinx_examples_as_code-0.8.0/sphinx_examples_as_code.egg-info/scm_version.json +8 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/test_gallery_downloads.py +84 -11
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/test_heading_level_reuse.py +7 -7
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/test_single_function_page.py +5 -5
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/test_sphinx_examples_as_code.py +735 -127
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/test_tinypages.py +79 -67
- sphinx_examples_as_code-0.6.1/.github/dependabot.yml +0 -16
- sphinx_examples_as_code-0.6.1/README.md +0 -176
- sphinx_examples_as_code-0.6.1/sphinx_examples_as_code.egg-info/scm_version.json +0 -8
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/.github/release.yml +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/.gitignore +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/.pre-commit-config.yaml +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/LICENSE +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/setup.cfg +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/SOURCES.txt +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/dependency_links.txt +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/scm_file_list.json +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/sphinx_examples_as_code.egg-info/top_level.txt +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/gallery_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/gallery_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/gallery_fixture/examples/plot_minimal.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/gallery_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/heading_level_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/heading_level_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/heading_level_fixture/examples/plot_reused_level.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/heading_level_fixture/examples/plot_three_levels.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/heading_level_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/single_function_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/single_function_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/single_function_fixture/mymodule.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/tinypages/conf.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/tinypages/docstring_cases.py +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/tinypages/docstring_cases.rst +0 -0
- {sphinx_examples_as_code-0.6.1 → sphinx_examples_as_code-0.8.0}/tests/tinypages/index.rst +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
multi-ecosystem-groups:
|
|
3
|
+
maintenance:
|
|
4
|
+
schedule:
|
|
5
|
+
interval: quarterly
|
|
6
|
+
labels: [maintenance, dependencies]
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: pip
|
|
9
|
+
directory: /
|
|
10
|
+
schedule:
|
|
11
|
+
interval: weekly
|
|
12
|
+
labels: [maintenance, dependencies]
|
|
13
|
+
allow:
|
|
14
|
+
- dependency-name: pyvista
|
|
15
|
+
- dependency-name: sphinx-gallery
|
|
16
|
+
groups:
|
|
17
|
+
project:
|
|
18
|
+
patterns: ['*']
|
|
19
|
+
- package-ecosystem: github-actions
|
|
20
|
+
directory: /
|
|
21
|
+
patterns: ['*']
|
|
22
|
+
multi-ecosystem-group: maintenance
|
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
with:
|
|
23
23
|
persist-credentials: false
|
|
24
24
|
fetch-depth: 0
|
|
25
|
-
- uses: astral-sh/setup-uv@
|
|
25
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
26
26
|
with:
|
|
27
27
|
enable-cache: false
|
|
28
28
|
- run: uv sync --group dev --extra gallery
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
with:
|
|
44
44
|
persist-credentials: false
|
|
45
45
|
fetch-depth: 0
|
|
46
|
-
- uses: astral-sh/setup-uv@
|
|
46
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
47
47
|
with:
|
|
48
48
|
enable-cache: false
|
|
49
49
|
- uses: pyvista/setup-headless-display-action@c103a2ff45650d38cb71684b5dc6cdfeb9442c79 # v5.1.0
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
pyvista: true
|
|
52
52
|
- run: uv sync --group dev --extra gallery --python ${{ matrix.python-version }}
|
|
53
53
|
- run: uv run pytest tests/ --cov --cov-report=xml
|
|
54
|
-
- uses: codecov/codecov-action@
|
|
54
|
+
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
|
55
55
|
with:
|
|
56
56
|
files: ./coverage.xml
|
|
57
57
|
env:
|
|
@@ -65,7 +65,7 @@ jobs:
|
|
|
65
65
|
with:
|
|
66
66
|
persist-credentials: false
|
|
67
67
|
fetch-depth: 0
|
|
68
|
-
- uses: astral-sh/setup-uv@
|
|
68
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
69
69
|
with:
|
|
70
70
|
enable-cache: false
|
|
71
71
|
- run: uv build
|
|
@@ -87,3 +87,19 @@ jobs:
|
|
|
87
87
|
name: dist
|
|
88
88
|
path: dist/
|
|
89
89
|
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
90
|
+
|
|
91
|
+
release:
|
|
92
|
+
needs: publish
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
permissions:
|
|
95
|
+
contents: write
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
98
|
+
with:
|
|
99
|
+
name: dist
|
|
100
|
+
path: dist/
|
|
101
|
+
# Notes come from the merged pull requests, shaped by .github/release.yml.
|
|
102
|
+
- env:
|
|
103
|
+
GH_TOKEN: ${{ github.token }}
|
|
104
|
+
TAG: ${{ github.ref_name }}
|
|
105
|
+
run: gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --title "$TAG" --generate-notes --verify-tag dist/*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-examples-as-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Sphinx extension for converting docstring examples into downloadable code.
|
|
5
5
|
Author-email: The PyVista Developers <info@pyvista.org>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,15 +22,19 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
+
Requires-Dist: packaging>=22
|
|
25
26
|
Requires-Dist: pyvista>=0.48
|
|
26
27
|
Provides-Extra: gallery
|
|
27
28
|
Requires-Dist: sphinx-gallery<0.23,>=0.22; extra == "gallery"
|
|
29
|
+
Provides-Extra: marimo
|
|
30
|
+
Requires-Dist: marimo>=0.14; extra == "marimo"
|
|
28
31
|
Dynamic: license-file
|
|
29
32
|
|
|
30
33
|
# sphinx-examples-as-code
|
|
31
34
|
|
|
32
35
|
A Sphinx extension that turns docstring/page "Examples" sections into downloadable,
|
|
33
|
-
runnable
|
|
36
|
+
runnable Python scripts, Jupyter notebooks and/or [marimo](https://marimo.io) notebooks,
|
|
37
|
+
with a download link inserted into the section.
|
|
34
38
|
|
|
35
39
|
Pages or docstrings without an Examples section are left completely untouched. Adding
|
|
36
40
|
`sphinx_examples_as_code` to `conf.py`'s `extensions` is the only on/off switch.
|
|
@@ -48,6 +52,12 @@ Sphinx-Gallery integration is opt-in and needs a compatible sphinx-gallery, whic
|
|
|
48
52
|
pip install 'sphinx-examples-as-code[gallery]'
|
|
49
53
|
```
|
|
50
54
|
|
|
55
|
+
marimo notebook downloads need marimo 0.14 or newer, which the `marimo` extra installs:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install 'sphinx-examples-as-code[marimo]'
|
|
59
|
+
```
|
|
60
|
+
|
|
51
61
|
Add it to your Sphinx `conf.py`:
|
|
52
62
|
|
|
53
63
|
```python
|
|
@@ -65,23 +75,32 @@ the keys you want to change from their default:
|
|
|
65
75
|
```python
|
|
66
76
|
sphinx_examples_as_code_conf = {
|
|
67
77
|
'link_position': 'top',
|
|
68
|
-
'formats': ['
|
|
78
|
+
'formats': ['python', 'jupyter', 'marimo'],
|
|
69
79
|
'gallery_downloads': False,
|
|
70
80
|
'footer': (
|
|
71
81
|
'Generated by `sphinx-examples-as-code '
|
|
72
82
|
'<https://github.com/pyvista/sphinx-examples-as-code>`_'
|
|
73
83
|
),
|
|
74
84
|
'link_labels': {
|
|
75
|
-
'
|
|
76
|
-
'
|
|
85
|
+
'python': 'Download Python script',
|
|
86
|
+
'jupyter': 'Download Jupyter notebook',
|
|
87
|
+
'marimo': 'Download marimo notebook',
|
|
77
88
|
},
|
|
89
|
+
'script_metadata': False,
|
|
90
|
+
'dependencies': {},
|
|
91
|
+
'requires_python': None,
|
|
78
92
|
}
|
|
79
93
|
```
|
|
80
94
|
|
|
81
95
|
- `link_position`: where the download link(s) land within the Examples section. `'top'`
|
|
82
96
|
(default) or `'bottom'`.
|
|
83
|
-
- `formats`: which downloads to generate. A list containing `'
|
|
84
|
-
|
|
97
|
+
- `formats`: which downloads to generate. A list containing any of `'python'` (a plain
|
|
98
|
+
Python script, `<name>.py`), `'jupyter'` (a Jupyter notebook, `<name>.ipynb`) and
|
|
99
|
+
`'marimo'` (a marimo notebook, `<name>.marimo.py`); defaults to all three. Always
|
|
100
|
+
offered in that order regardless of how the list is written. Without marimo 0.14 or
|
|
101
|
+
newer installed, the default skips the marimo download, and a `formats` that names
|
|
102
|
+
`'marimo'` (in `conf.py` or through `-D`) stops the build with a configuration error. An unknown format raises a configuration error too;
|
|
103
|
+
the old `'py'` and `'ipynb'` names say what replaced them.
|
|
85
104
|
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
|
|
86
105
|
format(s) you want to change; any left unset keep reading their own default shown above.
|
|
87
106
|
- `gallery_downloads`: opt-in takeover of
|
|
@@ -91,11 +110,22 @@ sphinx_examples_as_code_conf = {
|
|
|
91
110
|
- `footer`: a string appended to the end of every generated file. Defaults to a one-line
|
|
92
111
|
"generated file" credit linking back to this project; set to `None` to omit it
|
|
93
112
|
entirely. Preceded by a blank line and a `-`-only divider line, which also renders as a
|
|
94
|
-
real horizontal rule in
|
|
113
|
+
real horizontal rule in notebooks; the footer always gets its own dedicated cell there.
|
|
95
114
|
Parsed as RST: a hyperlink written as `` `text <url>`_ `` becomes a real clickable
|
|
96
|
-
Markdown link in
|
|
97
|
-
markup at all becomes one comment line per line of text, and
|
|
98
|
-
paragraphs stay separated.
|
|
115
|
+
Markdown link in notebooks, and renders inline as `text url` in Python scripts. Plain
|
|
116
|
+
text with no markup at all becomes one comment line per line of text, and
|
|
117
|
+
blank-line-separated paragraphs stay separated.
|
|
118
|
+
- `script_metadata`: `True` adds each download's dependencies and a one-line run
|
|
119
|
+
command. See [Dependencies and run commands](#dependencies-and-run-commands) below.
|
|
120
|
+
`False` (default) adds neither.
|
|
121
|
+
- `dependencies`: extra PEP 508 requirements per format, added to the detected ones, e.g.
|
|
122
|
+
`{'jupyter': ['pyvista[jupyter]']}`. An entry naming a detected project replaces it,
|
|
123
|
+
so `{'python': ['pyvista>=0.46']}` sets its version. An entry without a version keeps
|
|
124
|
+
the automatic pin described below. Only used with `script_metadata` on.
|
|
125
|
+
- `requires_python`: a `requires-python` specifier for the dependency block, e.g.
|
|
126
|
+
`'>=3.10'`. `None` (default) derives one from the dependencies instead; see
|
|
127
|
+
[Dependencies and run commands](#dependencies-and-run-commands). Only used with
|
|
128
|
+
`script_metadata` on.
|
|
99
129
|
|
|
100
130
|
An unrecognized key raises a configuration error at build start.
|
|
101
131
|
|
|
@@ -107,6 +137,46 @@ Overriding a single key from the command line works via a dotted `-D` flag, e.g.
|
|
|
107
137
|
`-D sphinx_examples_as_code_conf.link_position=bottom` -- this only touches that one
|
|
108
138
|
key, leaving the rest (`conf.py`'s values, or the defaults) alone.
|
|
109
139
|
|
|
140
|
+
## Dependencies and run commands
|
|
141
|
+
|
|
142
|
+
With `script_metadata` on, each download lists the third-party projects its code
|
|
143
|
+
imports, plus any `dependencies` set for its format, and says how to run it:
|
|
144
|
+
|
|
145
|
+
- Python scripts start with a [PEP 723](https://peps.python.org/pep-0723/) `# /// script`
|
|
146
|
+
block, followed by `# Run this script with: uv run <name>.py`.
|
|
147
|
+
- Jupyter notebooks say `` Open this notebook with: `uvx --python "<requires-python>" --from jupyterlab --with <dependency> ... jupyter-lab <name>.ipynb` `` in their first cell,
|
|
148
|
+
with `--python` only when there is a `requires-python`. Arguments are double-quoted where
|
|
149
|
+
needed, so the command works in bash, zsh, PowerShell and cmd. An argument containing
|
|
150
|
+
`"`, `$`, a backtick, `\`, `%` or a `!` not followed by `=` is single-quoted instead,
|
|
151
|
+
which cmd does not understand.
|
|
152
|
+
- marimo notebooks start with the same `# /// script` block, which also lists `marimo`,
|
|
153
|
+
followed by `# Open this notebook with: uvx marimo edit --sandbox <name>.marimo.py`.
|
|
154
|
+
- Each run command is followed by a line linking to
|
|
155
|
+
[uv's installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
|
156
|
+
|
|
157
|
+
Imports are read from the code, not run. Each imported top-level module maps to the
|
|
158
|
+
project that provides it in the environment building the docs; standard-library
|
|
159
|
+
modules, relative imports and modules no single installed project provides are left
|
|
160
|
+
out. Projects the code needs but never imports, such as an optional backend, go in
|
|
161
|
+
`dependencies`.
|
|
162
|
+
|
|
163
|
+
The project named by Sphinx's own `project` setting (e.g. `project = 'PyVista'`) gets
|
|
164
|
+
extra treatment when an example imports it and it is installed: its dependency becomes
|
|
165
|
+
`<name>>=X.Y`, the installed release's major and minor version, or plain `<name>` for a
|
|
166
|
+
pre-release or dev build.
|
|
167
|
+
|
|
168
|
+
Unless `requires_python` is set, the block's `requires-python` combines:
|
|
169
|
+
|
|
170
|
+
- that project's own `Requires-Python`, e.g. `>=3.10`, when it is listed;
|
|
171
|
+
- `<3.N`, one minor version above the newest Python named in the `Programming Language ::
|
|
172
|
+
Python :: 3.X` classifiers of that project and of every listed dependency installed
|
|
173
|
+
from a platform-specific wheel, as installed in the environment building the docs.
|
|
174
|
+
Pure-Python dependencies and dependencies without such classifiers don't count, and
|
|
175
|
+
the bound is dropped when it would exclude every Python the project's own
|
|
176
|
+
`Requires-Python` allows.
|
|
177
|
+
|
|
178
|
+
With neither, the line is left out.
|
|
179
|
+
|
|
110
180
|
## Conversion rules
|
|
111
181
|
|
|
112
182
|
What happens to the content of an Examples section:
|
|
@@ -116,39 +186,43 @@ What happens to the content of an Examples section:
|
|
|
116
186
|
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
|
|
117
187
|
comments, set off with blank lines on both sides like any other directive.
|
|
118
188
|
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
|
|
119
|
-
their content as comments, indented one level under the label in
|
|
189
|
+
their content as comments, indented one level under the label in Python scripts only.
|
|
120
190
|
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
|
|
121
191
|
line on both sides in either format — a bare `#` instead of a real blank line when
|
|
122
|
-
that falls inside an admonition or a definition's own body in
|
|
123
|
-
thing still reads as one unbroken comment block. A definition list's term stays
|
|
124
|
-
the surrounding indent; its definition (the body nested under it) is indented one
|
|
125
|
-
level further in
|
|
192
|
+
that falls inside an admonition or a definition's own body in a Python script, so the
|
|
193
|
+
whole thing still reads as one unbroken comment block. A definition list's term stays
|
|
194
|
+
at the surrounding indent; its definition (the body nested under it) is indented one
|
|
195
|
+
level further in Python scripts, same as an admonition's own content.
|
|
126
196
|
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
|
|
127
197
|
double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
|
|
128
198
|
``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
|
|
129
|
-
the reference resolves,
|
|
130
|
-
link, only the display text.
|
|
199
|
+
the reference resolves, notebooks turn it into a clickable link; Python scripts never
|
|
200
|
+
write the link, only the display text.
|
|
131
201
|
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
|
|
132
202
|
backticks.
|
|
133
203
|
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
|
|
134
204
|
`#` comment.
|
|
135
205
|
- A table becomes an aligned text table, its caption on the line above: an RST simple
|
|
136
|
-
table in
|
|
137
|
-
line, and a column left entirely empty -- one holding only images, say -- is
|
|
138
|
-
along with its heading.
|
|
206
|
+
table in Python scripts, a Markdown pipe table in notebooks. Each cell is flattened to a
|
|
207
|
+
single line, and a column left entirely empty -- one holding only images, say -- is
|
|
208
|
+
dropped along with its heading.
|
|
139
209
|
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
|
|
140
210
|
line are dropped entirely.
|
|
141
211
|
- "See Also" content is always dropped.
|
|
142
212
|
|
|
143
|
-
Generated
|
|
213
|
+
Generated Python scripts start with a `# Examples from <qualified name>` title header
|
|
144
214
|
(gallery mode uses the page's own title instead -- see below), with a few whitespace
|
|
145
215
|
conventions: prose directly above a code block stays attached to it, a code block is
|
|
146
216
|
always followed by a blank line, and a directive (header, `# NOTE:`-style block) gets
|
|
147
217
|
blank lines on both sides.
|
|
148
218
|
|
|
149
|
-
Generated
|
|
219
|
+
Generated Jupyter notebooks use the same content, split into alternating code/markdown
|
|
150
220
|
cells instead.
|
|
151
221
|
|
|
222
|
+
Generated marimo notebooks are that same Jupyter notebook converted by marimo's own
|
|
223
|
+
converter, the one behind `marimo convert`. `from __future__` imports are dropped first,
|
|
224
|
+
so the notebook also runs as a plain script (`python <name>.marimo.py`).
|
|
225
|
+
|
|
152
226
|
A download link is only added if the resulting code contains at least one real
|
|
153
227
|
executable statement.
|
|
154
228
|
|
|
@@ -157,7 +231,7 @@ executable statement.
|
|
|
157
231
|
With `sphinx_examples_as_code_conf['gallery_downloads'] = True`, this extension takes
|
|
158
232
|
over the downloads on every page generated by
|
|
159
233
|
[sphinx-gallery](https://sphinx-gallery.github.io): the "Go to the end to download the
|
|
160
|
-
full example code" note and the `.py`/`.
|
|
234
|
+
full example code" note and the `.py`/`.zip` download footer are removed from
|
|
161
235
|
the page, replaced with download link(s) built by this extension instead — same
|
|
162
236
|
conversion rules as above, applied to the whole page rather than one Examples section.
|
|
163
237
|
The generated file's header uses the page's own title (e.g. `# Create Circular Arcs`)
|
|
@@ -172,7 +246,7 @@ is left completely untouched. Gallery pages and ordinary docstring/prose pages (
|
|
|
172
246
|
the Examples-section behavior above) can coexist on the same site.
|
|
173
247
|
|
|
174
248
|
A `# %%` cell with its own RST heading gets the same header treatment as the file's own
|
|
175
|
-
title, and renders as a real Markdown heading in
|
|
249
|
+
title, and renders as a real Markdown heading in notebooks. Level is relative to actual
|
|
176
250
|
RST section nesting, not always one below the file's own header: a cell heading nested
|
|
177
251
|
under the page's own title (the common case) is one level below it; one that reuses the
|
|
178
252
|
page title's own underline character is level 1, the same as the file's own header; a
|
|
@@ -181,10 +255,10 @@ cell heading nested under *that* is level 2 relative to it, and so on.
|
|
|
181
255
|
Each format uses one heading style consistently across every level, rather than mixing
|
|
182
256
|
styles:
|
|
183
257
|
|
|
184
|
-
-
|
|
258
|
+
- Python scripts use an RST-style title + underline, one character per level -- the same
|
|
185
259
|
sequence Sphinx's own documentation uses for sections through sub-paragraphs: `=`, `-`,
|
|
186
260
|
`~`, `^`, `"`, `'` for levels 1 through 6.
|
|
187
|
-
-
|
|
261
|
+
- Notebooks always use ATX syntax (`#`, `##`, `###`, ...) at every level.
|
|
188
262
|
|
|
189
263
|
Two things worth knowing before turning this on:
|
|
190
264
|
|
|
@@ -193,8 +267,13 @@ Two things worth knowing before turning this on:
|
|
|
193
267
|
block — not a documented extension API. A future sphinx-gallery release could shift
|
|
194
268
|
that structure without warning. Only sphinx-gallery 0.22.x is tested, so the `gallery`
|
|
195
269
|
extra caps there rather than letting the next release quietly produce no downloads.
|
|
196
|
-
- sphinx-gallery's own `.py`/`.
|
|
197
|
-
|
|
270
|
+
- sphinx-gallery's own `.py`/`.zip` downloads still end up copied into `_downloads/`,
|
|
271
|
+
even though nothing on the page links to them anymore. Its `.ipynb` is never written:
|
|
272
|
+
with `gallery_downloads` on, this extension sets `sphinx_gallery_conf['pypandoc']` to
|
|
273
|
+
`False` and `sphinx_gallery_conf['notebook_extensions']` to an empty set before
|
|
274
|
+
sphinx-gallery reads them, so pandoc is not needed. Setting `pypandoc`,
|
|
275
|
+
`notebook_extensions`, `first_notebook_cell`, `last_notebook_cell` or `notebook_images`
|
|
276
|
+
yourself has no effect and is reported as a warning.
|
|
198
277
|
|
|
199
278
|
## Development
|
|
200
279
|
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# sphinx-examples-as-code
|
|
2
|
+
|
|
3
|
+
A Sphinx extension that turns docstring/page "Examples" sections into downloadable,
|
|
4
|
+
runnable Python scripts, Jupyter notebooks and/or [marimo](https://marimo.io) notebooks,
|
|
5
|
+
with a download link inserted into the section.
|
|
6
|
+
|
|
7
|
+
Pages or docstrings without an Examples section are left completely untouched. Adding
|
|
8
|
+
`sphinx_examples_as_code` to `conf.py`'s `extensions` is the only on/off switch.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install sphinx-examples-as-code
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Sphinx-Gallery integration is opt-in and needs a compatible sphinx-gallery, which the
|
|
17
|
+
`gallery` extra pins:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install 'sphinx-examples-as-code[gallery]'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
marimo notebook downloads need marimo 0.14 or newer, which the `marimo` extra installs:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install 'sphinx-examples-as-code[marimo]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Add it to your Sphinx `conf.py`:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
extensions = [
|
|
33
|
+
...,
|
|
34
|
+
'sphinx_examples_as_code',
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
Everything lives in one dict in `conf.py`, `sphinx_examples_as_code_conf` -- set only
|
|
41
|
+
the keys you want to change from their default:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
sphinx_examples_as_code_conf = {
|
|
45
|
+
'link_position': 'top',
|
|
46
|
+
'formats': ['python', 'jupyter', 'marimo'],
|
|
47
|
+
'gallery_downloads': False,
|
|
48
|
+
'footer': (
|
|
49
|
+
'Generated by `sphinx-examples-as-code '
|
|
50
|
+
'<https://github.com/pyvista/sphinx-examples-as-code>`_'
|
|
51
|
+
),
|
|
52
|
+
'link_labels': {
|
|
53
|
+
'python': 'Download Python script',
|
|
54
|
+
'jupyter': 'Download Jupyter notebook',
|
|
55
|
+
'marimo': 'Download marimo notebook',
|
|
56
|
+
},
|
|
57
|
+
'script_metadata': False,
|
|
58
|
+
'dependencies': {},
|
|
59
|
+
'requires_python': None,
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- `link_position`: where the download link(s) land within the Examples section. `'top'`
|
|
64
|
+
(default) or `'bottom'`.
|
|
65
|
+
- `formats`: which downloads to generate. A list containing any of `'python'` (a plain
|
|
66
|
+
Python script, `<name>.py`), `'jupyter'` (a Jupyter notebook, `<name>.ipynb`) and
|
|
67
|
+
`'marimo'` (a marimo notebook, `<name>.marimo.py`); defaults to all three. Always
|
|
68
|
+
offered in that order regardless of how the list is written. Without marimo 0.14 or
|
|
69
|
+
newer installed, the default skips the marimo download, and a `formats` that names
|
|
70
|
+
`'marimo'` (in `conf.py` or through `-D`) stops the build with a configuration error. An unknown format raises a configuration error too;
|
|
71
|
+
the old `'py'` and `'ipynb'` names say what replaced them.
|
|
72
|
+
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
|
|
73
|
+
format(s) you want to change; any left unset keep reading their own default shown above.
|
|
74
|
+
- `gallery_downloads`: opt-in takeover of
|
|
75
|
+
[sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
|
|
76
|
+
`False` (default) leaves sphinx-gallery pages untouched. See
|
|
77
|
+
[Sphinx-Gallery integration](#sphinx-gallery-integration) below.
|
|
78
|
+
- `footer`: a string appended to the end of every generated file. Defaults to a one-line
|
|
79
|
+
"generated file" credit linking back to this project; set to `None` to omit it
|
|
80
|
+
entirely. Preceded by a blank line and a `-`-only divider line, which also renders as a
|
|
81
|
+
real horizontal rule in notebooks; the footer always gets its own dedicated cell there.
|
|
82
|
+
Parsed as RST: a hyperlink written as `` `text <url>`_ `` becomes a real clickable
|
|
83
|
+
Markdown link in notebooks, and renders inline as `text url` in Python scripts. Plain
|
|
84
|
+
text with no markup at all becomes one comment line per line of text, and
|
|
85
|
+
blank-line-separated paragraphs stay separated.
|
|
86
|
+
- `script_metadata`: `True` adds each download's dependencies and a one-line run
|
|
87
|
+
command. See [Dependencies and run commands](#dependencies-and-run-commands) below.
|
|
88
|
+
`False` (default) adds neither.
|
|
89
|
+
- `dependencies`: extra PEP 508 requirements per format, added to the detected ones, e.g.
|
|
90
|
+
`{'jupyter': ['pyvista[jupyter]']}`. An entry naming a detected project replaces it,
|
|
91
|
+
so `{'python': ['pyvista>=0.46']}` sets its version. An entry without a version keeps
|
|
92
|
+
the automatic pin described below. Only used with `script_metadata` on.
|
|
93
|
+
- `requires_python`: a `requires-python` specifier for the dependency block, e.g.
|
|
94
|
+
`'>=3.10'`. `None` (default) derives one from the dependencies instead; see
|
|
95
|
+
[Dependencies and run commands](#dependencies-and-run-commands). Only used with
|
|
96
|
+
`script_metadata` on.
|
|
97
|
+
|
|
98
|
+
An unrecognized key raises a configuration error at build start.
|
|
99
|
+
|
|
100
|
+
Cross-references and hyperlinks resolve into absolute links using Sphinx's own
|
|
101
|
+
[`html_baseurl`](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_baseurl).
|
|
102
|
+
Leave it unset and no links are generated anywhere.
|
|
103
|
+
|
|
104
|
+
Overriding a single key from the command line works via a dotted `-D` flag, e.g.
|
|
105
|
+
`-D sphinx_examples_as_code_conf.link_position=bottom` -- this only touches that one
|
|
106
|
+
key, leaving the rest (`conf.py`'s values, or the defaults) alone.
|
|
107
|
+
|
|
108
|
+
## Dependencies and run commands
|
|
109
|
+
|
|
110
|
+
With `script_metadata` on, each download lists the third-party projects its code
|
|
111
|
+
imports, plus any `dependencies` set for its format, and says how to run it:
|
|
112
|
+
|
|
113
|
+
- Python scripts start with a [PEP 723](https://peps.python.org/pep-0723/) `# /// script`
|
|
114
|
+
block, followed by `# Run this script with: uv run <name>.py`.
|
|
115
|
+
- Jupyter notebooks say `` Open this notebook with: `uvx --python "<requires-python>" --from jupyterlab --with <dependency> ... jupyter-lab <name>.ipynb` `` in their first cell,
|
|
116
|
+
with `--python` only when there is a `requires-python`. Arguments are double-quoted where
|
|
117
|
+
needed, so the command works in bash, zsh, PowerShell and cmd. An argument containing
|
|
118
|
+
`"`, `$`, a backtick, `\`, `%` or a `!` not followed by `=` is single-quoted instead,
|
|
119
|
+
which cmd does not understand.
|
|
120
|
+
- marimo notebooks start with the same `# /// script` block, which also lists `marimo`,
|
|
121
|
+
followed by `# Open this notebook with: uvx marimo edit --sandbox <name>.marimo.py`.
|
|
122
|
+
- Each run command is followed by a line linking to
|
|
123
|
+
[uv's installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
|
124
|
+
|
|
125
|
+
Imports are read from the code, not run. Each imported top-level module maps to the
|
|
126
|
+
project that provides it in the environment building the docs; standard-library
|
|
127
|
+
modules, relative imports and modules no single installed project provides are left
|
|
128
|
+
out. Projects the code needs but never imports, such as an optional backend, go in
|
|
129
|
+
`dependencies`.
|
|
130
|
+
|
|
131
|
+
The project named by Sphinx's own `project` setting (e.g. `project = 'PyVista'`) gets
|
|
132
|
+
extra treatment when an example imports it and it is installed: its dependency becomes
|
|
133
|
+
`<name>>=X.Y`, the installed release's major and minor version, or plain `<name>` for a
|
|
134
|
+
pre-release or dev build.
|
|
135
|
+
|
|
136
|
+
Unless `requires_python` is set, the block's `requires-python` combines:
|
|
137
|
+
|
|
138
|
+
- that project's own `Requires-Python`, e.g. `>=3.10`, when it is listed;
|
|
139
|
+
- `<3.N`, one minor version above the newest Python named in the `Programming Language ::
|
|
140
|
+
Python :: 3.X` classifiers of that project and of every listed dependency installed
|
|
141
|
+
from a platform-specific wheel, as installed in the environment building the docs.
|
|
142
|
+
Pure-Python dependencies and dependencies without such classifiers don't count, and
|
|
143
|
+
the bound is dropped when it would exclude every Python the project's own
|
|
144
|
+
`Requires-Python` allows.
|
|
145
|
+
|
|
146
|
+
With neither, the line is left out.
|
|
147
|
+
|
|
148
|
+
## Conversion rules
|
|
149
|
+
|
|
150
|
+
What happens to the content of an Examples section:
|
|
151
|
+
|
|
152
|
+
- Doctest blocks (`>>> ...` / `... ...`) keep their input lines, prompts stripped, as
|
|
153
|
+
real Python source. Doctest *output* lines are dropped — only the input code matters.
|
|
154
|
+
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
|
|
155
|
+
comments, set off with blank lines on both sides like any other directive.
|
|
156
|
+
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
|
|
157
|
+
their content as comments, indented one level under the label in Python scripts only.
|
|
158
|
+
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
|
|
159
|
+
line on both sides in either format — a bare `#` instead of a real blank line when
|
|
160
|
+
that falls inside an admonition or a definition's own body in a Python script, so the
|
|
161
|
+
whole thing still reads as one unbroken comment block. A definition list's term stays
|
|
162
|
+
at the surrounding indent; its definition (the body nested under it) is indented one
|
|
163
|
+
level further in Python scripts, same as an admonition's own content.
|
|
164
|
+
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
|
|
165
|
+
double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
|
|
166
|
+
``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
|
|
167
|
+
the reference resolves, notebooks turn it into a clickable link; Python scripts never
|
|
168
|
+
write the link, only the display text.
|
|
169
|
+
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
|
|
170
|
+
backticks.
|
|
171
|
+
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
|
|
172
|
+
`#` comment.
|
|
173
|
+
- A table becomes an aligned text table, its caption on the line above: an RST simple
|
|
174
|
+
table in Python scripts, a Markdown pipe table in notebooks. Each cell is flattened to a
|
|
175
|
+
single line, and a column left entirely empty -- one holding only images, say -- is
|
|
176
|
+
dropped along with its heading.
|
|
177
|
+
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
|
|
178
|
+
line are dropped entirely.
|
|
179
|
+
- "See Also" content is always dropped.
|
|
180
|
+
|
|
181
|
+
Generated Python scripts start with a `# Examples from <qualified name>` title header
|
|
182
|
+
(gallery mode uses the page's own title instead -- see below), with a few whitespace
|
|
183
|
+
conventions: prose directly above a code block stays attached to it, a code block is
|
|
184
|
+
always followed by a blank line, and a directive (header, `# NOTE:`-style block) gets
|
|
185
|
+
blank lines on both sides.
|
|
186
|
+
|
|
187
|
+
Generated Jupyter notebooks use the same content, split into alternating code/markdown
|
|
188
|
+
cells instead.
|
|
189
|
+
|
|
190
|
+
Generated marimo notebooks are that same Jupyter notebook converted by marimo's own
|
|
191
|
+
converter, the one behind `marimo convert`. `from __future__` imports are dropped first,
|
|
192
|
+
so the notebook also runs as a plain script (`python <name>.marimo.py`).
|
|
193
|
+
|
|
194
|
+
A download link is only added if the resulting code contains at least one real
|
|
195
|
+
executable statement.
|
|
196
|
+
|
|
197
|
+
## Sphinx-Gallery integration
|
|
198
|
+
|
|
199
|
+
With `sphinx_examples_as_code_conf['gallery_downloads'] = True`, this extension takes
|
|
200
|
+
over the downloads on every page generated by
|
|
201
|
+
[sphinx-gallery](https://sphinx-gallery.github.io): the "Go to the end to download the
|
|
202
|
+
full example code" note and the `.py`/`.zip` download footer are removed from
|
|
203
|
+
the page, replaced with download link(s) built by this extension instead — same
|
|
204
|
+
conversion rules as above, applied to the whole page rather than one Examples section.
|
|
205
|
+
The generated file's header uses the page's own title (e.g. `# Create Circular Arcs`)
|
|
206
|
+
rather than the generic `# Examples from <docname>`.
|
|
207
|
+
|
|
208
|
+
The "Total running time" line and the "Gallery generated by Sphinx-Gallery" credit line
|
|
209
|
+
stay on the rendered page untouched, but never make it into the generated download
|
|
210
|
+
itself.
|
|
211
|
+
|
|
212
|
+
Detection is automatic and per-page — any page without a sphinx-gallery download footer
|
|
213
|
+
is left completely untouched. Gallery pages and ordinary docstring/prose pages (using
|
|
214
|
+
the Examples-section behavior above) can coexist on the same site.
|
|
215
|
+
|
|
216
|
+
A `# %%` cell with its own RST heading gets the same header treatment as the file's own
|
|
217
|
+
title, and renders as a real Markdown heading in notebooks. Level is relative to actual
|
|
218
|
+
RST section nesting, not always one below the file's own header: a cell heading nested
|
|
219
|
+
under the page's own title (the common case) is one level below it; one that reuses the
|
|
220
|
+
page title's own underline character is level 1, the same as the file's own header; a
|
|
221
|
+
cell heading nested under *that* is level 2 relative to it, and so on.
|
|
222
|
+
|
|
223
|
+
Each format uses one heading style consistently across every level, rather than mixing
|
|
224
|
+
styles:
|
|
225
|
+
|
|
226
|
+
- Python scripts use an RST-style title + underline, one character per level -- the same
|
|
227
|
+
sequence Sphinx's own documentation uses for sections through sub-paragraphs: `=`, `-`,
|
|
228
|
+
`~`, `^`, `"`, `'` for levels 1 through 6.
|
|
229
|
+
- Notebooks always use ATX syntax (`#`, `##`, `###`, ...) at every level.
|
|
230
|
+
|
|
231
|
+
Two things worth knowing before turning this on:
|
|
232
|
+
|
|
233
|
+
- It's built against sphinx-gallery's own doctree output — the `sphx-glr-*` CSS classes
|
|
234
|
+
its own theming depends on, and the private node type holding a highlighted code
|
|
235
|
+
block — not a documented extension API. A future sphinx-gallery release could shift
|
|
236
|
+
that structure without warning. Only sphinx-gallery 0.22.x is tested, so the `gallery`
|
|
237
|
+
extra caps there rather than letting the next release quietly produce no downloads.
|
|
238
|
+
- sphinx-gallery's own `.py`/`.zip` downloads still end up copied into `_downloads/`,
|
|
239
|
+
even though nothing on the page links to them anymore. Its `.ipynb` is never written:
|
|
240
|
+
with `gallery_downloads` on, this extension sets `sphinx_gallery_conf['pypandoc']` to
|
|
241
|
+
`False` and `sphinx_gallery_conf['notebook_extensions']` to an empty set before
|
|
242
|
+
sphinx-gallery reads them, so pandoc is not needed. Setting `pypandoc`,
|
|
243
|
+
`notebook_extensions`, `first_notebook_cell`, `last_notebook_cell` or `notebook_images`
|
|
244
|
+
yourself has no effect and is reported as a warning.
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
uv sync --group dev --extra gallery
|
|
250
|
+
uv run pytest
|
|
251
|
+
uv run pre-commit run --all-files
|
|
252
|
+
```
|
|
@@ -4,6 +4,7 @@ requires = ['setuptools>=64', 'setuptools_scm>=8']
|
|
|
4
4
|
|
|
5
5
|
[dependency-groups]
|
|
6
6
|
dev = [
|
|
7
|
+
'marimo>=0.14',
|
|
7
8
|
'nbformat',
|
|
8
9
|
'numpydoc',
|
|
9
10
|
'pre-commit',
|
|
@@ -31,7 +32,7 @@ classifiers = [
|
|
|
31
32
|
'Programming Language :: Python :: 3.14',
|
|
32
33
|
'Topic :: Scientific/Engineering :: Visualization',
|
|
33
34
|
]
|
|
34
|
-
dependencies = ['pyvista>=0.48']
|
|
35
|
+
dependencies = ['packaging>=22', 'pyvista>=0.48']
|
|
35
36
|
description = 'Sphinx extension for converting docstring examples into downloadable code.'
|
|
36
37
|
dynamic = ['version']
|
|
37
38
|
keywords = ['download', 'examples', 'sphinx']
|
|
@@ -42,6 +43,7 @@ requires-python = '>=3.10'
|
|
|
42
43
|
|
|
43
44
|
[project.optional-dependencies]
|
|
44
45
|
gallery = ['sphinx-gallery>=0.22,<0.23']
|
|
46
|
+
marimo = ['marimo>=0.14']
|
|
45
47
|
|
|
46
48
|
[project.urls]
|
|
47
49
|
Homepage = 'https://github.com/pyvista/sphinx-examples-as-code'
|