Skip to content

Auto-import documented objects into autodoc doctest REPLs#18

Merged
chrizzFTD merged 1 commit into
cursor/autodoc-packages-config-c412from
cursor/autodoc-import-bootstrap-d63f
Jul 1, 2026
Merged

Auto-import documented objects into autodoc doctest REPLs#18
chrizzFTD merged 1 commit into
cursor/autodoc-packages-config-c412from
cursor/autodoc-import-bootstrap-d63f

Conversation

@chrizzFTD

Copy link
Copy Markdown
Owner

Summary

Completes the autodoc REPL workflow from PR #17 by automatically importing the documented object into the browser REPL namespace before doctest replay.

When pyrepl_autodoc_packages is set, each autodoc doctest REPL now gets a silent generated bootstrap script (emitted as src=) derived from autodoc desc_signature metadata. Doctest examples can keep using unqualified names like example_generator without changing docstrings.

Problem

PR #17 installs the documented package via micropip.install(), but replay still failed with:

NameError: name 'example_generator' is not defined

Wheel install only makes import package possible; it does not bind documented names into the REPL globals.

Solution

At build time, derive an import statement from autodoc metadata:

Documented object Generated bootstrap
autofunction:: pkg.mod.func from pkg.mod import func
autoclass:: pkg.Widget from pkg import Widget
methods / nested names from pkg import Outer (first segment of fullname)
automodule:: pkg.mod import pkg.mod

Bootstrap scripts are written to _static/pyrepl/{docname}-{n}-bootstrap.py and run silently before interactive replay.

Example output

For pyrepl_test_pkg.demo.example_generator:

<py-repl src="_static/pyrepl/example-1-bootstrap.py"
         packages="_static/wheels/pyrepl_test_pkg-1.0.0-py3-none-any.whl"
         no-header no-banner
         replay-src="_static/pyrepl/example-1.py"></py-repl>

Bootstrap (example-1-bootstrap.py):

from pyrepl_test_pkg.demo import example_generator

Replay (example-1.py, unchanged):

print([i for i in example_generator(4)])

Design notes

  • Always on when packages are configured — no separate pyrepl_autodoc_import option
  • Wheels only — bootstrap is only generated when pyrepl_autodoc_packages is set
  • No vendored JS changes — reuses existing silent :src: bootstrap in pyrepl-web
  • Replay stays faithful — imports are not prepended to doctest replay scripts

Tests

  • tests/test_autodoc_bootstrap_source.py — unit tests for import derivation
  • Updated tests/test_autodoc_bootstrap.py — asserts bootstrap src=, metadata, and written files for function and class cases
  • Existing tests confirm no bootstrap when pyrepl_autodoc_packages is unset
Open in Web Open in Cursor 

When pyrepl_autodoc_packages is set, generate a silent bootstrap script
that imports the documented object from autodoc desc_signature metadata
before replaying doctest input. This lets unqualified doctest names like
example_generator work without changing docstrings.

Bootstrap scripts are written alongside replay files under
_static/pyrepl/ and emitted as src= on autodoc py-repl widgets.

Co-authored-by: chrizzftd <chrizzFTD@users.noreply.github.com>
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 sphinx-pyrepl-web | 🛠️ Build #33394125 | 📁 Comparing be17b64 against latest (931ed8b)

  🔍 Preview build  

3 files changed
± example.html
± genindex.html
± index.html

@chrizzFTD

Copy link
Copy Markdown
Owner Author

@cursoragent review

@cursor

cursor Bot commented Jul 1, 2026

Copy link
Copy Markdown

PR Review: Auto-import documented objects into autodoc doctest REPLs (#18)

Verdict: Approve with minor notes. The change is focused, fits existing machinery, and solves the real gap left by PR #17. RTD preview built successfully.


What works well

Correct problem/solution split. Wheel install (packages=) and namespace setup (src= bootstrap) are separate concerns, composed in the right runtime order (install → silent exec → replay). That matches how pyrepl-web already works.

Minimal, testable surface area. Import derivation is isolated in autodoc_bootstrap_source():

def autodoc_bootstrap_source(
    module: str | None,
    fullname: str | None,
    objtype: str | None,
) -> str | None:
    """Return a silent import script for the documented autodoc object."""
    if not module:
        return None

    if objtype == "module" or not fullname:
        return f"import {module}\n"

    if "." in fullname:
        root = fullname.split(".", 1)[0]
        return f"from {module} import {root}\n"

    return f"from {module} import {fullname}\n"

Replay stays faithful. Imports go into a separate bootstrap file, not prepended to replay scripts — doctest docstrings remain unchanged and the interactive UX only shows doctest input.

Gating is sensible. Bootstrap only when both autodoc desc and pyrepl_autodoc_packages are present. No packages → replay-only (existing behavior).

Good test coverage for the happy path.

Case Covered
autofunction + wheel test_autodoc_packages_emits_configured_wheel
autoclass + wheel test_autodoc_packages_for_out_of_tree_module
No packages test_autodoc_without_packages_is_replay_only
Import derivation units test_autodoc_bootstrap_source.py

The example_generator case is asserted end-to-end: HTML src=, metadata, written file, replay content unchanged.


Minor issues / suggestions

1. Wheel must match the documented package (document, don’t block merge)

test_autodoc_packages_for_out_of_tree_module documents installed_pkg.Widget but installs the pyrepl_test_pkg wheel. Bootstrap correctly generates from installed_pkg import Widget, but runtime would fail unless the wheel actually contains installed_pkg. That’s fine for a build-time test, but worth a one-line README note: the wheel must be the documented package (or include it).

2. Bootstrap copy is nested under replay metadata loop

    replay_dest = outdir / "_static" / "pyrepl"
    for docname, metadata in app.env.metadata.items():
        raw = metadata.get(REPLAY_FILES_KEY)
        if not raw:
            continue
        ...
        raw_bootstrap = metadata.get(BOOTSTRAP_FILES_KEY)
        if raw_bootstrap:
            bootstrap_files = json.loads(raw_bootstrap)
            for name, content in bootstrap_files.items():
                (replay_dest / name).write_text(content, encoding="utf-8")

Bootstrap files are only written when replay files exist for that docname. That’s always true today because they’re registered together, but a separate loop over BOOTSTRAP_FILES_KEY would be more robust if registration ever diverges. Low priority.

3. Known edge cases (acceptable for v1, worth documenting)

Scenario Bootstrap Likely doctest expectation
Nested inner class (Outer.Inner) from mod import Outer Bare Inner() may still fail
Instance methods using self imports class only needs instance in doctest
autodata / module-level constants from mod import name usually fine

None of these are regressions; they’re inherent limits of metadata-only import guessing. A short “Limitations” bullet in the PR or README would help.

4. Optional test gap (non-blocking)

No test for pyrepl_doctest_blocks = "all" with packages set — confirming autodoc blocks get bootstrap while plain RST doctests do not. Behavior looks correct from the code path (desc is not None), but an assertion would lock it in.

5. Silent bootstrap failures are invisible to users

If the import fails at runtime (wrong wheel, missing symbol), pyrepl-web suppresses bootstrap output and the user hits NameError on replay. Consider a follow-up Sphinx warning when bootstrap is generated for a module not obviously covered by pyrepl_autodoc_packages — not required for this PR.


Code quality

  • register_autodoc_repl returning (path, name) is a clean pairing mechanism.
  • Bootstrap naming (index-1-bootstrap.py) is predictable and collision-free.
  • No vendored JS changes — good scope discipline.
  • Docs/README updates accurately describe install + auto-import.

Summary

Area Rating
Correctness (stated goal)
Scope / complexity
Tests ✅ (minor gaps)
Docs
Runtime edge cases ⚠️ documented limitations

Recommendation: merge once PR #17 lands (or merge stacked). The example_generator NameError is fixed cleanly without docstring changes, using autodoc metadata and existing silent :src: bootstrap — exactly what was planned.

Optional follow-ups (separate PRs): limitations docs, all+packages test, runtime mismatch warning.

Open in Web Open in Cursor 

@chrizzFTD
chrizzFTD marked this pull request as ready for review July 1, 2026 21:52
@chrizzFTD
chrizzFTD merged commit 45128d7 into cursor/autodoc-packages-config-c412 Jul 1, 2026
4 checks passed
@chrizzFTD
chrizzFTD deleted the cursor/autodoc-import-bootstrap-d63f branch July 1, 2026 21:59
cursor Bot pushed a commit that referenced this pull request Jul 1, 2026
PR #18 merged auto-import bootstrap that emits silent src= bootstrap
scripts alongside packages=. The prior cleanup commit removed the src
parameter from make_pyrepl_raw, causing TypeError in transform_doctest_blocks
and failing CI/RTD builds.

Co-authored-by: chrizzftd <chrizzFTD@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants