sphinx-pyrepl-web

Sphinx extension to embed pyrepl-web in documentation.

Install

pip install sphinx-pyrepl-web

For development:

pip install -e ".[test,docs]"

Usage

Add the extension to the target project’s conf.py:

extensions = [
    "sphinx_pyrepl_web",
]

Embed a REPL with the py-repl directive:

.. py-repl::

.. py-repl::
   :theme: catppuccin-latte
   :no-header:

.. py-repl::
   :src: setup.py
   :packages: numpy

.. py-repl::
   :no-header:

   >>> import math
   >>> math.sqrt(16)

Directive options

All options drive pyrepl-web’s attributes, with the exception of silent:

Option

Description

:theme:

Color theme (catppuccin-mocha, catppuccin-latte)

:packages:

Comma-separated PyPI packages or local wheel paths under _static/wheels/

:repl-title:

Title in the REPL header

:src:

Path to a Python startup script

:replay:

Replay :src: with interactive prompts instead of silent load

:silent:

Keep :src: silent even when combined with a directive body

:no-header:

Hide the header bar

:no-buttons:

Hide copy/clear buttons

:readonly:

Disable input

:no-banner:

Hide the Python version banner

Python code within the .. py-repl:: directive is written to _static/pyrepl/ at build time and emitted as replay-src.

File paths in :packages:, :src:, replay-src, and pyrepl_autodoc_packages are rewritten to page-relative URLs at build time so REPLs work on nested pages (for example docs/api/...). PyPI package names, absolute URLs, and paths you write as root-absolute (/_static/...) are left unchanged.

Optional Sphinx config:

pyrepl_js = "../pyrepl.js"  # default; path to the pyrepl-web loader script
pyrepl_doctest_blocks = False  # default; see Docstring conversion below
pyrepl_autodoc_packages = None  # optional; wheel path or PyPI name for autodoc REPLs

Docstring conversion

Converting doctest examples from docstrings into interactive REPLs is opt-in with sphinx.ext.autodoc:

# conf.py
extensions = [
    "sphinx.ext.autodoc",
    "sphinx_pyrepl_web",
]
pyrepl_doctest_blocks = "autodoc"
pyrepl_autodoc_packages = "_static/wheels/my_package-1.0.0-py3-none-any.whl"

pyrepl_doctest_blocks options

False (default)

Disable autodoc conversion

"autodoc"

Convert doctests found by autodoc

"all"

Transform every doctest block found

pyrepl_autodoc_packages options

unset / None / ""

Replay doctest input only (no wheel install or auto-import)

wheel path or PyPI name

Install the package and import the documented object before replay (comma-separated)

Autodoc integration assumes a single documented package. The wheel (or PyPI name) is installed in the browser REPL and the documented name is imported automatically so doctest examples can use unqualified names. Autodoc still imports the package on the host at build time.

To build this project’s docs locally:

pip install -e ".[docs]"

The [docs] extra installs doc build dependencies plus the pyrepl_test_pkg fixture used in the examples.

Local Pyodide wheels

Preload a wheel built for Pyodide (for example a wasm extension or an unreleased branch build) by placing it under the Sphinx static path and referencing it from :packages:. Ensure html_static_path includes "_static":

# conf.py
html_static_path = ["_static"]
.. py-repl::
   :packages: _static/wheels/myext-pyodide.whl
   :src: _static/bootstrap.py
   :no-banner:

   >>> import myext
   >>> myext.ping()

Wheels under _static/ are copied into the HTML output when _static is listed in html_static_path (Sphinx does not copy project static files automatically unless configured). At runtime, pyrepl-web resolves site-relative wheel paths to absolute URLs before calling micropip.install().

CI tip: copy each build artifact to a stable docs filename so RST does not need updating per release, for example cp dist/myext-1.2.3-*.whl docs/_static/wheels/myext-pyodide.whl.

Ensure the web server serves .whl files with MIME type application/zip (Read the Docs does this by default).

Updating pyrepl-web

Since chrizzFTD/pyrepl-web is a fork, this sphinx extension vendors the JavaScript assets for easier distribution. To update them, run:

python scripts/vendor_repl.py

The grill branch is used by default. Use the branch argument to specify a different one:

python scripts/vendor_repl.py --branch custom/feature-branch

This requires git and Bun.

Examples

Basic REPL

.. py-repl::

Light theme, minimal

.. py-repl::
   :theme: catppuccin-latte
   :no-header:
   :no-banner:

Startup script

The :src: option loads a Python script into the REPL namespace. If the script defines a setup() function, its output is shown when the REPL starts.

Startup script:

message = "Hello from the startup script!"


def setup():
    print(message)
    print("Try: message")

RST content:

.. py-repl::
   :src: _static/setup.py

Rendered result:

Replay session

Inline directive content should follow Doctest-style (>>> / ...) and is used as replay prompts.

.. py-repl::
   :no-header:
   :no-banner:

   >>> x = 2 + 2
   >>> print(f"{x=}")
   >>> x * 10
   >>> class Foo:
   ...     x = 1
   ...
   >>> Foo()

Combine a silent bootstrap file with a visible replay body:

.. py-repl::
   :src: _static/setup.py
   :no-header:

   >>> print(message)

Use :replay: on :src: to source a file as replay.

Source script:

greeting = "Hello from replay mode"
print(greeting)
greeting.upper()

RST content:

.. py-repl::
   :src: _static/replay_demo.py
   :replay:
   :no-header:
   :no-banner:

Rendered result:

Autodoc

When pyrepl_doctest_blocks = "autodoc", doctest examples in documented APIs become interactive REPLs. Set pyrepl_autodoc_packages to install the documented package from a Pyodide-compatible wheel (or PyPI name) and automatically import the documented object before replay:

# conf.py
html_static_path = ["_static"]
pyrepl_autodoc_packages = "_static/wheels/pyrepl_test_pkg-1.0.0-py3-none-any.whl"

Autodoc still imports the package on the host at build time (for example via pip install -e ".[docs]" in this repository).

Source module:

RST content:

.. autofunction:: pyrepl_test_pkg.demo.example_generator

Rendered result:

pyrepl_test_pkg.demo.example_generator(n)

Generators yield values useful for iteration.

Example

Local Pyodide wheels

The same wheel can be referenced manually from .. py-repl:: when you want a standalone REPL without autodoc:

.. py-repl::
   :packages: _static/wheels/pyrepl_test_pkg-1.0.0-py3-none-any.whl
   :no-header:
   :no-banner:

   >>> import pyrepl_test_pkg
   >>> pyrepl_test_pkg.ping()