sphinx-examples-as-code 0.4.0__tar.gz → 0.5.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.4.0 → sphinx_examples_as_code-0.5.0}/PKG-INFO +11 -20
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/README.md +10 -19
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code/__init__.py +50 -88
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code/_version.py +3 -3
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/PKG-INFO +11 -20
- sphinx_examples_as_code-0.5.0/sphinx_examples_as_code.egg-info/scm_version.json +8 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_single_function_page.py +0 -34
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_sphinx_examples_as_code.py +97 -284
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_tinypages.py +23 -107
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/tinypages/docstring_cases.py +4 -6
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/tinypages/docstring_cases.rst +3 -3
- sphinx_examples_as_code-0.4.0/sphinx_examples_as_code.egg-info/scm_version.json +0 -8
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/.github/dependabot.yml +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/.github/release.yml +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/.github/workflows/ci.yml +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/.gitignore +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/.pre-commit-config.yaml +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/LICENSE +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/pyproject.toml +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/setup.cfg +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/SOURCES.txt +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/dependency_links.txt +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/requires.txt +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/scm_file_list.json +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code.egg-info/top_level.txt +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/gallery_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/gallery_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/gallery_fixture/examples/plot_minimal.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/gallery_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/heading_level_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/heading_level_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/heading_level_fixture/examples/plot_reused_level.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/heading_level_fixture/examples/plot_three_levels.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/heading_level_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/single_function_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/single_function_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/single_function_fixture/mymodule.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_gallery_downloads.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_heading_level_reuse.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/tinypages/conf.py +0 -0
- {sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/tinypages/index.rst +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-examples-as-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -66,7 +66,6 @@ sphinx_examples_as_code_conf = {
|
|
|
66
66
|
'py': 'Download Python source code',
|
|
67
67
|
'ipynb': 'Download Jupyter notebook',
|
|
68
68
|
},
|
|
69
|
-
'include_see_also': True,
|
|
70
69
|
}
|
|
71
70
|
```
|
|
72
71
|
|
|
@@ -76,13 +75,6 @@ sphinx_examples_as_code_conf = {
|
|
|
76
75
|
(default). Always offered in that order regardless of how the list is written.
|
|
77
76
|
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
|
|
78
77
|
format(s) you want to change; any left unset keep reading their own default shown above.
|
|
79
|
-
- `include_see_also`: whether "See Also" content is included in the generated file.
|
|
80
|
-
`True` (default) includes it, in any of its forms: a `.. seealso::` admonition, a bare
|
|
81
|
-
`.. rubric:: See Also`, a hand-written `See Also` heading, or numpydoc's own "See Also"
|
|
82
|
-
field (including when it's been reordered outside the Examples section itself, or the
|
|
83
|
-
Examples section has been hoisted to a heading of its own — a setup some projects use to
|
|
84
|
-
get "Examples" listed in the page's own navigation). `False` excludes "See Also" content
|
|
85
|
-
in every one of those forms.
|
|
86
78
|
- `gallery_downloads`: opt-in takeover of
|
|
87
79
|
[sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
|
|
88
80
|
`False` (default) leaves sphinx-gallery pages untouched. See
|
|
@@ -114,27 +106,26 @@ What happens to the content of an Examples section:
|
|
|
114
106
|
real Python source. Doctest *output* lines are dropped — only the input code matters.
|
|
115
107
|
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
|
|
116
108
|
comments, set off with blank lines on both sides like any other directive.
|
|
117
|
-
- Admonitions (`.. note::`, `.. warning::`,
|
|
118
|
-
|
|
119
|
-
`.py` only. "See Also" is recognized in any of its three forms (`.. seealso::`, a bare
|
|
120
|
-
`.. rubric:: See Also`, or a hand-written `See Also` heading) and always renders the
|
|
121
|
-
same way.
|
|
109
|
+
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
|
|
110
|
+
their content as comments, indented one level under the label in `.py` only.
|
|
122
111
|
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
|
|
123
|
-
line on both sides in either format
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
line on both sides in either format — a bare `#` instead of a real blank line when
|
|
113
|
+
that falls inside an admonition or a definition's own body in `.py`, so the whole
|
|
114
|
+
thing still reads as one unbroken comment block. A definition list's term stays at
|
|
115
|
+
the surrounding indent; its definition (the body nested under it) is indented one
|
|
116
|
+
level further in `.py`, same as an admonition's own content.
|
|
126
117
|
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
|
|
127
118
|
double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
|
|
128
119
|
``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
|
|
129
|
-
the reference resolves
|
|
130
|
-
|
|
131
|
-
else in `.py` the link is simply omitted.
|
|
120
|
+
the reference resolves, `.ipynb` turns it into a clickable link; `.py` never writes the
|
|
121
|
+
link, only the display text.
|
|
132
122
|
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
|
|
133
123
|
backticks.
|
|
134
124
|
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
|
|
135
125
|
`#` comment.
|
|
136
126
|
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
|
|
137
127
|
line are dropped entirely.
|
|
128
|
+
- "See Also" content is always dropped.
|
|
138
129
|
|
|
139
130
|
Generated `.py` files start with a `# Examples from <qualified name>` title header
|
|
140
131
|
(gallery mode uses the page's own title instead -- see below), with a few whitespace
|
|
@@ -39,7 +39,6 @@ sphinx_examples_as_code_conf = {
|
|
|
39
39
|
'py': 'Download Python source code',
|
|
40
40
|
'ipynb': 'Download Jupyter notebook',
|
|
41
41
|
},
|
|
42
|
-
'include_see_also': True,
|
|
43
42
|
}
|
|
44
43
|
```
|
|
45
44
|
|
|
@@ -49,13 +48,6 @@ sphinx_examples_as_code_conf = {
|
|
|
49
48
|
(default). Always offered in that order regardless of how the list is written.
|
|
50
49
|
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
|
|
51
50
|
format(s) you want to change; any left unset keep reading their own default shown above.
|
|
52
|
-
- `include_see_also`: whether "See Also" content is included in the generated file.
|
|
53
|
-
`True` (default) includes it, in any of its forms: a `.. seealso::` admonition, a bare
|
|
54
|
-
`.. rubric:: See Also`, a hand-written `See Also` heading, or numpydoc's own "See Also"
|
|
55
|
-
field (including when it's been reordered outside the Examples section itself, or the
|
|
56
|
-
Examples section has been hoisted to a heading of its own — a setup some projects use to
|
|
57
|
-
get "Examples" listed in the page's own navigation). `False` excludes "See Also" content
|
|
58
|
-
in every one of those forms.
|
|
59
51
|
- `gallery_downloads`: opt-in takeover of
|
|
60
52
|
[sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
|
|
61
53
|
`False` (default) leaves sphinx-gallery pages untouched. See
|
|
@@ -87,27 +79,26 @@ What happens to the content of an Examples section:
|
|
|
87
79
|
real Python source. Doctest *output* lines are dropped — only the input code matters.
|
|
88
80
|
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
|
|
89
81
|
comments, set off with blank lines on both sides like any other directive.
|
|
90
|
-
- Admonitions (`.. note::`, `.. warning::`,
|
|
91
|
-
|
|
92
|
-
`.py` only. "See Also" is recognized in any of its three forms (`.. seealso::`, a bare
|
|
93
|
-
`.. rubric:: See Also`, or a hand-written `See Also` heading) and always renders the
|
|
94
|
-
same way.
|
|
82
|
+
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
|
|
83
|
+
their content as comments, indented one level under the label in `.py` only.
|
|
95
84
|
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
|
|
96
|
-
line on both sides in either format
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
line on both sides in either format — a bare `#` instead of a real blank line when
|
|
86
|
+
that falls inside an admonition or a definition's own body in `.py`, so the whole
|
|
87
|
+
thing still reads as one unbroken comment block. A definition list's term stays at
|
|
88
|
+
the surrounding indent; its definition (the body nested under it) is indented one
|
|
89
|
+
level further in `.py`, same as an admonition's own content.
|
|
99
90
|
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
|
|
100
91
|
double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
|
|
101
92
|
``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
|
|
102
|
-
the reference resolves
|
|
103
|
-
|
|
104
|
-
else in `.py` the link is simply omitted.
|
|
93
|
+
the reference resolves, `.ipynb` turns it into a clickable link; `.py` never writes the
|
|
94
|
+
link, only the display text.
|
|
105
95
|
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
|
|
106
96
|
backticks.
|
|
107
97
|
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
|
|
108
98
|
`#` comment.
|
|
109
99
|
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
|
|
110
100
|
line are dropped entirely.
|
|
101
|
+
- "See Also" content is always dropped.
|
|
111
102
|
|
|
112
103
|
Generated `.py` files start with a `# Examples from <qualified name>` title header
|
|
113
104
|
(gallery mode uses the page's own title instead -- see below), with a few whitespace
|
{sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code/__init__.py
RENAMED
|
@@ -7,7 +7,6 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
import ast
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from dataclasses import replace
|
|
11
10
|
import hashlib
|
|
12
11
|
import json
|
|
13
12
|
from pathlib import Path
|
|
@@ -154,9 +153,7 @@ class _RenderContext:
|
|
|
154
153
|
app: Sphinx
|
|
155
154
|
docname: str
|
|
156
155
|
fmt: str # 'py' or 'ipynb'
|
|
157
|
-
in_see_also: bool = False
|
|
158
156
|
in_footer: bool = False
|
|
159
|
-
show_see_also: bool = True
|
|
160
157
|
|
|
161
158
|
|
|
162
159
|
def _normalized_html_baseurl(app: Sphinx) -> str | None:
|
|
@@ -233,9 +230,8 @@ def _render_reference(node: nodes.reference, ctx: _RenderContext) -> str:
|
|
|
233
230
|
"""Render a resolved or unresolved cross-reference/hyperlink.
|
|
234
231
|
|
|
235
232
|
The target URL, if resolved, is used as a clickable markdown link in
|
|
236
|
-
notebooks (always), and as literal URL text in ``.py`` (only within
|
|
237
|
-
|
|
238
|
-
omitted).
|
|
233
|
+
notebooks (always), and as literal URL text in ``.py`` (only within the
|
|
234
|
+
footer -- elsewhere in ``.py`` the link is simply omitted).
|
|
239
235
|
"""
|
|
240
236
|
display = node.astext()
|
|
241
237
|
is_code = any(isinstance(child, nodes.literal) for child in node.children)
|
|
@@ -247,11 +243,6 @@ def _render_reference(node: nodes.reference, ctx: _RenderContext) -> str:
|
|
|
247
243
|
if ctx.fmt == 'ipynb':
|
|
248
244
|
return f'[`{display}`]({url})' if is_code else f'[{display}]({url})'
|
|
249
245
|
|
|
250
|
-
if ctx.in_see_also:
|
|
251
|
-
# Surrounding newlines set this off on its own line; _add_comment
|
|
252
|
-
# splits on them like any other multi-line text.
|
|
253
|
-
return f'\n{display} {url}\n'
|
|
254
|
-
|
|
255
246
|
if ctx.in_footer:
|
|
256
247
|
# Inline, not surrounded by newlines -- "text url" in place, not a
|
|
257
248
|
# markdown link.
|
|
@@ -403,6 +394,34 @@ def _indent_comment_line(line: str) -> str:
|
|
|
403
394
|
return f'# {_ADMONITION_CONTENT_INDENT}{line.removeprefix("# ")}'
|
|
404
395
|
|
|
405
396
|
|
|
397
|
+
def _is_raw_code_line(line: str) -> bool:
|
|
398
|
+
"""Check whether a rendered ``.py`` line is real code, not a ``#`` comment line."""
|
|
399
|
+
return bool(line) and not line.startswith('#')
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _fill_comment_gaps(lines: list[str]) -> list[str]:
|
|
403
|
+
"""Turn a blank separator line into a bare ``#`` unless it borders raw code.
|
|
404
|
+
|
|
405
|
+
``_join_segments`` forces a blank line around some segment boundaries
|
|
406
|
+
(e.g. a paragraph followed by a nested list) meant to visually separate
|
|
407
|
+
unrelated top-level content. Folded into one ``# LABEL:`` comment block
|
|
408
|
+
instead (see ``_indent_label_content``/``_definition_segment``), that
|
|
409
|
+
same blank line needs a ``#`` to still read as part of that one block.
|
|
410
|
+
A blank line bordering a raw code line (left flush and uncommented --
|
|
411
|
+
see ``_convert_node``'s doctest/literal-block handling) stays blank,
|
|
412
|
+
since that boundary really is between code and comment.
|
|
413
|
+
"""
|
|
414
|
+
result: list[str] = []
|
|
415
|
+
for i, line in enumerate(lines):
|
|
416
|
+
if line != '':
|
|
417
|
+
result.append(line)
|
|
418
|
+
continue
|
|
419
|
+
before = lines[i - 1] if i > 0 else ''
|
|
420
|
+
after = lines[i + 1] if i + 1 < len(lines) else ''
|
|
421
|
+
result.append(line if _is_raw_code_line(before) or _is_raw_code_line(after) else '#')
|
|
422
|
+
return result
|
|
423
|
+
|
|
424
|
+
|
|
406
425
|
def _indent_label_content(lines: list[str], fmt: str) -> list[str]:
|
|
407
426
|
"""Indent every line after the first (a ``# LABEL:`` line) under it.
|
|
408
427
|
|
|
@@ -410,7 +429,8 @@ def _indent_label_content(lines: list[str], fmt: str) -> list[str]:
|
|
|
410
429
|
"""
|
|
411
430
|
if fmt != 'py' or not lines:
|
|
412
431
|
return lines
|
|
413
|
-
|
|
432
|
+
label, *rest = _fill_comment_gaps(lines)
|
|
433
|
+
return [label, *(_indent_comment_line(line) for line in rest)]
|
|
414
434
|
|
|
415
435
|
|
|
416
436
|
def _definition_segment(node: nodes.definition, ctx: _RenderContext) -> list[Segment]:
|
|
@@ -423,7 +443,7 @@ def _definition_segment(node: nodes.definition, ctx: _RenderContext) -> list[Seg
|
|
|
423
443
|
segments.extend(_convert_node(child, ctx))
|
|
424
444
|
lines = _join_segments(segments)
|
|
425
445
|
if ctx.fmt == 'py':
|
|
426
|
-
lines = [_indent_comment_line(line) for line in lines]
|
|
446
|
+
lines = [_indent_comment_line(line) for line in _fill_comment_gaps(lines)]
|
|
427
447
|
return [('text', lines)] if lines else []
|
|
428
448
|
|
|
429
449
|
|
|
@@ -458,12 +478,11 @@ def _convert_admonition(
|
|
|
458
478
|
node: nodes.Element, label: str, ctx: _RenderContext, *, skip_first_title: bool = False
|
|
459
479
|
) -> list[Segment]:
|
|
460
480
|
"""Convert an admonition-like container to a ``# LABEL:`` directive segment."""
|
|
461
|
-
inner_ctx = replace(ctx, in_see_also=True) if label.upper() == 'SEE ALSO' else ctx
|
|
462
481
|
inner: list[Segment] = [('text', [f'# {label}:'])]
|
|
463
482
|
for child in node.children:
|
|
464
483
|
if skip_first_title and isinstance(child, nodes.title):
|
|
465
484
|
continue
|
|
466
|
-
inner.extend(_convert_node(child,
|
|
485
|
+
inner.extend(_convert_node(child, ctx))
|
|
467
486
|
lines = _indent_label_content(_join_segments(inner), ctx.fmt)
|
|
468
487
|
return [('directive', lines)]
|
|
469
488
|
|
|
@@ -479,16 +498,17 @@ def _convert_node(node: nodes.Node, ctx: _RenderContext) -> list[Segment]:
|
|
|
479
498
|
if isinstance(node, nodes.literal_block):
|
|
480
499
|
return _convert_literal_block(node)
|
|
481
500
|
if type(node) in _ADMONITION_LABELS:
|
|
482
|
-
if _is_see_also_type(node)
|
|
501
|
+
if _is_see_also_type(node):
|
|
502
|
+
# "See Also" content is always dropped -- see _build_segments
|
|
503
|
+
# for the bare-rubric form and _is_see_also_section below for
|
|
504
|
+
# the hand-written-heading form.
|
|
483
505
|
return []
|
|
484
506
|
return _convert_admonition(node, _ADMONITION_LABELS[type(node)], ctx)
|
|
485
507
|
if _is_see_also_section(node):
|
|
486
508
|
# a hand-written "See Also\n--------" heading nests as a full
|
|
487
|
-
# section rather than a flat sibling -
|
|
488
|
-
#
|
|
489
|
-
|
|
490
|
-
return []
|
|
491
|
-
return _convert_admonition(node, 'SEE ALSO', ctx, skip_first_title=True)
|
|
509
|
+
# section rather than a flat sibling - dropped the same as any
|
|
510
|
+
# other "See Also" form.
|
|
511
|
+
return []
|
|
492
512
|
if isinstance(node, nodes.admonition):
|
|
493
513
|
# generic ``.. admonition:: Custom Title`` - use its own title as the label
|
|
494
514
|
title_node = node.next_node(nodes.title)
|
|
@@ -565,42 +585,6 @@ def _is_see_also_type(node: nodes.Node) -> bool:
|
|
|
565
585
|
return type(node) in _ADMONITION_LABELS and _ADMONITION_LABELS[type(node)] == 'SEE ALSO'
|
|
566
586
|
|
|
567
587
|
|
|
568
|
-
def _see_also_in_desc_content(desc: addnodes.desc) -> nodes.Node | None:
|
|
569
|
-
"""Find a "See Also" admonition-type node among a ``desc`` node's own ``desc_content``."""
|
|
570
|
-
content = next(
|
|
571
|
-
(child for child in desc.children if isinstance(child, addnodes.desc_content)), None
|
|
572
|
-
)
|
|
573
|
-
if content is None:
|
|
574
|
-
return None
|
|
575
|
-
return next((child for child in content.children if _is_see_also_type(child)), None)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
def _find_external_see_also(
|
|
579
|
-
parent: nodes.Element, start: int, end: int, heading: nodes.Node
|
|
580
|
-
) -> nodes.Node | None:
|
|
581
|
-
"""Find a "See Also" part sited outside the normal ``[start, end)`` span.
|
|
582
|
-
|
|
583
|
-
numpydoc's own "See Also" field is canonically reordered to sit before
|
|
584
|
-
"Examples", as a sibling under ``parent``. If "Examples" was written as
|
|
585
|
-
a real RST heading and hoisted out of its own ``desc_content`` to page
|
|
586
|
-
level (see ``_enclosing_descs``), "See Also" -- not a ``nodes.section``,
|
|
587
|
-
so untouched by that hoisting -- stays behind there instead, in the
|
|
588
|
-
``desc_content`` of the one ``desc`` that used to contain this heading.
|
|
589
|
-
Only that nearest enclosing ``desc`` is checked -- not every one
|
|
590
|
-
``_enclosing_descs`` yields -- or a page listing several documented
|
|
591
|
-
objects in a row would leak one object's "See Also" onto another's.
|
|
592
|
-
"""
|
|
593
|
-
for i, child in enumerate(parent.children):
|
|
594
|
-
if start <= i < end:
|
|
595
|
-
continue
|
|
596
|
-
if _is_see_also_type(child):
|
|
597
|
-
return child
|
|
598
|
-
desc = next(_enclosing_descs(heading), None)
|
|
599
|
-
if desc is not None:
|
|
600
|
-
return _see_also_in_desc_content(desc)
|
|
601
|
-
return None
|
|
602
|
-
|
|
603
|
-
|
|
604
588
|
def _examples_spans(doctree: nodes.document) -> list[tuple[nodes.Element, int, int, nodes.Node]]:
|
|
605
589
|
"""Find every "Examples" heading's content span.
|
|
606
590
|
|
|
@@ -839,20 +823,14 @@ def _build_segments(nodes_in_span: list[nodes.Node], ctx: _RenderContext) -> lis
|
|
|
839
823
|
"""Convert a span's nodes into segments.
|
|
840
824
|
|
|
841
825
|
A bare ``.. rubric:: See Also`` heading isn't wrapped in a container the
|
|
842
|
-
way ``.. seealso::`` or a nested section are
|
|
843
|
-
|
|
826
|
+
way ``.. seealso::`` or a nested section are, so everything after it is
|
|
827
|
+
just as much "See Also" content as the heading itself, with no node of
|
|
828
|
+
its own marking where it ends -- dropping the heading and stopping
|
|
829
|
+
there drops all of it.
|
|
844
830
|
"""
|
|
845
831
|
segments: list[Segment] = []
|
|
846
|
-
for
|
|
832
|
+
for node in nodes_in_span:
|
|
847
833
|
if isinstance(node, nodes.rubric) and _is_see_also_heading(node):
|
|
848
|
-
if not ctx.show_see_also:
|
|
849
|
-
break
|
|
850
|
-
inner_ctx = replace(ctx, in_see_also=True)
|
|
851
|
-
inner: list[Segment] = [('text', ['# SEE ALSO:'])]
|
|
852
|
-
for later_node in nodes_in_span[i + 1 :]:
|
|
853
|
-
inner.extend(_convert_node(later_node, inner_ctx))
|
|
854
|
-
lines = _indent_label_content(_join_segments(inner), ctx.fmt)
|
|
855
|
-
segments.append(('directive', lines))
|
|
856
834
|
break
|
|
857
835
|
segments.extend(_convert_node(node, ctx))
|
|
858
836
|
return segments
|
|
@@ -867,7 +845,6 @@ def _build_download_entries(
|
|
|
867
845
|
formats: list[str],
|
|
868
846
|
footer: str | None,
|
|
869
847
|
link_labels: dict[str, str],
|
|
870
|
-
include_see_also: bool,
|
|
871
848
|
) -> list[tuple[str, str]]:
|
|
872
849
|
"""Convert a span of nodes into written ``.py``/``.ipynb`` files, per ``formats``.
|
|
873
850
|
|
|
@@ -880,7 +857,7 @@ def _build_download_entries(
|
|
|
880
857
|
file's own header is the one heading every generated file has exactly
|
|
881
858
|
one of (see ``_title_underline_segment``).
|
|
882
859
|
"""
|
|
883
|
-
py_ctx = _RenderContext(app=app, docname=docname, fmt='py'
|
|
860
|
+
py_ctx = _RenderContext(app=app, docname=docname, fmt='py')
|
|
884
861
|
py_segments = _build_segments(nodes_in_span, py_ctx)
|
|
885
862
|
|
|
886
863
|
if not any(kind == 'code' for kind, _lines in py_segments):
|
|
@@ -901,9 +878,7 @@ def _build_download_entries(
|
|
|
901
878
|
if fmt == 'py':
|
|
902
879
|
rel_path = _write_source(app, name, source)
|
|
903
880
|
else:
|
|
904
|
-
ipynb_ctx = _RenderContext(
|
|
905
|
-
app=app, docname=docname, fmt='ipynb', show_see_also=include_see_also
|
|
906
|
-
)
|
|
881
|
+
ipynb_ctx = _RenderContext(app=app, docname=docname, fmt='ipynb')
|
|
907
882
|
ipynb_segments = _build_segments(nodes_in_span, ipynb_ctx)
|
|
908
883
|
ipynb_header = _title_underline_segment(header_title, 1, 'ipynb')
|
|
909
884
|
# Converted separately from the footer and concatenated, rather
|
|
@@ -929,14 +904,9 @@ def _process_span(
|
|
|
929
904
|
formats: list[str],
|
|
930
905
|
footer: str | None,
|
|
931
906
|
link_labels: dict[str, str],
|
|
932
|
-
include_see_also: bool,
|
|
933
907
|
) -> None:
|
|
934
908
|
"""Convert one Examples span and insert download link(s) if it has real code."""
|
|
935
909
|
nodes_in_span = list(parent.children[start:end])
|
|
936
|
-
if include_see_also:
|
|
937
|
-
external_see_also = _find_external_see_also(parent, start, end, heading)
|
|
938
|
-
if external_see_also is not None:
|
|
939
|
-
nodes_in_span.append(external_see_also)
|
|
940
910
|
|
|
941
911
|
name = _qualified_name_for(heading, docname, counter)
|
|
942
912
|
entries = _build_download_entries(
|
|
@@ -948,7 +918,6 @@ def _process_span(
|
|
|
948
918
|
formats,
|
|
949
919
|
footer,
|
|
950
920
|
link_labels,
|
|
951
|
-
include_see_also,
|
|
952
921
|
)
|
|
953
922
|
if not entries:
|
|
954
923
|
return
|
|
@@ -1029,7 +998,6 @@ def _process_gallery_page(
|
|
|
1029
998
|
formats: list[str],
|
|
1030
999
|
footer: str | None,
|
|
1031
1000
|
link_labels: dict[str, str],
|
|
1032
|
-
include_see_also: bool,
|
|
1033
1001
|
) -> None:
|
|
1034
1002
|
"""Replace a sphinx-gallery page's own download footer with converted downloads.
|
|
1035
1003
|
|
|
@@ -1072,7 +1040,6 @@ def _process_gallery_page(
|
|
|
1072
1040
|
formats,
|
|
1073
1041
|
footer,
|
|
1074
1042
|
link_labels,
|
|
1075
|
-
include_see_also,
|
|
1076
1043
|
)
|
|
1077
1044
|
if not entries:
|
|
1078
1045
|
return
|
|
@@ -1098,12 +1065,9 @@ def _process_doctree(app: Sphinx, doctree: nodes.document, docname: str) -> None
|
|
|
1098
1065
|
formats = conf['formats']
|
|
1099
1066
|
footer = conf['footer']
|
|
1100
1067
|
link_labels = conf['link_labels']
|
|
1101
|
-
include_see_also = conf['include_see_also']
|
|
1102
1068
|
|
|
1103
1069
|
if conf['gallery_downloads']:
|
|
1104
|
-
_process_gallery_page(
|
|
1105
|
-
app, docname, doctree, position, formats, footer, link_labels, include_see_also
|
|
1106
|
-
)
|
|
1070
|
+
_process_gallery_page(app, docname, doctree, position, formats, footer, link_labels)
|
|
1107
1071
|
|
|
1108
1072
|
# Process spans per shared parent, last to first.
|
|
1109
1073
|
spans = _examples_spans(doctree)
|
|
@@ -1123,7 +1087,6 @@ def _process_doctree(app: Sphinx, doctree: nodes.document, docname: str) -> None
|
|
|
1123
1087
|
formats,
|
|
1124
1088
|
footer,
|
|
1125
1089
|
link_labels,
|
|
1126
|
-
include_see_also,
|
|
1127
1090
|
)
|
|
1128
1091
|
|
|
1129
1092
|
|
|
@@ -1141,7 +1104,6 @@ _CONF_DEFAULTS: dict[str, object] = {
|
|
|
1141
1104
|
'gallery_downloads': False,
|
|
1142
1105
|
'footer': _DEFAULT_FOOTER,
|
|
1143
1106
|
'link_labels': _DEFAULT_LINK_LABELS,
|
|
1144
|
-
'include_see_also': True,
|
|
1145
1107
|
}
|
|
1146
1108
|
|
|
1147
1109
|
|
|
@@ -1159,7 +1121,7 @@ def _coerce_conf_value(key: str, value: object) -> object:
|
|
|
1159
1121
|
return value
|
|
1160
1122
|
if key == 'formats':
|
|
1161
1123
|
return value.split(',')
|
|
1162
|
-
if key
|
|
1124
|
+
if key == 'gallery_downloads':
|
|
1163
1125
|
if value == '0':
|
|
1164
1126
|
return False
|
|
1165
1127
|
if value == '1':
|
{sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/sphinx_examples_as_code/_version.py
RENAMED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.5.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g7bd70a33e'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-examples-as-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -66,7 +66,6 @@ sphinx_examples_as_code_conf = {
|
|
|
66
66
|
'py': 'Download Python source code',
|
|
67
67
|
'ipynb': 'Download Jupyter notebook',
|
|
68
68
|
},
|
|
69
|
-
'include_see_also': True,
|
|
70
69
|
}
|
|
71
70
|
```
|
|
72
71
|
|
|
@@ -76,13 +75,6 @@ sphinx_examples_as_code_conf = {
|
|
|
76
75
|
(default). Always offered in that order regardless of how the list is written.
|
|
77
76
|
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
|
|
78
77
|
format(s) you want to change; any left unset keep reading their own default shown above.
|
|
79
|
-
- `include_see_also`: whether "See Also" content is included in the generated file.
|
|
80
|
-
`True` (default) includes it, in any of its forms: a `.. seealso::` admonition, a bare
|
|
81
|
-
`.. rubric:: See Also`, a hand-written `See Also` heading, or numpydoc's own "See Also"
|
|
82
|
-
field (including when it's been reordered outside the Examples section itself, or the
|
|
83
|
-
Examples section has been hoisted to a heading of its own — a setup some projects use to
|
|
84
|
-
get "Examples" listed in the page's own navigation). `False` excludes "See Also" content
|
|
85
|
-
in every one of those forms.
|
|
86
78
|
- `gallery_downloads`: opt-in takeover of
|
|
87
79
|
[sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
|
|
88
80
|
`False` (default) leaves sphinx-gallery pages untouched. See
|
|
@@ -114,27 +106,26 @@ What happens to the content of an Examples section:
|
|
|
114
106
|
real Python source. Doctest *output* lines are dropped — only the input code matters.
|
|
115
107
|
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
|
|
116
108
|
comments, set off with blank lines on both sides like any other directive.
|
|
117
|
-
- Admonitions (`.. note::`, `.. warning::`,
|
|
118
|
-
|
|
119
|
-
`.py` only. "See Also" is recognized in any of its three forms (`.. seealso::`, a bare
|
|
120
|
-
`.. rubric:: See Also`, or a hand-written `See Also` heading) and always renders the
|
|
121
|
-
same way.
|
|
109
|
+
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
|
|
110
|
+
their content as comments, indented one level under the label in `.py` only.
|
|
122
111
|
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
|
|
123
|
-
line on both sides in either format
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
line on both sides in either format — a bare `#` instead of a real blank line when
|
|
113
|
+
that falls inside an admonition or a definition's own body in `.py`, so the whole
|
|
114
|
+
thing still reads as one unbroken comment block. A definition list's term stays at
|
|
115
|
+
the surrounding indent; its definition (the body nested under it) is indented one
|
|
116
|
+
level further in `.py`, same as an admonition's own content.
|
|
126
117
|
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
|
|
127
118
|
double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
|
|
128
119
|
``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
|
|
129
|
-
the reference resolves
|
|
130
|
-
|
|
131
|
-
else in `.py` the link is simply omitted.
|
|
120
|
+
the reference resolves, `.ipynb` turns it into a clickable link; `.py` never writes the
|
|
121
|
+
link, only the display text.
|
|
132
122
|
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
|
|
133
123
|
backticks.
|
|
134
124
|
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
|
|
135
125
|
`#` comment.
|
|
136
126
|
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
|
|
137
127
|
line are dropped entirely.
|
|
128
|
+
- "See Also" content is always dropped.
|
|
138
129
|
|
|
139
130
|
Generated `.py` files start with a `# Examples from <qualified name>` title header
|
|
140
131
|
(gallery mode uses the page's own title instead -- see below), with a few whitespace
|
{sphinx_examples_as_code-0.4.0 → sphinx_examples_as_code-0.5.0}/tests/test_single_function_page.py
RENAMED
|
@@ -38,37 +38,3 @@ def test_download_named_after_the_function_not_the_docname(tmp_path: Path):
|
|
|
38
38
|
|
|
39
39
|
lines = py_path.read_text(encoding='utf-8').splitlines()
|
|
40
40
|
assert lines[0] == '# Examples from mymodule.download_bunny'
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_hoisted_see_also_field_included_by_default(tmp_path: Path):
|
|
44
|
-
# numpydoc's own "See Also" field is never itself a section, so the
|
|
45
|
-
# hoisting that moves "Examples" out to page level (see conf.py) leaves
|
|
46
|
-
# "See Also" behind in the object's own desc_content -- orphaned from
|
|
47
|
-
# the heading unless _find_external_see_also looks there too.
|
|
48
|
-
html_dir = tmp_path / 'html'
|
|
49
|
-
doctree_dir = tmp_path / 'doctrees'
|
|
50
|
-
returncode, out, err = _run_sphinx_build(_sphinx_build_cmd(FIXTURE_DIR, html_dir, doctree_dir))
|
|
51
|
-
assert returncode == 0, f'sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n'
|
|
52
|
-
|
|
53
|
-
py_path = next((html_dir / '_downloads').rglob('*.py'))
|
|
54
|
-
content = py_path.read_text(encoding='utf-8')
|
|
55
|
-
assert '# SEE ALSO:' in content
|
|
56
|
-
assert 'Download bunny dataset.' in content.split('# SEE ALSO:')[1]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_hoisted_see_also_field_excluded_when_disabled(tmp_path: Path):
|
|
60
|
-
html_dir = tmp_path / 'html'
|
|
61
|
-
doctree_dir = tmp_path / 'doctrees'
|
|
62
|
-
returncode, out, err = _run_sphinx_build(
|
|
63
|
-
_sphinx_build_cmd(
|
|
64
|
-
FIXTURE_DIR,
|
|
65
|
-
html_dir,
|
|
66
|
-
doctree_dir,
|
|
67
|
-
('-D', 'sphinx_examples_as_code_conf.include_see_also=0'),
|
|
68
|
-
),
|
|
69
|
-
)
|
|
70
|
-
assert returncode == 0, f'sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n'
|
|
71
|
-
|
|
72
|
-
py_path = next((html_dir / '_downloads').rglob('*.py'))
|
|
73
|
-
content = py_path.read_text(encoding='utf-8')
|
|
74
|
-
assert '# SEE ALSO:' not in content
|