From b4147916fda779156c2c061a0851ba044a230554 Mon Sep 17 00:00:00 2001 From: Nicholas Ceglia Date: Fri, 5 Jun 2026 15:39:39 -0400 Subject: [PATCH] docs: make ReadTheDocs build deps-free and reliable (D6) The RTD build installed the package with its full ML stack (torch, scvi-tools, scanpy, ...) and then let autodoc import it - slow and prone to out-of-memory failures on RTD, and it never actually worked with the new D11 autodoc. Build the API reference from source with the heavy deps mocked instead: - conf.py: autodoc_mock_imports for torch/pyro/scvi/sklearn/scanpy/anndata/ scipy/matplotlib/seaborn/mpltern/umap/tqdm/daft/gseapy (numpy/pandas kept real for intersphinx). autodoc still reads TCRi's own signatures/docstrings. - conf.py: autodoc_class_signature = 'separated' so TCRIModel's constructor renders its real __init__ signature instead of the (*args, **kwargs) that a mocked scvi base class would otherwise inject. - conf.py: resolve version from installed metadata, falling back to parsing pyproject.toml (correct even without an install). - .readthedocs.yaml: bump python 3.9 -> 3.12 (pyproject requires >=3.10), drop the `pip install .` step (no heavy deps), HTML-only formats. - docs/requirements.txt: trim to the Sphinx toolchain + numpy/pandas; pin sphinx>=7,<9, sphinx-rtd-theme>=2.0, myst-parser>=2.0. - delete the stale duplicate docs/.readthedocs.yaml (RTD uses the repo-root one). Verified locally with mocks engaged (sphinx's mock finder shadows the installed torch/scvi), reproducing the deps-free RTD path: build succeeded, 0 warnings. Real API renders (joint_distribution_posterior, save_tcri_session, and TCRIModel.__init__ with n_latent=128); version resolves to 0.1.0. Closes Notion D6. Co-Authored-By: Claude Opus 4.8 --- .readthedocs.yaml | 16 ++++++------ docs/.readthedocs.yaml | 21 ---------------- docs/conf.py | 55 +++++++++++++++++++++++++++++++++++------- docs/requirements.txt | 16 ++++++------ 4 files changed, 62 insertions(+), 46 deletions(-) delete mode 100644 docs/.readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 95f1051..7264327 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,21 +1,19 @@ -# .readthedocs.yaml -# Read the Docs configuration file +# .readthedocs.yaml — Read the Docs build configuration +# +# The heavy runtime dependencies (torch, scvi-tools, scanpy, pyro, ...) are NOT +# installed here. They are mocked at build time via ``autodoc_mock_imports`` in +# docs/conf.py, so the API reference builds from source without pulling the full +# ML stack — which keeps the RTD build fast and avoids out-of-memory failures. version: 2 build: os: ubuntu-22.04 tools: - python: "3.9" + python: "3.12" python: install: - requirements: docs/requirements.txt - - method: pip - path: . sphinx: configuration: docs/conf.py - -formats: - - pdf - - epub diff --git a/docs/.readthedocs.yaml b/docs/.readthedocs.yaml deleted file mode 100644 index 95f1051..0000000 --- a/docs/.readthedocs.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# .readthedocs.yaml -# Read the Docs configuration file -version: 2 - -build: - os: ubuntu-22.04 - tools: - python: "3.9" - -python: - install: - - requirements: docs/requirements.txt - - method: pip - path: . - -sphinx: - configuration: docs/conf.py - -formats: - - pdf - - epub diff --git a/docs/conf.py b/docs/conf.py index b7c355b..aa8d722 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,12 +12,26 @@ author = 'Nicholas Ceglia' # The full version, including alpha/beta/rc tags. -# Single source of truth is pyproject.toml; read it back via installed metadata. -try: - from importlib.metadata import version as _pkg_version - release = _pkg_version("tcri") -except Exception: - release = "0.0.0" +# Single source of truth is pyproject.toml. Prefer installed metadata; fall back +# to parsing pyproject.toml directly so the version is correct even when the +# package is not installed in the docs environment (e.g. on ReadTheDocs, where +# the heavy runtime deps are mocked rather than installed). +def _get_release(): + try: + from importlib.metadata import version as _pkg_version + return _pkg_version("tcri") + except Exception: + pass + try: + import tomllib # Python 3.11+ + _pp = os.path.join(os.path.dirname(__file__), os.pardir, "pyproject.toml") + with open(_pp, "rb") as _f: + return tomllib.load(_f)["project"]["version"] + except Exception: + return "0.0.0" + + +release = _get_release() version = release # -- General configuration --------------------------------------------------- @@ -80,6 +94,11 @@ # -- Options for autodoc extension ------------------------------------------- autodoc_member_order = 'bysource' autodoc_typehints = 'description' +# Read constructor signatures from ``__init__`` directly. Without this, classes +# whose (mocked) base injects ``__new__(*args, **kwargs)`` — e.g. TCRIModel via +# scvi's BaseModelClass — would render as ``TCRIModel(*args, **kwargs)`` on the +# deps-mocked ReadTheDocs build. +autodoc_class_signature = 'separated' autodoc_default_options = { 'members': True, 'undoc-members': True, @@ -90,9 +109,27 @@ add_module_names = False # Generate stub pages for any autosummary directives. autosummary_generate = True -# Don't fail the whole build if an optional/heavy import is unavailable at -# doc-build time; autodoc will note the missing object instead. -autodoc_mock_imports = [] +# Mock the heavy / native scientific dependencies so the API reference can be +# built from source without installing the full ML stack (torch, scvi-tools, +# scanpy, ...). autodoc still reads the real signatures and docstrings of TCRi's +# own code; only third-party imports are stubbed. numpy and pandas are kept real +# (they're light and let intersphinx resolve their types). +autodoc_mock_imports = [ + "torch", + "pyro", + "scvi", + "sklearn", + "scanpy", + "anndata", + "scipy", + "matplotlib", + "seaborn", + "mpltern", + "umap", + "tqdm", + "daft", + "gseapy", +] # -- Options for napoleon extension ------------------------------------------ napoleon_google_docstring = True diff --git a/docs/requirements.txt b/docs/requirements.txt index e94e2d6..d9f8caf 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,9 +1,11 @@ -sphinx>=4.0.0 -sphinx_rtd_theme>=1.0.0 -myst-parser>=0.15.0 -scipy +# Documentation build dependencies — intentionally light. +# +# The package's heavy runtime deps (torch, scvi-tools, scanpy, pyro, mpltern, +# ...) are mocked via autodoc_mock_imports in conf.py, so they are NOT listed +# here. Only the Sphinx toolchain plus numpy/pandas (kept real for clean +# intersphinx type links) are needed. +sphinx>=7,<9 +sphinx-rtd-theme>=2.0 +myst-parser>=2.0 numpy -scanpy pandas -matplotlib -seaborn