sphinx_spack_lite Extension

The sphinx_spack_lite Python package is a Sphinx extension that brings the spack-lite interactive terminal to any Sphinx documentation site. It adds a :runnable: option to the standard code-block directive; marked blocks gain a ▶ Run button that lazily initialises the spack-lite Web Worker and streams output inline — with zero impact on page-load time for readers who do not click.

Installation

The extension lives in the spack-lite repository and can be installed directly from GitHub:

$ pip install git+https://github.com/wdconinc/spack-lite.git

Or, if you have cloned the repository locally:

$ pip install -e /path/to/spack-lite

Configuration

Add "sphinx_spack_lite" to your conf.py extension list and set spack_lite_base_url to the URL prefix where worker.js, spack-lite.tar.gz, and spack-packages.tar.gz are served:

# conf.py
extensions = [
    ...,
    "sphinx_spack_lite",
]

# URL prefix where spack-lite assets are hosted.
# Must end with '/'.  Defaults to "_static/" (bundle into docs build).
# For ReadTheDocs, point to a GitHub Releases URL to avoid bloating
# the docs build with large binary tarballs.
spack_lite_base_url = "https://wdconinc.github.io/spack-lite/"

Authoring runnable blocks

Add :runnable: to any code-block directive whose shell commands should be executable in the browser. The extension extracts every line that begins with a $ prompt, strips it, and passes the joined commands to the worker when the reader clicks ▶ Run:

.. code-block:: console
   :runnable:

   $ spack list

   $ spack info hdf5

Multiple $ ... lines are concatenated and run as a single logical session. Non-prompt lines (output examples, comments) are ignored by the runner but still rendered for the reader.

How it works

  1. Build time — RunnableCodeBlock wraps the highlighted code block in a <div data-spack-runnable="true" data-spack-commands="…"> container. The commands string is the prompt-stripped text embedded as an HTML attribute.

  2. Page load — spack_run.js is added to every page that contains at least one runnable block (via Sphinx’s app.add_js_file with loading_method="defer"). On DOMContentLoaded it scans for [data-spack-runnable] elements and injects ▶ Run buttons and output <pre> elements — but does not start the worker.

  3. First click — the worker is created with new Worker(workerUrl). A { type: 'configure', … } message is posted immediately so the worker uses the correct asset URLs. A loading indicator appears on the button.

  4. Execution — subsequent clicks (or the first click once the worker is ready) post { type: 'run', command } to the worker. stdout chunks stream into the inline output area in real time; a result message marks the block as done.

  5. Shared environment — the same worker handles all blocks on the page, so a cd or spack install in one block affects subsequent blocks.

ReadTheDocs notes

ReadTheDocs does not currently support setting custom Cross-Origin-Opener-Policy / Cross-Origin-Embedder-Policy HTTP headers, which are required to enable SharedArrayBuffer. Pyodide can run without SharedArrayBuffer — only the Ctrl-C interrupt feature is degraded. Basic command execution is unaffected.

For spack_lite_base_url, point to a GitHub Releases download URL rather than bundling the binary tarballs into the docs build:

spack_lite_base_url = (
    "https://github.com/wdconinc/spack-lite/releases/latest/download/"
)

You will need to create a GitHub Release containing spack-lite.tar.gz and spack-packages.tar.gz as release assets.

Extension API reference

sphinx_spack_lite

A Sphinx extension that integrates spack-lite (the browser-based Spack REPL) into Sphinx documentation.

Usage

In conf.py:

extensions = [
    ...,
    "sphinx_spack_lite",
]

# URL prefix where worker.js, shim_system.py, shell.py,
# spack-lite.tar.gz and spack-packages.tar.gz are served.
# Default is "_static/" (i.e., they are bundled into the docs build).
# For ReadTheDocs you may want to point this at a GitHub Release URL
# to avoid bloating the docs build with large binary tarballs.
spack_lite_base_url = "https://github.com/your-org/spack-lite/releases/latest/download/"

In getting_started.rst:

.. code-block:: console
   :runnable:

   $ spack list

Notes

  • The Web Worker (worker.js) is initialised lazily: nothing loads until the user clicks the first “Run ▶” button on a page.

  • The worker is shared across all runnable blocks on a page, so the Spack environment (installed packages, cwd, etc.) persists between block runs.

  • SharedArrayBuffer-based interrupt (Ctrl-C) is unavailable on ReadTheDocs because it requires COOP/COEP HTTP headers that RTD does not support. Basic command execution is unaffected.

sphinx_spack_lite._directives

Patches Sphinx’s built-in code-block directive to recognise a :runnable: flag. When set, the generated code block is wrapped in a spack_run_container node whose HTML visitor emits a <div> with data-spack-runnable and data-spack-commands attributes. The JS layer (spack_run.js) scans for those attributes to inject “Run” buttons.

The commands string is built by stripping the shell prompt prefix (lines starting with $) from every line that begins with it; continuation lines (output, comments) are ignored. The result is a JSON-encoded list of bare commands.

class sphinx_spack_lite._directives.RunnableCodeBlock(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)

code-block subclass that adds the :runnable: boolean option.

option_spec = {'caption': <function unchanged_required>, 'class': <function class_option>, 'dedent': <function optional_int>, 'emphasize-lines': <function unchanged_required>, 'force': <function flag>, 'lineno-start': <class 'int'>, 'linenos': <function flag>, 'name': <function unchanged>, 'runnable': <function RunnableCodeBlock.<lambda>>}

Mapping of option names to validator functions.

sphinx_spack_lite._directives._extract_commands(source: str) → str

Return a JSON array of bare shell commands from a console block.

Only lines beginning with a shell prompt ($) are treated as command lines; all other lines (output, blank lines, comments) are dropped.

The result is a JSON-encoded list so that it survives round-trip through an HTML attribute value (HTML parsers normalise raw newlines in attributes to spaces, which would corrupt a newline-joined string).

class sphinx_spack_lite._directives.spack_run_container(rawsource: str = '', *children, **attributes: Any)

Wrapper node that carries spack-lite execution metadata as HTML data attrs.