sphinx-examples-as-code 0.2.0__tar.gz → 0.2.1__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.2.0 → sphinx_examples_as_code-0.2.1}/PKG-INFO +1 -1
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code/__init__.py +29 -4
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code/_version.py +3 -3
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/PKG-INFO +1 -1
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/SOURCES.txt +4 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/scm_file_list.json +4 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/scm_version.json +2 -2
- sphinx_examples_as_code-0.2.1/tests/single_function_fixture/conf.py +84 -0
- sphinx_examples_as_code-0.2.1/tests/single_function_fixture/index.rst +4 -0
- sphinx_examples_as_code-0.2.1/tests/single_function_fixture/mymodule.py +13 -0
- sphinx_examples_as_code-0.2.1/tests/test_single_function_page.py +40 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/test_sphinx_examples_as_code.py +40 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/.github/dependabot.yml +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/.github/release.yml +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/.github/workflows/ci.yml +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/.gitignore +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/.pre-commit-config.yaml +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/README.md +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/pyproject.toml +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/setup.cfg +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/dependency_links.txt +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/requires.txt +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code.egg-info/top_level.txt +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/examples/plot_minimal.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/test_gallery_downloads.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/test_tinypages.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/conf.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/docstring_cases.py +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/docstring_cases.rst +0 -0
- {sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/index.rst +0 -0
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/sphinx_examples_as_code/__init__.py
RENAMED
|
@@ -494,15 +494,40 @@ def _examples_spans(doctree: nodes.document) -> list[tuple[nodes.Element, int, i
|
|
|
494
494
|
return spans
|
|
495
495
|
|
|
496
496
|
|
|
497
|
+
def _desc_id(desc: addnodes.desc) -> str | None:
|
|
498
|
+
"""Pull the documented object's id (e.g. ``pkg.mod.func``) off a ``desc`` node."""
|
|
499
|
+
signature = desc.next_node(addnodes.desc_signature)
|
|
500
|
+
if signature is not None and signature.get('ids'):
|
|
501
|
+
return signature['ids'][0]
|
|
502
|
+
return None
|
|
503
|
+
|
|
504
|
+
|
|
497
505
|
def _qualified_name_for(node: nodes.Node, docname: str, counter: int) -> str:
|
|
498
506
|
"""Best-effort identifier used to name the generated file and its title header."""
|
|
499
507
|
ancestor: nodes.Node | None = node.parent
|
|
500
508
|
while ancestor is not None:
|
|
501
509
|
if isinstance(ancestor, addnodes.desc):
|
|
502
|
-
|
|
503
|
-
if
|
|
504
|
-
return
|
|
505
|
-
|
|
510
|
+
desc_id = _desc_id(ancestor)
|
|
511
|
+
if desc_id is not None:
|
|
512
|
+
return desc_id
|
|
513
|
+
parent = ancestor.parent
|
|
514
|
+
if parent is not None:
|
|
515
|
+
# An "Examples" section written as a real RST heading (not a
|
|
516
|
+
# bare ``.. rubric::``) starts a new docutils section -- one
|
|
517
|
+
# can't nest inside the ``desc_content`` a numpydoc-rendered
|
|
518
|
+
# docstring builds it in, so it gets promoted to a sibling of
|
|
519
|
+
# the object's own ``desc`` node instead (same reason
|
|
520
|
+
# sphinx-gallery's own "# %%"-headed cells become siblings; see
|
|
521
|
+
# gallery mode's sibling-section handling). Look for the
|
|
522
|
+
# nearest ``desc`` immediately preceding it under that shared
|
|
523
|
+
# parent, closest first.
|
|
524
|
+
index = parent.index(ancestor)
|
|
525
|
+
for sibling in reversed(parent.children[:index]):
|
|
526
|
+
if isinstance(sibling, addnodes.desc):
|
|
527
|
+
desc_id = _desc_id(sibling)
|
|
528
|
+
if desc_id is not None:
|
|
529
|
+
return desc_id
|
|
530
|
+
ancestor = parent
|
|
506
531
|
base = Path(docname).name or docname
|
|
507
532
|
return f'{base}-example-{counter}'
|
|
508
533
|
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 1)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g5224fa967'
|
|
@@ -15,12 +15,16 @@ sphinx_examples_as_code.egg-info/scm_file_list.json
|
|
|
15
15
|
sphinx_examples_as_code.egg-info/scm_version.json
|
|
16
16
|
sphinx_examples_as_code.egg-info/top_level.txt
|
|
17
17
|
tests/test_gallery_downloads.py
|
|
18
|
+
tests/test_single_function_page.py
|
|
18
19
|
tests/test_sphinx_examples_as_code.py
|
|
19
20
|
tests/test_tinypages.py
|
|
20
21
|
tests/gallery_fixture/conf.py
|
|
21
22
|
tests/gallery_fixture/index.rst
|
|
22
23
|
tests/gallery_fixture/examples/GALLERY_HEADER.rst
|
|
23
24
|
tests/gallery_fixture/examples/plot_minimal.py
|
|
25
|
+
tests/single_function_fixture/conf.py
|
|
26
|
+
tests/single_function_fixture/index.rst
|
|
27
|
+
tests/single_function_fixture/mymodule.py
|
|
24
28
|
tests/tinypages/conf.py
|
|
25
29
|
tests/tinypages/docstring_cases.py
|
|
26
30
|
tests/tinypages/docstring_cases.rst
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
".github/release.yml",
|
|
9
9
|
".github/workflows/ci.yml",
|
|
10
10
|
"tests/test_tinypages.py",
|
|
11
|
+
"tests/test_single_function_page.py",
|
|
11
12
|
"tests/test_gallery_downloads.py",
|
|
12
13
|
"tests/test_sphinx_examples_as_code.py",
|
|
13
14
|
"tests/tinypages/index.rst",
|
|
@@ -18,6 +19,9 @@
|
|
|
18
19
|
"tests/gallery_fixture/conf.py",
|
|
19
20
|
"tests/gallery_fixture/examples/plot_minimal.py",
|
|
20
21
|
"tests/gallery_fixture/examples/GALLERY_HEADER.rst",
|
|
22
|
+
"tests/single_function_fixture/index.rst",
|
|
23
|
+
"tests/single_function_fixture/conf.py",
|
|
24
|
+
"tests/single_function_fixture/mymodule.py",
|
|
21
25
|
"sphinx_examples_as_code/__init__.py"
|
|
22
26
|
]
|
|
23
27
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Sphinx config reproducing a real-world numpydoc section-hoisting setup.
|
|
2
|
+
|
|
3
|
+
numpydoc renders docstring sections (Notes, Examples, ...) as
|
|
4
|
+
``.. rubric::`` by default, which never becomes a real docutils section --
|
|
5
|
+
so ``_qualified_name_for``'s plain ancestor walk (desc_content -> desc)
|
|
6
|
+
always finds the object's own ``desc`` node directly, no special handling
|
|
7
|
+
needed. Some projects (pyvista's real docs included -- see its
|
|
8
|
+
``doc/source/conf.py``) override this to get real headings for the page's
|
|
9
|
+
"on this page" navbar, then hoist those sections out of the autodoc
|
|
10
|
+
``desc`` node to page level so Sphinx's TocTreeCollector can see them. That
|
|
11
|
+
lands "Examples" as a *sibling* of the object's own ``desc``, not an
|
|
12
|
+
ancestor of its own heading -- the exact structure that produced a
|
|
13
|
+
nonsensical ``<docname>-example-1`` header in production for a page that
|
|
14
|
+
unambiguously documents one function.
|
|
15
|
+
|
|
16
|
+
Reproduced here (rather than in ``tinypages/conf.py``) since it changes
|
|
17
|
+
docstring-section rendering globally for the build it's applied to, and
|
|
18
|
+
``tinypages`` is shared by many other tests that already pin exact output
|
|
19
|
+
for the un-hoisted (rubric-based) case.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
from docutils import nodes
|
|
28
|
+
from numpydoc.docscrape_sphinx import SphinxDocString
|
|
29
|
+
from sphinx import addnodes
|
|
30
|
+
|
|
31
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
32
|
+
|
|
33
|
+
extensions = [
|
|
34
|
+
'numpydoc',
|
|
35
|
+
'sphinx.ext.autodoc',
|
|
36
|
+
'sphinx_examples_as_code',
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
root_doc = 'index'
|
|
40
|
+
project = 'single_function_fixture'
|
|
41
|
+
exclude_patterns = ['_build']
|
|
42
|
+
numpydoc_show_class_members = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _str_header(self, name):
|
|
46
|
+
return [name, '-' * len(name), '']
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
SphinxDocString._str_header = _str_header
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _is_nested_desc(node: nodes.Node) -> bool:
|
|
53
|
+
parent = node.parent
|
|
54
|
+
while parent is not None:
|
|
55
|
+
if isinstance(parent, addnodes.desc):
|
|
56
|
+
return True
|
|
57
|
+
parent = parent.parent
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _hoist_docstring_sections(app, doctree) -> None:
|
|
62
|
+
"""Move docstring sections out of their ``desc`` node to page level."""
|
|
63
|
+
for desc in list(doctree.findall(addnodes.desc)):
|
|
64
|
+
if _is_nested_desc(desc):
|
|
65
|
+
continue
|
|
66
|
+
parent = desc.parent
|
|
67
|
+
if parent is None:
|
|
68
|
+
continue
|
|
69
|
+
# Only hoist when this object owns the page -- otherwise sections
|
|
70
|
+
# from several objects would collide at page level.
|
|
71
|
+
if len([node for node in parent if isinstance(node, addnodes.desc)]) != 1:
|
|
72
|
+
continue
|
|
73
|
+
content = next((node for node in desc if isinstance(node, addnodes.desc_content)), None)
|
|
74
|
+
if content is None:
|
|
75
|
+
continue
|
|
76
|
+
sections = [node for node in content if isinstance(node, nodes.section)]
|
|
77
|
+
index = parent.index(desc)
|
|
78
|
+
for offset, section in enumerate(sections):
|
|
79
|
+
content.remove(section)
|
|
80
|
+
parent.insert(index + 1 + offset, section)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def setup(app):
|
|
84
|
+
app.connect('doctree-read', _hoist_docstring_sections)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Regression test for a real numpydoc "hoisted section" page structure.
|
|
2
|
+
|
|
3
|
+
Builds a real sphinx site (not a hand-built doctree) using the same
|
|
4
|
+
rubric-to-heading + section-hoisting setup pyvista's actual docs build uses
|
|
5
|
+
(see ``single_function_fixture/conf.py``), confirming
|
|
6
|
+
``_qualified_name_for`` names the download after the documented function
|
|
7
|
+
rather than falling back to a nonsensical ``<docname>-example-1`` -- the
|
|
8
|
+
exact bug seen in a real pyvista build for
|
|
9
|
+
``pyvista.examples.downloads.download_bunny``.
|
|
10
|
+
|
|
11
|
+
Complements the hand-built-doctree unit tests for ``_qualified_name_for``
|
|
12
|
+
in ``test_sphinx_examples_as_code.py``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from test_tinypages import _run_sphinx_build
|
|
20
|
+
from test_tinypages import _sphinx_build_cmd
|
|
21
|
+
|
|
22
|
+
FIXTURE_DIR = Path(__file__).parent / 'single_function_fixture'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_download_named_after_the_function_not_the_docname(tmp_path: Path):
|
|
26
|
+
html_dir = tmp_path / 'html'
|
|
27
|
+
doctree_dir = tmp_path / 'doctrees'
|
|
28
|
+
returncode, out, err = _run_sphinx_build(_sphinx_build_cmd(FIXTURE_DIR, html_dir, doctree_dir))
|
|
29
|
+
assert returncode == 0, f'sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n'
|
|
30
|
+
|
|
31
|
+
downloads_dir = html_dir / '_downloads'
|
|
32
|
+
py_files = list(downloads_dir.rglob('*.py'))
|
|
33
|
+
assert len(py_files) == 1, f'expected exactly one generated .py, got {py_files}'
|
|
34
|
+
py_path = py_files[0]
|
|
35
|
+
|
|
36
|
+
# not "index-example-1" (the docname-derived fallback name)
|
|
37
|
+
assert py_path.stem == 'mymodule_download_bunny'
|
|
38
|
+
|
|
39
|
+
lines = py_path.read_text(encoding='utf-8').splitlines()
|
|
40
|
+
assert lines[0] == '# Examples from mymodule.download_bunny'
|
|
@@ -700,6 +700,46 @@ def test_qualified_name_for_desc_signature_without_ids_falls_back():
|
|
|
700
700
|
assert seac._qualified_name_for(heading, 'mypage', 2) == 'mypage-example-2'
|
|
701
701
|
|
|
702
702
|
|
|
703
|
+
def test_qualified_name_for_examples_section_sibling_of_desc():
|
|
704
|
+
r"""A real ``Examples\n--------`` heading, not a bare rubric.
|
|
705
|
+
|
|
706
|
+
Matches an actual numpydoc-rendered autosummary page: docutils can't
|
|
707
|
+
nest a new section inside the non-sectioning ``desc_content`` a
|
|
708
|
+
docstring's other fields render into, so it promotes "Examples" to a
|
|
709
|
+
section of its own, sitting as a *sibling* of the object's ``desc`` --
|
|
710
|
+
not an ancestor of the heading the way the plain-rubric case above is.
|
|
711
|
+
Confirmed against a real pyvista build, where this exact structure
|
|
712
|
+
produced a nonsensical ``<docname>-example-1`` header for a page that
|
|
713
|
+
unambiguously documents one function.
|
|
714
|
+
"""
|
|
715
|
+
page_section = nodes.section()
|
|
716
|
+
page_section += nodes.title('', 'download_bunny')
|
|
717
|
+
desc = addnodes.desc()
|
|
718
|
+
desc += addnodes.desc_signature(ids=['pyvista.examples.downloads.download_bunny'])
|
|
719
|
+
page_section += desc
|
|
720
|
+
examples_section = nodes.section()
|
|
721
|
+
heading = nodes.title('', 'Examples')
|
|
722
|
+
examples_section += heading
|
|
723
|
+
page_section += examples_section
|
|
724
|
+
|
|
725
|
+
assert (
|
|
726
|
+
seac._qualified_name_for(heading, 'page', 1) == 'pyvista.examples.downloads.download_bunny'
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def test_qualified_name_for_examples_section_sibling_no_desc_falls_back():
|
|
731
|
+
# a promoted sibling section with nothing resembling a desc anywhere
|
|
732
|
+
# nearby -- e.g. a plain prose page's own "Examples\n--------" heading
|
|
733
|
+
page_section = nodes.section()
|
|
734
|
+
page_section += nodes.title('', 'Some Guide Page')
|
|
735
|
+
examples_section = nodes.section()
|
|
736
|
+
heading = nodes.title('', 'Examples')
|
|
737
|
+
examples_section += heading
|
|
738
|
+
page_section += examples_section
|
|
739
|
+
|
|
740
|
+
assert seac._qualified_name_for(heading, 'mypage', 4) == 'mypage-example-4'
|
|
741
|
+
|
|
742
|
+
|
|
703
743
|
# ---------------------------------------------------------------------------
|
|
704
744
|
# _header_segment
|
|
705
745
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/conf.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/gallery_fixture/index.rst
RENAMED
|
File without changes
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/test_gallery_downloads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/docstring_cases.py
RENAMED
|
File without changes
|
{sphinx_examples_as_code-0.2.0 → sphinx_examples_as_code-0.2.1}/tests/tinypages/docstring_cases.rst
RENAMED
|
File without changes
|
|
File without changes
|