sphinx-examples-as-code 0.8.3__tar.gz → 0.9.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.3 → sphinx_examples_as_code-0.9.0}/PKG-INFO +10 -2
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/README.md +9 -1
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code/__init__.py +68 -5
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code/_version.py +3 -3
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/PKG-INFO +10 -2
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/scm_version.json +2 -2
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_gallery_downloads.py +49 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_sphinx_examples_as_code.py +59 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/.github/dependabot.yml +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/.github/release.yml +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/.github/workflows/ci.yml +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/.gitignore +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/.pre-commit-config.yaml +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/LICENSE +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/pyproject.toml +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/setup.cfg +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/SOURCES.txt +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/dependency_links.txt +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/requires.txt +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/scm_file_list.json +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code.egg-info/top_level.txt +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/examples/plot_minimal.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/examples/GALLERY_HEADER.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/examples/plot_reused_level.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/examples/plot_three_levels.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/single_function_fixture/conf.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/single_function_fixture/index.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/single_function_fixture/mymodule.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_heading_level_reuse.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_single_function_page.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_tinypages.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/tinypages/conf.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/tinypages/docstring_cases.py +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/tinypages/docstring_cases.rst +0 -0
- {sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.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.9.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
|
|
@@ -159,7 +159,15 @@ Imports are read from the code, not run. Each imported top-level module maps to
|
|
|
159
159
|
project that provides it in the environment building the docs; standard-library
|
|
160
160
|
modules, relative imports and modules no single installed project provides are left
|
|
161
161
|
out. Projects the code needs but never imports, such as an optional backend, go in
|
|
162
|
-
`dependencies
|
|
162
|
+
`dependencies`, or for a single page in an `examples-as-code-dependencies` directive anywhere on it.
|
|
163
|
+
It takes PEP 508 requirements separated by spaces or commas, adds them to every download from
|
|
164
|
+
that page, and renders nothing:
|
|
165
|
+
|
|
166
|
+
```rst
|
|
167
|
+
.. examples-as-code-dependencies:: sympy imageio-ffmpeg>=0.4
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
In a Sphinx-Gallery example, put it in the example's docstring. In Python scripts and Jupyter notebooks, a dependency with no version or extras of
|
|
163
171
|
its own is left out when another listed one already installs it on every platform, e.g. `numpy`
|
|
164
172
|
next to `pyvista`. marimo notebooks list every dependency, since marimo's sandbox adds back any
|
|
165
173
|
it does not find by name.
|
|
@@ -128,7 +128,15 @@ Imports are read from the code, not run. Each imported top-level module maps to
|
|
|
128
128
|
project that provides it in the environment building the docs; standard-library
|
|
129
129
|
modules, relative imports and modules no single installed project provides are left
|
|
130
130
|
out. Projects the code needs but never imports, such as an optional backend, go in
|
|
131
|
-
`dependencies
|
|
131
|
+
`dependencies`, or for a single page in an `examples-as-code-dependencies` directive anywhere on it.
|
|
132
|
+
It takes PEP 508 requirements separated by spaces or commas, adds them to every download from
|
|
133
|
+
that page, and renders nothing:
|
|
134
|
+
|
|
135
|
+
```rst
|
|
136
|
+
.. examples-as-code-dependencies:: sympy imageio-ffmpeg>=0.4
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
In a Sphinx-Gallery example, put it in the example's docstring. In Python scripts and Jupyter notebooks, a dependency with no version or extras of
|
|
132
140
|
its own is left out when another listed one already installs it on every platform, e.g. `numpy`
|
|
133
141
|
next to `pyvista`. marimo notebooks list every dependency, since marimo's sandbox adds back any
|
|
134
142
|
it does not find by name.
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/sphinx_examples_as_code/__init__.py
RENAMED
|
@@ -23,6 +23,7 @@ from urllib.parse import urlsplit
|
|
|
23
23
|
|
|
24
24
|
from docutils import nodes
|
|
25
25
|
from docutils.core import publish_doctree
|
|
26
|
+
from packaging.requirements import InvalidRequirement
|
|
26
27
|
from packaging.requirements import Requirement
|
|
27
28
|
from packaging.specifiers import InvalidSpecifier
|
|
28
29
|
from packaging.specifiers import SpecifierSet
|
|
@@ -32,6 +33,7 @@ from packaging.version import Version
|
|
|
32
33
|
from sphinx import addnodes
|
|
33
34
|
from sphinx.errors import ConfigError
|
|
34
35
|
from sphinx.util import logging
|
|
36
|
+
from sphinx.util.docutils import SphinxDirective
|
|
35
37
|
|
|
36
38
|
from ._version import __version__
|
|
37
39
|
|
|
@@ -42,6 +44,7 @@ if TYPE_CHECKING:
|
|
|
42
44
|
|
|
43
45
|
from sphinx.application import Sphinx
|
|
44
46
|
from sphinx.config import Config
|
|
47
|
+
from sphinx.environment import BuildEnvironment
|
|
45
48
|
|
|
46
49
|
# Node types marking the start of another section - bound the end of an
|
|
47
50
|
# Examples span. Includes desc/index -- numpydoc renders a class's members
|
|
@@ -336,14 +339,19 @@ def _join_segments(segments: list[Segment]) -> list[str]:
|
|
|
336
339
|
|
|
337
340
|
|
|
338
341
|
def _convert_doctest_block(node: nodes.doctest_block) -> list[Segment]:
|
|
339
|
-
"""Convert a doctest block
|
|
342
|
+
"""Convert a doctest block; see ``_convert_doctest_source``."""
|
|
343
|
+
return _convert_doctest_source(node.astext())
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _convert_doctest_source(source: str) -> list[Segment]:
|
|
347
|
+
"""Convert doctest text, stripping ``>>> ``/``... `` prompts.
|
|
340
348
|
|
|
341
349
|
Non-prompted, non-blank lines are doctest *output* -- dropped entirely,
|
|
342
350
|
not kept as comments.
|
|
343
351
|
"""
|
|
344
352
|
lines: list[str] = []
|
|
345
353
|
has_code = False
|
|
346
|
-
for line in
|
|
354
|
+
for line in source.splitlines():
|
|
347
355
|
if line.startswith('>>> ') or line == '>>>':
|
|
348
356
|
lines.append(_clean_code_comment(line[4:]))
|
|
349
357
|
has_code = True
|
|
@@ -382,6 +390,8 @@ def _convert_literal_block(node: nodes.Element) -> list[Segment]:
|
|
|
382
390
|
language, source = node.get('language', ''), node.astext()
|
|
383
391
|
# Case-insensitive: sphinx-gallery emits ``.. code-block:: Python`` (capitalized).
|
|
384
392
|
if language.lower() in _PYTHON_LANGUAGES:
|
|
393
|
+
if source.lstrip().startswith('>>>'):
|
|
394
|
+
return _convert_doctest_source(source)
|
|
385
395
|
lines = [_clean_code_comment(line) for line in source.splitlines()]
|
|
386
396
|
while lines and not lines[-1].strip():
|
|
387
397
|
lines.pop()
|
|
@@ -1006,20 +1016,29 @@ def _drop_future_imports(source: list[str]) -> list[str]:
|
|
|
1006
1016
|
return [line for i, line in enumerate(source, start=1) if i not in dropped]
|
|
1007
1017
|
|
|
1008
1018
|
|
|
1019
|
+
# Stands in for a line's leading % so marimo does not read it as an IPython magic.
|
|
1020
|
+
_PERCENT_MARK = '\ue000'
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
def _mark_percent_lines(source: list[str]) -> list[str]:
|
|
1024
|
+
"""Replace the ``%`` that starts a line with ``_PERCENT_MARK``."""
|
|
1025
|
+
return [_PERCENT_MARK + line[1:] if line.startswith('%') else line for line in source]
|
|
1026
|
+
|
|
1027
|
+
|
|
1009
1028
|
def _write_marimo_notebook(app: Sphinx, name: str, notebook: dict, header: str = '') -> str:
|
|
1010
1029
|
"""Convert a generated Jupyter notebook with marimo's own converter and write it."""
|
|
1011
1030
|
from marimo._convert.converters import MarimoConvert
|
|
1012
1031
|
|
|
1013
1032
|
# A __future__ import inside a marimo cell is a SyntaxError when run as a script.
|
|
1014
1033
|
cells = [
|
|
1015
|
-
{**cell, 'source': _drop_future_imports(cell['source'])}
|
|
1034
|
+
{**cell, 'source': _mark_percent_lines(_drop_future_imports(cell['source']))}
|
|
1016
1035
|
if cell['cell_type'] == 'code'
|
|
1017
1036
|
else cell
|
|
1018
1037
|
for cell in notebook['cells']
|
|
1019
1038
|
]
|
|
1020
1039
|
ir = MarimoConvert.from_ipynb(json.dumps({**notebook, 'cells': cells})).to_ir()
|
|
1021
1040
|
source = MarimoConvert.from_ir(replace(ir, cells=_title_first(ir.cells))).to_py()
|
|
1022
|
-
return _write_download_file(app, name, _FMT_MARIMO, header + source)
|
|
1041
|
+
return _write_download_file(app, name, _FMT_MARIMO, header + source.replace(_PERCENT_MARK, '%'))
|
|
1023
1042
|
|
|
1024
1043
|
|
|
1025
1044
|
@functools.cache
|
|
@@ -1351,10 +1370,13 @@ def _build_download_entries(
|
|
|
1351
1370
|
if script_metadata is not None:
|
|
1352
1371
|
implied = {_FMT_MARIMO: ['marimo']}
|
|
1353
1372
|
extra = script_metadata['dependencies']
|
|
1373
|
+
page = script_metadata.get('page_dependencies', [])
|
|
1354
1374
|
project = _documented_project(script_metadata['project'])
|
|
1355
1375
|
pins = {project.name: project.pin} if project and project.pin else {}
|
|
1356
1376
|
deps = {
|
|
1357
|
-
fmt: _script_dependencies(
|
|
1377
|
+
fmt: _script_dependencies(
|
|
1378
|
+
source, [*implied.get(fmt, []), *extra.get(fmt, []), *page], pins
|
|
1379
|
+
)
|
|
1358
1380
|
for fmt in formats
|
|
1359
1381
|
}
|
|
1360
1382
|
|
|
@@ -1595,6 +1617,7 @@ def _process_doctree(app: Sphinx, doctree: nodes.document, docname: str) -> None
|
|
|
1595
1617
|
'dependencies': conf['dependencies'],
|
|
1596
1618
|
'requires_python': conf['requires_python'],
|
|
1597
1619
|
'project': app.config.project,
|
|
1620
|
+
'page_dependencies': _page_dependencies(app.env).get(docname, []),
|
|
1598
1621
|
}
|
|
1599
1622
|
if conf['script_metadata']
|
|
1600
1623
|
else None
|
|
@@ -1837,8 +1860,48 @@ def _disable_gallery_notebooks(_app: Sphinx, config: Config) -> None:
|
|
|
1837
1860
|
config.sphinx_gallery_conf = {**gallery_conf, **overrides}
|
|
1838
1861
|
|
|
1839
1862
|
|
|
1863
|
+
def _page_dependencies(env: BuildEnvironment) -> dict[str, list[str]]:
|
|
1864
|
+
"""Return the requirements each page added with ``examples-as-code-dependencies``."""
|
|
1865
|
+
return vars(env).setdefault('_examples_as_code_dependencies', {})
|
|
1866
|
+
|
|
1867
|
+
|
|
1868
|
+
class _DependenciesDirective(SphinxDirective):
|
|
1869
|
+
"""Add requirements to every download from this page; renders nothing."""
|
|
1870
|
+
|
|
1871
|
+
required_arguments = 1
|
|
1872
|
+
final_argument_whitespace = True
|
|
1873
|
+
|
|
1874
|
+
def run(self) -> list[nodes.Node]:
|
|
1875
|
+
"""Record the requirements for the current page."""
|
|
1876
|
+
requirements = self.arguments[0].replace(',', ' ').split()
|
|
1877
|
+
for requirement in requirements:
|
|
1878
|
+
try:
|
|
1879
|
+
Requirement(requirement)
|
|
1880
|
+
except InvalidRequirement as exc:
|
|
1881
|
+
msg = f'Invalid requirement {requirement!r}: {exc}'
|
|
1882
|
+
raise self.error(msg) from None
|
|
1883
|
+
_page_dependencies(self.env).setdefault(self.env.docname, []).extend(requirements)
|
|
1884
|
+
return []
|
|
1885
|
+
|
|
1886
|
+
|
|
1887
|
+
def _purge_page_dependencies(_app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
|
1888
|
+
"""Forget a page's requirements before it is read again."""
|
|
1889
|
+
_page_dependencies(env).pop(docname, None)
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
def _merge_page_dependencies(
|
|
1893
|
+
_app: Sphinx, env: BuildEnvironment, docnames: set[str], other: BuildEnvironment
|
|
1894
|
+
) -> None:
|
|
1895
|
+
"""Copy the requirements a parallel reader recorded into the main environment."""
|
|
1896
|
+
theirs = _page_dependencies(other)
|
|
1897
|
+
_page_dependencies(env).update((name, theirs[name]) for name in docnames if name in theirs)
|
|
1898
|
+
|
|
1899
|
+
|
|
1840
1900
|
def setup(app: Sphinx) -> dict: # numpydoc ignore=RT01
|
|
1841
1901
|
"""Register the extension."""
|
|
1902
|
+
app.add_directive('examples-as-code-dependencies', _DependenciesDirective)
|
|
1903
|
+
app.connect('env-purge-doc', _purge_page_dependencies)
|
|
1904
|
+
app.connect('env-merge-info', _merge_page_dependencies)
|
|
1842
1905
|
app.connect('doctree-resolved', _process_doctree)
|
|
1843
1906
|
app.connect('config-inited', _finalize_conf)
|
|
1844
1907
|
# sphinx-gallery fills its own defaults at priority 10.
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.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.9.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 9, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g71df6bcfb'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-examples-as-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -159,7 +159,15 @@ Imports are read from the code, not run. Each imported top-level module maps to
|
|
|
159
159
|
project that provides it in the environment building the docs; standard-library
|
|
160
160
|
modules, relative imports and modules no single installed project provides are left
|
|
161
161
|
out. Projects the code needs but never imports, such as an optional backend, go in
|
|
162
|
-
`dependencies
|
|
162
|
+
`dependencies`, or for a single page in an `examples-as-code-dependencies` directive anywhere on it.
|
|
163
|
+
It takes PEP 508 requirements separated by spaces or commas, adds them to every download from
|
|
164
|
+
that page, and renders nothing:
|
|
165
|
+
|
|
166
|
+
```rst
|
|
167
|
+
.. examples-as-code-dependencies:: sympy imageio-ffmpeg>=0.4
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
In a Sphinx-Gallery example, put it in the example's docstring. In Python scripts and Jupyter notebooks, a dependency with no version or extras of
|
|
163
171
|
its own is left out when another listed one already installs it on every platform, e.g. `numpy`
|
|
164
172
|
next to `pyvista`. marimo notebooks list every dependency, since marimo's sandbox adds back any
|
|
165
173
|
it does not find by name.
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_gallery_downloads.py
RENAMED
|
@@ -334,3 +334,52 @@ def test_gallery_downloads_enabled_warns_about_notebook_settings(tmp_path: Path)
|
|
|
334
334
|
assert "sphinx_gallery_conf key(s) 'pypandoc', 'first_notebook_cell' have no effect" in err
|
|
335
335
|
assert "'pypandoc' not available" not in err
|
|
336
336
|
assert not list((src_dir / 'auto_examples').rglob('*.ipynb'))
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _build_with_dependencies(tmp_path: Path, requirements: str) -> tuple[int, str, str, Path]:
|
|
340
|
+
"""Build the fixture with an ``examples-as-code-dependencies`` directive in its example."""
|
|
341
|
+
src_dir = tmp_path / 'src'
|
|
342
|
+
shutil.copytree(FIXTURE_DIR, src_dir)
|
|
343
|
+
example = src_dir / 'examples' / 'plot_minimal.py'
|
|
344
|
+
source = example.read_text(encoding='utf-8')
|
|
345
|
+
marker = '==========================\n'
|
|
346
|
+
directive = f'{marker}\n.. examples-as-code-dependencies:: {requirements}\n'
|
|
347
|
+
example.write_text(source.replace(marker, directive, 1), encoding='utf-8')
|
|
348
|
+
html_dir = tmp_path / 'html'
|
|
349
|
+
sphinx_args = (
|
|
350
|
+
'-D',
|
|
351
|
+
'sphinx_examples_as_code_conf.gallery_downloads=1',
|
|
352
|
+
'-D',
|
|
353
|
+
'sphinx_examples_as_code_conf.script_metadata=1',
|
|
354
|
+
'-D',
|
|
355
|
+
'sphinx_examples_as_code_conf.formats=python,jupyter,marimo',
|
|
356
|
+
)
|
|
357
|
+
returncode, out, err = _run_sphinx_build(
|
|
358
|
+
_sphinx_build_cmd(src_dir, html_dir, tmp_path / 'doctrees', sphinx_args)
|
|
359
|
+
)
|
|
360
|
+
return returncode, out, err, html_dir
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def test_dependencies_directive_adds_to_every_download(tmp_path: Path):
|
|
364
|
+
"""``examples-as-code-dependencies`` lists its requirements in each download's header."""
|
|
365
|
+
returncode, out, err, html_dir = _build_with_dependencies(tmp_path, 'sympy tqdm>=4')
|
|
366
|
+
assert returncode == 0, f'sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n'
|
|
367
|
+
downloads = [
|
|
368
|
+
path
|
|
369
|
+
for path in (html_dir / '_downloads').rglob('plot_minimal*')
|
|
370
|
+
if path.suffix == '.ipynb'
|
|
371
|
+
or (path.suffix == '.py' and path.read_text(encoding='utf-8').startswith('# /// script'))
|
|
372
|
+
]
|
|
373
|
+
assert sorted(path.suffix for path in downloads) == ['.ipynb', '.py', '.py']
|
|
374
|
+
for path in downloads:
|
|
375
|
+
text = path.read_text(encoding='utf-8')
|
|
376
|
+
assert 'sympy' in text
|
|
377
|
+
assert 'tqdm>=4' in text
|
|
378
|
+
assert 'examples-as-code-dependencies' not in text
|
|
379
|
+
html = (html_dir / 'auto_examples' / 'plot_minimal.html').read_text(encoding='utf-8')
|
|
380
|
+
assert 'examples-as-code-dependencies' not in html
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def test_dependencies_directive_rejects_invalid_requirement(tmp_path: Path):
|
|
384
|
+
_returncode, _out, err, _html_dir = _build_with_dependencies(tmp_path, 'not!valid')
|
|
385
|
+
assert "Invalid requirement 'not!valid'" in err
|
|
@@ -15,6 +15,7 @@ from unittest.mock import Mock
|
|
|
15
15
|
|
|
16
16
|
from docutils import nodes
|
|
17
17
|
from docutils.core import publish_doctree
|
|
18
|
+
from docutils.parsers.rst import DirectiveError
|
|
18
19
|
import pytest
|
|
19
20
|
from sphinx import addnodes
|
|
20
21
|
from sphinx.errors import ConfigError
|
|
@@ -352,6 +353,13 @@ def test_convert_doctest_block_trailing_blank_trimmed():
|
|
|
352
353
|
# ---------------------------------------------------------------------------
|
|
353
354
|
|
|
354
355
|
|
|
356
|
+
@pytest.mark.parametrize('language', ['python', 'py'])
|
|
357
|
+
def test_convert_literal_block_python_doctest_style(language: str):
|
|
358
|
+
node = nodes.literal_block('', '>>> import math\n>>> math.floor(8.5)\n8')
|
|
359
|
+
node['language'] = language
|
|
360
|
+
assert seac._convert_literal_block(node) == [('code', ['import math', 'math.floor(8.5)'])]
|
|
361
|
+
|
|
362
|
+
|
|
355
363
|
def test_convert_literal_block_python():
|
|
356
364
|
# Sphinx's own code-block directive sets a ``language`` attribute
|
|
357
365
|
# (unlike plain docutils', which uses CSS classes instead), so this
|
|
@@ -3096,6 +3104,21 @@ def test_script_metadata_drops_covered_except_marimo(
|
|
|
3096
3104
|
assert ('numpy' in str(header)) is listed
|
|
3097
3105
|
|
|
3098
3106
|
|
|
3107
|
+
def test_write_marimo_notebook_keeps_percent_lines(tmp_path: Path):
|
|
3108
|
+
code = ['art = """\n', '% %\n', '%% %\n', '"""\n', 'x = 5 % 3']
|
|
3109
|
+
notebook = {
|
|
3110
|
+
'cells': [{'cell_type': 'code', 'metadata': {}, 'source': code}],
|
|
3111
|
+
'metadata': {},
|
|
3112
|
+
'nbformat': 4,
|
|
3113
|
+
'nbformat_minor': 5,
|
|
3114
|
+
}
|
|
3115
|
+
rel_path = seac._write_marimo_notebook(Mock(outdir=str(tmp_path)), 'art', notebook)
|
|
3116
|
+
source = (tmp_path / '_downloads' / rel_path).read_text(encoding='utf-8')
|
|
3117
|
+
assert 'magic command' not in source
|
|
3118
|
+
assert seac._PERCENT_MARK not in source
|
|
3119
|
+
assert ' art = """\n % %\n %% %\n """\n x = 5 % 3\n' in source
|
|
3120
|
+
|
|
3121
|
+
|
|
3099
3122
|
@pytest.mark.parametrize(
|
|
3100
3123
|
('codes', 'expected'),
|
|
3101
3124
|
[
|
|
@@ -3304,3 +3327,39 @@ def test_drop_covered_keeps_pinned_platform_specific_and_cycles(fake_requires):
|
|
|
3304
3327
|
def test_drop_covered_without_metadata_keeps_everything(fake_requires):
|
|
3305
3328
|
fake_requires({})
|
|
3306
3329
|
assert seac._drop_covered(['numpy', 'pyvista']) == ['numpy', 'pyvista']
|
|
3330
|
+
|
|
3331
|
+
|
|
3332
|
+
def test_page_dependencies_purge_and_merge():
|
|
3333
|
+
env, other = SimpleNamespace(), SimpleNamespace()
|
|
3334
|
+
seac._page_dependencies(env)['a'] = ['sympy']
|
|
3335
|
+
seac._page_dependencies(other).update({'b': ['tqdm'], 'c': ['rich']})
|
|
3336
|
+
seac._merge_page_dependencies(None, env, {'b'}, other)
|
|
3337
|
+
assert seac._page_dependencies(env) == {'a': ['sympy'], 'b': ['tqdm']}
|
|
3338
|
+
seac._purge_page_dependencies(None, env, 'a')
|
|
3339
|
+
seac._purge_page_dependencies(None, env, 'missing')
|
|
3340
|
+
assert seac._page_dependencies(env) == {'b': ['tqdm']}
|
|
3341
|
+
|
|
3342
|
+
|
|
3343
|
+
def _run_dependencies_directive(argument: str, env: SimpleNamespace) -> list[nodes.Node]:
|
|
3344
|
+
"""Run ``examples-as-code-dependencies`` with ``argument`` on page ``page``."""
|
|
3345
|
+
state = Mock()
|
|
3346
|
+
state.document.settings.env = env
|
|
3347
|
+
directive = seac._DependenciesDirective(
|
|
3348
|
+
'examples-as-code-dependencies', [argument], {}, [], 1, 0, '', state, Mock()
|
|
3349
|
+
)
|
|
3350
|
+
return directive.run()
|
|
3351
|
+
|
|
3352
|
+
|
|
3353
|
+
def test_dependencies_directive_records_requirements():
|
|
3354
|
+
env = SimpleNamespace(docname='page')
|
|
3355
|
+
assert _run_dependencies_directive('sympy, tqdm>=4', env) == []
|
|
3356
|
+
_run_dependencies_directive('rich', env)
|
|
3357
|
+
assert seac._page_dependencies(env) == {'page': ['sympy', 'tqdm>=4', 'rich']}
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
def test_dependencies_directive_invalid_requirement():
|
|
3361
|
+
env = SimpleNamespace(docname='page')
|
|
3362
|
+
with pytest.raises(DirectiveError) as excinfo:
|
|
3363
|
+
_run_dependencies_directive('sympy not!valid', env)
|
|
3364
|
+
assert excinfo.value.msg.startswith("Invalid requirement 'not!valid'")
|
|
3365
|
+
assert seac._page_dependencies(env) == {}
|
|
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
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/conf.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/gallery_fixture/index.rst
RENAMED
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/heading_level_fixture/conf.py
RENAMED
|
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.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_heading_level_reuse.py
RENAMED
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/test_single_function_page.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/tinypages/docstring_cases.py
RENAMED
|
File without changes
|
{sphinx_examples_as_code-0.8.3 → sphinx_examples_as_code-0.9.0}/tests/tinypages/docstring_cases.rst
RENAMED
|
File without changes
|
|
File without changes
|