Metadata-Version: 2.4
Name: sphinx-examples-as-code
Version: 0.6.0
Summary: Sphinx extension for converting docstring examples into downloadable code.
Author-email: The PyVista Developers <info@pyvista.org>
License-Expression: MIT
Project-URL: Homepage, https://github.com/pyvista/sphinx-examples-as-code
Keywords: download,examples,sphinx
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Sphinx :: Extension
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyvista>=0.48
Dynamic: license-file

# sphinx-examples-as-code

A Sphinx extension that turns docstring/page "Examples" sections into downloadable,
runnable `.py` and/or `.ipynb` files, with a download link inserted into the section.

Pages or docstrings without an Examples section are left completely untouched. Adding
`sphinx_examples_as_code` to `conf.py`'s `extensions` is the only on/off switch.

## Installation

```bash
pip install sphinx-examples-as-code
```

Add it to your Sphinx `conf.py`:

```python
extensions = [
    ...,
    'sphinx_examples_as_code',
]
```

## Configuration

Everything lives in one dict in `conf.py`, `sphinx_examples_as_code_conf` -- set only
the keys you want to change from their default:

```python
sphinx_examples_as_code_conf = {
    'link_position': 'top',
    'formats': ['py', 'ipynb'],
    'gallery_downloads': False,
    'footer': (
        'Generated by `sphinx-examples-as-code '
        '<https://github.com/pyvista/sphinx-examples-as-code>`_'
    ),
    'link_labels': {
        'py': 'Download Python source code',
        'ipynb': 'Download Jupyter notebook',
    },
}
```

- `link_position`: where the download link(s) land within the Examples section. `'top'`
  (default) or `'bottom'`.
- `formats`: which downloads to generate. A list containing `'py'`, `'ipynb'`, or both
  (default). Always offered in that order regardless of how the list is written.
- `link_labels`: the text of the download link(s) themselves, per format. Set only the
  format(s) you want to change; any left unset keep reading their own default shown above.
- `gallery_downloads`: opt-in takeover of
  [sphinx-gallery](https://sphinx-gallery.github.io)'s own per-example downloads.
  `False` (default) leaves sphinx-gallery pages untouched. See
  [Sphinx-Gallery integration](#sphinx-gallery-integration) below.
- `footer`: a string appended to the end of every generated file. Defaults to a one-line
  "generated file" credit linking back to this project; set to `None` to omit it
  entirely. Preceded by a blank line and a `-`-only divider line, which also renders as a
  real horizontal rule in `.ipynb`; the footer always gets its own dedicated cell there.
  Parsed as RST: a hyperlink written as `` `text <url>`_ `` becomes a real clickable
  Markdown link in `.ipynb`, and renders inline as `text url` in `.py`. Plain text with no
  markup at all becomes one comment line per line of text, and blank-line-separated
  paragraphs stay separated.

An unrecognized key raises a configuration error at build start.

Cross-references and hyperlinks resolve into absolute links using Sphinx's own
[`html_baseurl`](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_baseurl).
Leave it unset and no links are generated anywhere.

Overriding a single key from the command line works via a dotted `-D` flag, e.g.
`-D sphinx_examples_as_code_conf.link_position=bottom` -- this only touches that one
key, leaving the rest (`conf.py`'s values, or the defaults) alone.

## Conversion rules

What happens to the content of an Examples section:

- Doctest blocks (`>>> ...` / `... ...`) keep their input lines, prompts stripped, as
  real Python source. Doctest *output* lines are dropped — only the input code matters.
- `.. code-block:: python` (or `py`) blocks are kept as-is; other languages become
  comments, set off with blank lines on both sides like any other directive.
- Admonitions (`.. note::`, `.. warning::`, ...) become a `# LABEL:` comment followed by
  their content as comments, indented one level under the label in `.py` only.
- A bullet/numbered list becomes a `-`/`N.`-marked line per item, set off with a blank
  line on both sides in either format — a bare `#` instead of a real blank line when
  that falls inside an admonition or a definition's own body in `.py`, so the whole
  thing still reads as one unbroken comment block. A definition list's term stays at
  the surrounding indent; its definition (the body nested under it) is indented one
  level further in `.py`, same as an admonition's own content.
- Cross-references and inline code (`:class:`, `:meth:`, `:func:`, `:attr:`,
  double-backtick literals, ...) keep their display text, wrapped in backticks (e.g.
  ``:class:`pyvista.Plotter` `` -> `` `pyvista.Plotter` ``). If `html_baseurl` is set and
  the reference resolves, `.ipynb` turns it into a clickable link; `.py` never writes the
  link, only the display text.
- Plain prose-style references (`:ref:`, `:doc:`) are treated the same way, minus the
  backticks.
- Everything else text-bearing (prose, captions, other non-Python code) becomes a plain
  `#` comment.
- A table becomes an aligned text table, its caption on the line above: an RST simple
  table in `.py`, a Markdown pipe table in `.ipynb`. Each cell is flattened to a single
  line, and a column left entirely empty -- one holding only images, say -- is dropped
  along with its heading.
- Figures/images, raw HTML, sphinx-design dropdowns/tab-sets, and sphinx-tags' `.. tags::`
  line are dropped entirely.
- "See Also" content is always dropped.

Generated `.py` files start with a `# Examples from <qualified name>` title header
(gallery mode uses the page's own title instead -- see below), with a few whitespace
conventions: prose directly above a code block stays attached to it, a code block is
always followed by a blank line, and a directive (header, `# NOTE:`-style block) gets
blank lines on both sides.

Generated `.ipynb` notebooks use the same content, split into alternating code/markdown
cells instead.

A download link is only added if the resulting code contains at least one real
executable statement.

## Sphinx-Gallery integration

With `sphinx_examples_as_code_conf['gallery_downloads'] = True`, this extension takes
over the downloads on every page generated by
[sphinx-gallery](https://sphinx-gallery.github.io): the "Go to the end to download the
full example code" note and the `.py`/`.ipynb`/`.zip` download footer are removed from
the page, replaced with download link(s) built by this extension instead — same
conversion rules as above, applied to the whole page rather than one Examples section.
The generated file's header uses the page's own title (e.g. `# Create Circular Arcs`)
rather than the generic `# Examples from <docname>`.

The "Total running time" line and the "Gallery generated by Sphinx-Gallery" credit line
stay on the rendered page untouched, but never make it into the generated download
itself.

Detection is automatic and per-page — any page without a sphinx-gallery download footer
is left completely untouched. Gallery pages and ordinary docstring/prose pages (using
the Examples-section behavior above) can coexist on the same site.

A `# %%` cell with its own RST heading gets the same header treatment as the file's own
title, and renders as a real Markdown heading in `.ipynb`. Level is relative to actual
RST section nesting, not always one below the file's own header: a cell heading nested
under the page's own title (the common case) is one level below it; one that reuses the
page title's own underline character is level 1, the same as the file's own header; a
cell heading nested under *that* is level 2 relative to it, and so on.

Each format uses one heading style consistently across every level, rather than mixing
styles:

- `.py` uses an RST-style title + underline, one character per level -- the same
  sequence Sphinx's own documentation uses for sections through sub-paragraphs: `=`, `-`,
  `~`, `^`, `"`, `'` for levels 1 through 6.
- `.ipynb` always uses ATX syntax (`#`, `##`, `###`, ...) at every level.

Two things worth knowing before turning this on:

- It's built against sphinx-gallery's own doctree output — the `sphx-glr-*` CSS classes
  its own theming depends on, and the private node type holding a highlighted code
  block — not a documented extension API. A future sphinx-gallery release could shift
  that structure without warning. Tested against sphinx-gallery 0.22 and later.
- sphinx-gallery's own `.py`/`.ipynb`/`.zip` downloads still end up copied into
  `_downloads/`, even though nothing on the page links to them anymore.

## Development

```bash
uv sync --group dev
uv run pytest
uv run pre-commit run --all-files
```
