Skip to content

Commit

Permalink
Build all docs notebooks in test_notebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
opcode81 committed Aug 13, 2024
1 parent 219ba2b commit baf6720
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 9 deletions.
6 changes: 4 additions & 2 deletions docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ only_build_toc_files : true
#######################################################################################
# Execution settings
execute:
execute_notebooks : "auto" # NOTE: Repeat below in `nb_execution_mode`. Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off")
# NOTE: Notebooks are not executed, because test_notebooks.py executes them and stores them with outputs in the docs/ folder
# NOTE: If changed, repeat below in `nb_execution_mode`.
execute_notebooks : "off" # Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off")
cache : "" # A path to the jupyter cache that will be used to store execution artifacts. Defaults to `_build/.jupyter_cache/`
exclude_patterns : [] # A list of patterns to *skip* in execution (e.g. a notebook that takes a really long time)
timeout : 1000 # The maximum time (in seconds) each notebook cell is allowed to run.
Expand Down Expand Up @@ -139,7 +141,7 @@ sphinx:
add_module_names: False
github_username: thu-ml
github_repository: tianshou
nb_execution_mode: "auto"
nb_execution_mode: "off"
nb_merge_streams: True # This is important for cell outputs to appear as single blocks rather than one block per line
python_use_unqualified_type_names: True
nb_mime_priority_overrides: [
Expand Down
51 changes: 44 additions & 7 deletions notebooks/test_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os
import pathlib
from glob import glob
from typing import Dict, Tuple

import nbformat
Expand Down Expand Up @@ -32,7 +33,7 @@ def notebooksUsedInDocs() -> Tuple[Dict[str, str], str]:
for notebook_filename in notebooks_to_copy:
if not os.path.exists(NOTEBOOKS_DIR / notebook_filename):
raise FileNotFoundError(f"Notebook {notebook_filename} does not exist in notebooks directory")
return notebooks_to_copy, str(path)
return notebooks_to_copy, str(path.absolute())
raise Exception("Could not find notebooks directory in docs")


Expand All @@ -49,22 +50,58 @@ def preprocess_cell(self, cell, resources, index):
return super().preprocess_cell(cell, resources, index)


def execute_notebook(notebook_path):
"""
Execute a jupyter notebook and return the executed notebook
:param notebook_path: path to the notebook
:return: the notebook with output cells added
"""
log.info(f"Reading jupyter notebook from {notebook_path}")
with open(notebook_path) as f:
nb = nbformat.read(f, as_version=4)
ep = LoggingExecutePreprocessor(notebook_path, timeout=600)
ep.preprocess(nb, resources={"metadata": {"path": str(NOTEBOOKS_DIR)}})
return nb


@pytest.mark.parametrize(
"notebook", [file for file in os.listdir(NOTEBOOKS_DIR) if file.endswith(".ipynb") and file not in NOTEBOOKS_NOT_TESTED]
)
def test_notebook(notebook):
"""
Tests notebooks in the /notebooks folder, optionally copying the executed notebook to the /docs folder
:param notebook:
:return:
"""
notebook_path = NOTEBOOKS_DIR / notebook
log.info(f"Reading jupyter notebook from {notebook_path}")
with open(notebook_path) as f:
nb = nbformat.read(f, as_version=4)
ep = LoggingExecutePreprocessor(notebook, timeout=600)
ep.preprocess(nb, resources={"metadata": {"path": str(NOTEBOOKS_DIR)}})
nb = execute_notebook(notebook_path)

# saving the executed notebook to docs
if notebook in NOTEBOOKS_TO_COPY:
output_path = os.path.join(DOCS_NOTEBOOKS_DIR, NOTEBOOKS_TO_COPY[notebook])
log.info(f"Saving executed notebook to {output_path} for documentation purposes")
log.info(f"Saving executed notebook to {output_path}")
with open(output_path, "w", encoding="utf-8") as f:
nbformat.write(nb, f)
else:
log.info(f"Notebook {notebook} is not used in docs; not copied")


@pytest.mark.parametrize(
"notebook_path", [p for p in [os.path.abspath(g) for g in glob(f"{DOCS_DIR}/**/*.ipynb")] if not p.startswith(DOCS_NOTEBOOKS_DIR)]
)
def test_build_docs_notebook_non_tutorial(notebook_path):
"""
Process all notebooks in the docs/ folder that are purely documentation, i.e. ones that aren't tutorials which
have been copied to DOCS_NOTEBOOKS_DIR by the other test above.
Runs each notebook and saves it with output cells in-place.
:param notebook_path:
:return:
"""
nb = execute_notebook(notebook_path)
with open(notebook_path, "w", encoding="utf-8") as f:
log.info(f"Saving executed notebook with outputs to {notebook_path}")
nbformat.write(nb, f)

0 comments on commit baf6720

Please sign in to comment.