Summary
sphinx-pyrepl-web emits site-relative static paths (_static/...) in <py-repl> attributes (packages, src, replay-src). This works on flat doc layouts (e.g. index.html at the site root) but breaks on nested Sphinx pages (e.g. api/module.html).
Normalize emitted paths to root-absolute form (/_static/...) at build time so REPLs work regardless of page depth.
Problem
The extension currently writes paths like:
<py-repl packages="_static/wheels/foo.whl"
src="_static/pyrepl/index-1-bootstrap.py"
replay-src="_static/pyrepl/index-1.py"></py-repl>
At runtime, pyrepl-web resolves these against document.baseURI:
| Page |
document.baseURI |
Resolved _static/wheels/foo.whl |
Actual asset location |
index.html |
https://docs.example/en/latest/index.html |
https://docs.example/en/latest/_static/wheels/foo.whl |
✓ |
api/module.html |
https://docs.example/en/latest/api/module.html |
https://docs.example/en/latest/api/_static/wheels/foo.whl |
✗ (404) |
The same failure affects:
packages= → micropip.install() cannot fetch the wheel
src= → silent bootstrap import script fails to load
replay-src= → doctest replay script fails to load
pyrepl-web PR #5 adds runtime URL resolution for packages= via new URL(pkg, document.baseURI), which fixes flat layouts but not nested pages — root-absolute paths (/_static/...) resolve correctly from any page depth.
This was called out as follow-up work in #15 and deferred for initial shipping because current consumers (naming, costa) use flat layouts.
Motivation
- Enable sphinx-pyrepl-web on standard multi-page Sphinx projects (
docs/api/..., docs/guide/...)
- Avoid requiring every consumer to hand-author
/_static/... paths in RST/config
- One consistent fix for all emitted static references (wheels, bootstrap scripts, replay scripts)
Proposed implementation
Add a small helper in sphinx_pyrepl_web/__init__.py, e.g. normalize_static_ref(path: str) -> str:
def normalize_static_ref(path: str) -> str:
if path.startswith(("/", "http://", "https://", "emfs:")):
return path
if path.startswith("_static/"):
return "/" + path
return path
Apply when emitting attributes in:
make_pyrepl_raw() — packages, src, replay-src
register_autodoc_repl() / register_autodoc_bootstrap() return values
PyRepl directive (:src:, :packages:) if not already root-absolute
Expected output after fix:
<py-repl packages="/_static/wheels/foo.whl"
src="/_static/pyrepl/index-1-bootstrap.py"
replay-src="/_static/pyrepl/index-1.py"></py-repl>
Alternative (more complex): compute ../ depth from docname and emit relative paths. Root-absolute is simpler and matches pyrepl-web's existing resolution behavior for leading /.
Test plan
-
Unit test — normalize_static_ref():
_static/wheels/foo.whl → /_static/wheels/foo.whl
/_static/wheels/foo.whl → unchanged
numpy / https://cdn.example/w.whl → unchanged
-
Build test — nested page fixture:
docs/api/index.rst with .. py-repl:: :packages: _static/wheels/foo.whl
- Assert
api/index.html contains packages="/_static/wheels/foo.whl" (not api/_static/...)
-
Regression — flat index.html layout still works
Out of scope
- Changing pyrepl-web runtime resolution (already handles
/_static/... correctly)
- Normalizing user-provided paths outside
_static/ (e.g. demo.py for :src:)
Acceptance criteria
Related
Summary
sphinx-pyrepl-web emits site-relative static paths (
_static/...) in<py-repl>attributes (packages,src,replay-src). This works on flat doc layouts (e.g.index.htmlat the site root) but breaks on nested Sphinx pages (e.g.api/module.html).Normalize emitted paths to root-absolute form (
/_static/...) at build time so REPLs work regardless of page depth.Problem
The extension currently writes paths like:
At runtime, pyrepl-web resolves these against
document.baseURI:document.baseURI_static/wheels/foo.whlindex.htmlhttps://docs.example/en/latest/index.htmlhttps://docs.example/en/latest/_static/wheels/foo.whlapi/module.htmlhttps://docs.example/en/latest/api/module.htmlhttps://docs.example/en/latest/api/_static/wheels/foo.whlThe same failure affects:
packages=→micropip.install()cannot fetch the wheelsrc=→ silent bootstrap import script fails to loadreplay-src=→ doctest replay script fails to loadpyrepl-web PR #5 adds runtime URL resolution for
packages=vianew URL(pkg, document.baseURI), which fixes flat layouts but not nested pages — root-absolute paths (/_static/...) resolve correctly from any page depth.This was called out as follow-up work in #15 and deferred for initial shipping because current consumers (naming, costa) use flat layouts.
Motivation
docs/api/...,docs/guide/...)/_static/...paths in RST/configProposed implementation
Add a small helper in
sphinx_pyrepl_web/__init__.py, e.g.normalize_static_ref(path: str) -> str:Apply when emitting attributes in:
make_pyrepl_raw()—packages,src,replay-srcregister_autodoc_repl()/register_autodoc_bootstrap()return valuesPyRepldirective (:src:,:packages:) if not already root-absoluteExpected output after fix:
Alternative (more complex): compute
../depth fromdocnameand emit relative paths. Root-absolute is simpler and matches pyrepl-web's existing resolution behavior for leading/.Test plan
Unit test —
normalize_static_ref():_static/wheels/foo.whl→/_static/wheels/foo.whl/_static/wheels/foo.whl→ unchangednumpy/https://cdn.example/w.whl→ unchangedBuild test — nested page fixture:
docs/api/index.rstwith.. py-repl:: :packages: _static/wheels/foo.whlapi/index.htmlcontainspackages="/_static/wheels/foo.whl"(notapi/_static/...)Regression — flat
index.htmllayout still worksOut of scope
/_static/...correctly)_static/(e.g.demo.pyfor:src:)Acceptance criteria
normalize_static_ref()applied to all_static/...paths emitted by the extension_static/paths are normalized automatically (optional)Related
resolvePackageRefs()in browser