sphinx-examples-as-code 0.1.0__tar.gz → 0.3.2__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.
Files changed (46) hide show
  1. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/.github/workflows/ci.yml +1 -1
  2. sphinx_examples_as_code-0.3.2/LICENSE +21 -0
  3. sphinx_examples_as_code-0.3.2/PKG-INFO +187 -0
  4. sphinx_examples_as_code-0.3.2/README.md +160 -0
  5. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/pyproject.toml +6 -0
  6. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code/__init__.py +498 -65
  7. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code/_version.py +3 -3
  8. sphinx_examples_as_code-0.3.2/sphinx_examples_as_code.egg-info/PKG-INFO +187 -0
  9. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code.egg-info/SOURCES.txt +16 -0
  10. sphinx_examples_as_code-0.3.2/sphinx_examples_as_code.egg-info/scm_file_list.json +34 -0
  11. sphinx_examples_as_code-0.3.2/sphinx_examples_as_code.egg-info/scm_version.json +8 -0
  12. sphinx_examples_as_code-0.3.2/tests/gallery_fixture/conf.py +24 -0
  13. sphinx_examples_as_code-0.3.2/tests/gallery_fixture/examples/GALLERY_HEADER.rst +2 -0
  14. sphinx_examples_as_code-0.3.2/tests/gallery_fixture/examples/plot_minimal.py +28 -0
  15. sphinx_examples_as_code-0.3.2/tests/gallery_fixture/index.rst +7 -0
  16. sphinx_examples_as_code-0.3.2/tests/heading_level_fixture/conf.py +23 -0
  17. sphinx_examples_as_code-0.3.2/tests/heading_level_fixture/examples/GALLERY_HEADER.rst +2 -0
  18. sphinx_examples_as_code-0.3.2/tests/heading_level_fixture/examples/plot_reused_level.py +19 -0
  19. sphinx_examples_as_code-0.3.2/tests/heading_level_fixture/examples/plot_three_levels.py +19 -0
  20. sphinx_examples_as_code-0.3.2/tests/heading_level_fixture/index.rst +7 -0
  21. sphinx_examples_as_code-0.3.2/tests/single_function_fixture/conf.py +84 -0
  22. sphinx_examples_as_code-0.3.2/tests/single_function_fixture/index.rst +4 -0
  23. sphinx_examples_as_code-0.3.2/tests/single_function_fixture/mymodule.py +13 -0
  24. sphinx_examples_as_code-0.3.2/tests/test_gallery_downloads.py +263 -0
  25. sphinx_examples_as_code-0.3.2/tests/test_heading_level_reuse.py +90 -0
  26. sphinx_examples_as_code-0.3.2/tests/test_single_function_page.py +40 -0
  27. sphinx_examples_as_code-0.3.2/tests/test_sphinx_examples_as_code.py +2160 -0
  28. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/tests/test_tinypages.py +182 -30
  29. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/tests/tinypages/docstring_cases.py +1 -1
  30. sphinx_examples_as_code-0.1.0/PKG-INFO +0 -106
  31. sphinx_examples_as_code-0.1.0/README.md +0 -81
  32. sphinx_examples_as_code-0.1.0/sphinx_examples_as_code.egg-info/PKG-INFO +0 -106
  33. sphinx_examples_as_code-0.1.0/sphinx_examples_as_code.egg-info/scm_file_list.json +0 -18
  34. sphinx_examples_as_code-0.1.0/sphinx_examples_as_code.egg-info/scm_version.json +0 -8
  35. sphinx_examples_as_code-0.1.0/tests/test_sphinx_examples_as_code.py +0 -1058
  36. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/.github/dependabot.yml +0 -0
  37. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/.github/release.yml +0 -0
  38. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/.gitignore +0 -0
  39. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/.pre-commit-config.yaml +0 -0
  40. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/setup.cfg +0 -0
  41. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code.egg-info/dependency_links.txt +0 -0
  42. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code.egg-info/requires.txt +0 -0
  43. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/sphinx_examples_as_code.egg-info/top_level.txt +0 -0
  44. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/tests/tinypages/conf.py +0 -0
  45. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/tests/tinypages/docstring_cases.rst +0 -0
  46. {sphinx_examples_as_code-0.1.0 → sphinx_examples_as_code-0.3.2}/tests/tinypages/index.rst +0 -0
@@ -86,4 +86,4 @@ jobs:
86
86
  with:
87
87
  name: dist
88
88
  path: dist/
89
- - uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # release/v1
89
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2026 The PyVista Developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to
7
+ deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ sell copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: sphinx-examples-as-code
3
+ Version: 0.3.2
4
+ Summary: Sphinx extension for converting docstring examples into downloadable code.
5
+ Author-email: The PyVista Developers <info@pyvista.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pyvista/sphinx-examples-as-code
8
+ Keywords: download,examples,sphinx
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: Sphinx :: Extension
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering :: Visualization
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pyvista>=0.48
26
+ Dynamic: license-file
27
+
28
+ # sphinx-examples-as-code
29
+
30
+ A Sphinx extension that turns docstring/page "Examples" sections into downloadable,
31
+ runnable `.py` and/or `.ipynb` files, with a download link inserted into the section.
32
+
33
+ Pages or docstrings without an Examples section are left completely untouched. Adding
34
+ `sphinx_examples_as_code` to `conf.py`'s `extensions` is the only on/off switch.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install sphinx-examples-as-code
40
+ ```
41
+
42
+ Add it to your Sphinx `conf.py`:
43
+
44
+ ```python
45
+ extensions = [
46
+ ...,
47
+ 'sphinx_examples_as_code',
48
+ ]
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ Everything lives in one dict in `conf.py`, `sphinx_examples_as_code_conf` -- set only
54
+ the keys you want to change from their default:
55
+
56
+ ```python
57
+ sphinx_examples_as_code_conf = {
58
+ 'link_position': 'top',
59
+ 'formats': ['py', 'ipynb'],
60
+ 'gallery_downloads': False,
61
+ 'footer': (
62
+ 'Generated by `sphinx-examples-as-code '
63
+ '<https://github.com/pyvista/sphinx-examples-as-code>`_'
64
+ ),
65
+ 'link_labels': {
66
+ 'py': 'Download Python source code',
67
+ 'ipynb': 'Download Jupyter notebook',
68
+ },
69
+ }
70
+ ```
71
+
72
+ - `link_position`: where the download link(s) land within the Examples section. `'top'`
73
+ (default) or `'bottom'`.
74
+ - `formats`: which downloads to generate. A list containing `'py'`, `'ipynb'`, or both
75
+ (default). Always offered in that order regardless of how the list is written.
76
+ - `link_labels`: the text of the download link(s) themselves, per format. Set only the
77
+ format(s) you want to change; any left unset keep reading their own default shown above.
78
+ - `gallery_downloads`: opt-in takeover of
79
+ [sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
80
+ `False` (default) leaves sphinx-gallery pages untouched. See
81
+ [Sphinx-Gallery integration](#sphinx-gallery-integration) below.
82
+ - `footer`: a string appended to the end of every generated file. Defaults to a one-line
83
+ "generated file" credit linking back to this project; set to `None` to omit it
84
+ entirely. Preceded by a blank line and a `-`-only divider line, which also renders as a
85
+ real horizontal rule in `.ipynb`; the footer always gets its own dedicated cell there.
86
+ Parsed as RST: a hyperlink written as `` `text <url>`_ `` becomes a real clickable
87
+ Markdown link in `.ipynb`, and renders inline as `text url` in `.py`. Plain text with no
88
+ markup at all becomes one comment line per line of text, and blank-line-separated
89
+ paragraphs stay separated.
90
+
91
+ An unrecognized key raises a configuration error at build start.
92
+
93
+ Cross-references and hyperlinks resolve into absolute links using Sphinx's own
94
+ [`html_baseurl`](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_baseurl).
95
+ Leave it unset and no links are generated anywhere.
96
+
97
+ Overriding a single key from the command line works via a dotted `-D` flag, e.g.
98
+ `-D sphinx_examples_as_code_conf.link_position=bottom` -- this only touches that one
99
+ key, leaving the rest (`conf.py`'s values, or the defaults) alone.
100
+
101
+ ## Conversion rules
102
+
103
+ What happens to the content of an Examples section:
104
+
105
+ - Doctest blocks (`>>> ...` / `... ...`) keep their input lines, prompts stripped, as
106
+ real Python source. Doctest *output* lines are dropped — only the input code matters.
107
+ - `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
108
+ comments, set off with blank lines on both sides like any other directive.
109
+ - Admonitions (`.. note::`, `.. warning::`, `.. seealso::`, ...) become a `# LABEL:`
110
+ comment followed by their content as comments. "See Also" is recognized in any of its
111
+ three forms (`.. seealso::`, a bare `.. rubric:: See Also`, or a hand-written `See
112
+ Also` heading) and always renders the same way.
113
+ - Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
114
+ double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
115
+ ``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
116
+ the reference resolves: `.ipynb` turns it into a clickable link everywhere; `.py` only
117
+ writes the link inside a "See Also" part (as `name url` on its own line) — everywhere
118
+ else in `.py` the link is simply omitted.
119
+ - Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
120
+ backticks.
121
+ - Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
122
+ `#` comment.
123
+ - Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
124
+ line are dropped entirely.
125
+
126
+ Generated `.py` files start with a `# Examples from <qualified name>` title header
127
+ (gallery mode uses the page's own title instead -- see below), with a few whitespace
128
+ conventions: prose directly above a code block stays attached to it, a code block is
129
+ always followed by a blank line, and a directive (header, `# NOTE:`-style block) gets
130
+ blank lines on both sides.
131
+
132
+ Generated `.ipynb` notebooks use the same content, split into alternating code/markdown
133
+ cells instead.
134
+
135
+ A download link is only added if the resulting code contains at least one real
136
+ executable statement.
137
+
138
+ ## Sphinx-Gallery integration
139
+
140
+ With `sphinx_examples_as_code_conf['gallery_downloads'] = True`, this extension takes
141
+ over the downloads on every page generated by
142
+ [sphinx-gallery](https://sphinx-gallery.github.io): the "Go to the end to download the
143
+ full example code" note and the `.py`/`.ipynb`/`.zip` download footer are removed from
144
+ the page, replaced with download link(s) built by this extension instead — same
145
+ conversion rules as above, applied to the whole page rather than one Examples section.
146
+ The generated file's header uses the page's own title (e.g. `# Create Circular Arcs`)
147
+ rather than the generic `# Examples from <docname>`.
148
+
149
+ The "Total running time" line and the "Gallery generated by Sphinx-Gallery" credit line
150
+ stay on the rendered page untouched, but never make it into the generated download
151
+ itself.
152
+
153
+ Detection is automatic and per-page — any page without a sphinx-gallery download footer
154
+ is left completely untouched. Gallery pages and ordinary docstring/prose pages (using
155
+ the Examples-section behavior above) can coexist on the same site.
156
+
157
+ A `# %%` cell with its own RST heading gets the same header treatment as the file's own
158
+ title, and renders as a real Markdown heading in `.ipynb`. Level is relative to actual
159
+ RST section nesting, not always one below the file's own header: a cell heading nested
160
+ under the page's own title (the common case) is one level below it; one that reuses the
161
+ page title's own underline character is level 1, the same as the file's own header; a
162
+ cell heading nested under *that* is level 2 relative to it, and so on.
163
+
164
+ Each format uses one heading style consistently across every level, rather than mixing
165
+ styles:
166
+
167
+ - `.py` uses an RST-style title + underline, one character per level -- the same
168
+ sequence Sphinx's own documentation uses for sections through sub-paragraphs: `=`, `-`,
169
+ `~`, `^`, `"`, `'` for levels 1 through 6.
170
+ - `.ipynb` always uses ATX syntax (`#`, `##`, `###`, ...), since that's a real Markdown
171
+ heading at any level, whereas an underline only reads as one for the first two.
172
+
173
+ Two things worth knowing before turning this on:
174
+
175
+ - It's built against sphinx-gallery's own RST/HTML output (the same `sphx-glr-*` CSS
176
+ classes its own theming depends on), not a documented extension API. A future
177
+ sphinx-gallery release could shift that structure without warning.
178
+ - sphinx-gallery's own `.py`/`.ipynb`/`.zip` downloads still end up copied into
179
+ `_downloads/`, even though nothing on the page links to them anymore.
180
+
181
+ ## Development
182
+
183
+ ```bash
184
+ uv sync --group dev
185
+ uv run pytest
186
+ uv run pre-commit run --all-files
187
+ ```
@@ -0,0 +1,160 @@
1
+ # sphinx-examples-as-code
2
+
3
+ A Sphinx extension that turns docstring/page "Examples" sections into downloadable,
4
+ runnable `.py` and/or `.ipynb` files, with a download link inserted into the section.
5
+
6
+ Pages or docstrings without an Examples section are left completely untouched. Adding
7
+ `sphinx_examples_as_code` to `conf.py`'s `extensions` is the only on/off switch.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install sphinx-examples-as-code
13
+ ```
14
+
15
+ Add it to your Sphinx `conf.py`:
16
+
17
+ ```python
18
+ extensions = [
19
+ ...,
20
+ 'sphinx_examples_as_code',
21
+ ]
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ Everything lives in one dict in `conf.py`, `sphinx_examples_as_code_conf` -- set only
27
+ the keys you want to change from their default:
28
+
29
+ ```python
30
+ sphinx_examples_as_code_conf = {
31
+ 'link_position': 'top',
32
+ 'formats': ['py', 'ipynb'],
33
+ 'gallery_downloads': False,
34
+ 'footer': (
35
+ 'Generated by `sphinx-examples-as-code '
36
+ '<https://github.com/pyvista/sphinx-examples-as-code>`_'
37
+ ),
38
+ 'link_labels': {
39
+ 'py': 'Download Python source code',
40
+ 'ipynb': 'Download Jupyter notebook',
41
+ },
42
+ }
43
+ ```
44
+
45
+ - `link_position`: where the download link(s) land within the Examples section. `'top'`
46
+ (default) or `'bottom'`.
47
+ - `formats`: which downloads to generate. A list containing `'py'`, `'ipynb'`, or both
48
+ (default). Always offered in that order regardless of how the list is written.
49
+ - `link_labels`: the text of the download link(s) themselves, per format. Set only the
50
+ format(s) you want to change; any left unset keep reading their own default shown above.
51
+ - `gallery_downloads`: opt-in takeover of
52
+ [sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
53
+ `False` (default) leaves sphinx-gallery pages untouched. See
54
+ [Sphinx-Gallery integration](#sphinx-gallery-integration) below.
55
+ - `footer`: a string appended to the end of every generated file. Defaults to a one-line
56
+ "generated file" credit linking back to this project; set to `None` to omit it
57
+ entirely. Preceded by a blank line and a `-`-only divider line, which also renders as a
58
+ real horizontal rule in `.ipynb`; the footer always gets its own dedicated cell there.
59
+ Parsed as RST: a hyperlink written as `` `text <url>`_ `` becomes a real clickable
60
+ Markdown link in `.ipynb`, and renders inline as `text url` in `.py`. Plain text with no
61
+ markup at all becomes one comment line per line of text, and blank-line-separated
62
+ paragraphs stay separated.
63
+
64
+ An unrecognized key raises a configuration error at build start.
65
+
66
+ Cross-references and hyperlinks resolve into absolute links using Sphinx's own
67
+ [`html_baseurl`](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_baseurl).
68
+ Leave it unset and no links are generated anywhere.
69
+
70
+ Overriding a single key from the command line works via a dotted `-D` flag, e.g.
71
+ `-D sphinx_examples_as_code_conf.link_position=bottom` -- this only touches that one
72
+ key, leaving the rest (`conf.py`'s values, or the defaults) alone.
73
+
74
+ ## Conversion rules
75
+
76
+ What happens to the content of an Examples section:
77
+
78
+ - Doctest blocks (`>>> ...` / `... ...`) keep their input lines, prompts stripped, as
79
+ real Python source. Doctest *output* lines are dropped — only the input code matters.
80
+ - `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
81
+ comments, set off with blank lines on both sides like any other directive.
82
+ - Admonitions (`.. note::`, `.. warning::`, `.. seealso::`, ...) become a `# LABEL:`
83
+ comment followed by their content as comments. "See Also" is recognized in any of its
84
+ three forms (`.. seealso::`, a bare `.. rubric:: See Also`, or a hand-written `See
85
+ Also` heading) and always renders the same way.
86
+ - Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
87
+ double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
88
+ ``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
89
+ the reference resolves: `.ipynb` turns it into a clickable link everywhere; `.py` only
90
+ writes the link inside a "See Also" part (as `name url` on its own line) — everywhere
91
+ else in `.py` the link is simply omitted.
92
+ - Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
93
+ backticks.
94
+ - Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
95
+ `#` comment.
96
+ - Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
97
+ line are dropped entirely.
98
+
99
+ Generated `.py` files start with a `# Examples from <qualified name>` title header
100
+ (gallery mode uses the page's own title instead -- see below), with a few whitespace
101
+ conventions: prose directly above a code block stays attached to it, a code block is
102
+ always followed by a blank line, and a directive (header, `# NOTE:`-style block) gets
103
+ blank lines on both sides.
104
+
105
+ Generated `.ipynb` notebooks use the same content, split into alternating code/markdown
106
+ cells instead.
107
+
108
+ A download link is only added if the resulting code contains at least one real
109
+ executable statement.
110
+
111
+ ## Sphinx-Gallery integration
112
+
113
+ With `sphinx_examples_as_code_conf['gallery_downloads'] = True`, this extension takes
114
+ over the downloads on every page generated by
115
+ [sphinx-gallery](https://sphinx-gallery.github.io): the "Go to the end to download the
116
+ full example code" note and the `.py`/`.ipynb`/`.zip` download footer are removed from
117
+ the page, replaced with download link(s) built by this extension instead — same
118
+ conversion rules as above, applied to the whole page rather than one Examples section.
119
+ The generated file's header uses the page's own title (e.g. `# Create Circular Arcs`)
120
+ rather than the generic `# Examples from <docname>`.
121
+
122
+ The "Total running time" line and the "Gallery generated by Sphinx-Gallery" credit line
123
+ stay on the rendered page untouched, but never make it into the generated download
124
+ itself.
125
+
126
+ Detection is automatic and per-page — any page without a sphinx-gallery download footer
127
+ is left completely untouched. Gallery pages and ordinary docstring/prose pages (using
128
+ the Examples-section behavior above) can coexist on the same site.
129
+
130
+ A `# %%` cell with its own RST heading gets the same header treatment as the file's own
131
+ title, and renders as a real Markdown heading in `.ipynb`. Level is relative to actual
132
+ RST section nesting, not always one below the file's own header: a cell heading nested
133
+ under the page's own title (the common case) is one level below it; one that reuses the
134
+ page title's own underline character is level 1, the same as the file's own header; a
135
+ cell heading nested under *that* is level 2 relative to it, and so on.
136
+
137
+ Each format uses one heading style consistently across every level, rather than mixing
138
+ styles:
139
+
140
+ - `.py` uses an RST-style title + underline, one character per level -- the same
141
+ sequence Sphinx's own documentation uses for sections through sub-paragraphs: `=`, `-`,
142
+ `~`, `^`, `"`, `'` for levels 1 through 6.
143
+ - `.ipynb` always uses ATX syntax (`#`, `##`, `###`, ...), since that's a real Markdown
144
+ heading at any level, whereas an underline only reads as one for the first two.
145
+
146
+ Two things worth knowing before turning this on:
147
+
148
+ - It's built against sphinx-gallery's own RST/HTML output (the same `sphx-glr-*` CSS
149
+ classes its own theming depends on), not a documented extension API. A future
150
+ sphinx-gallery release could shift that structure without warning.
151
+ - sphinx-gallery's own `.py`/`.ipynb`/`.zip` downloads still end up copied into
152
+ `_downloads/`, even though nothing on the page links to them anymore.
153
+
154
+ ## Development
155
+
156
+ ```bash
157
+ uv sync --group dev
158
+ uv run pytest
159
+ uv run pre-commit run --all-files
160
+ ```
@@ -11,6 +11,7 @@ dev = [
11
11
  'pytest-cov',
12
12
  'sphinx',
13
13
  'sphinx-design',
14
+ 'sphinx-gallery>=0.21.0',
14
15
  'trame-pyvista',
15
16
  ]
16
17
 
@@ -92,6 +93,10 @@ ignore = ['D203', 'D211', 'D213']
92
93
  [tool.ruff.lint.per-file-ignores]
93
94
  '*/conf.py' = ['E402']
94
95
  'tests/*' = ['D103']
96
+ # sphinx-gallery example scripts: the module docstring is an RST page title
97
+ # (title line + underline), not prose -- pydocstyle's summary-line rules don't apply.
98
+ 'tests/gallery_fixture/examples/*' = ['D205', 'D400', 'D415', 'T201']
99
+ 'tests/heading_level_fixture/examples/*' = ['D205', 'D400', 'D415', 'T201']
95
100
 
96
101
  [tool.ruff.lint.isort]
97
102
  combine-as-imports = true
@@ -100,6 +105,7 @@ force-sort-within-sections = true
100
105
  required-imports = ['from __future__ import annotations']
101
106
 
102
107
  [tool.setuptools]
108
+ license-files = ['LICENSE']
103
109
  packages = ['sphinx_examples_as_code']
104
110
 
105
111
  [tool.setuptools_scm]