Source code for pydoit_nb.notebook_run
"""
Notebook running
"""
from __future__ import annotations
import logging
import sys
from pathlib import Path
from typing import Any, Callable
import jupytext
import papermill
logger = logging.getLogger(__name__)
_PYTHON_ADD_NOTE_INTRODUCED: int = 11
"""Python3 minor version in which add_note was introduced"""
[docs]class NotebookExecutionException(Exception):
"""
Raised when a notebook fails to execute for any reason
"""
def __init__(self, exc: Exception, filename: Path):
note = f"{filename} failed to execute. Original exception: {exc}"
if sys.version_info[1] >= _PYTHON_ADD_NOTE_INTRODUCED:
self.add_note(note) # type: ignore # can be removed once we apply mypy with Python 3.11
super().__init__(exc)
[docs]def rewrite_notebook_default(
raw_notebook: Path,
unexecuted_notebook: Path,
fmt: str = "ipynb",
) -> None:
"""
Re-write notebooks from raw to unexecuted
This is our default implementation to use in :func:`run_notebook`.
Parameters
----------
raw_notebook
Path from which to read the raw notebook
unexecuted_notebook
Path in which to write the unexecuted notebook
fmt
Format in which to write the unexecuted notebook
"""
logger.info("Reading raw notebook with jupytext: %s", raw_notebook)
notebook_jupytext = jupytext.read(raw_notebook)
logger.info("Writing unexecuted notebook: %s", unexecuted_notebook)
# TODO: consider whether this should be elsewhere
unexecuted_notebook.parent.mkdir(parents=True, exist_ok=True)
jupytext.write(
notebook_jupytext,
unexecuted_notebook,
fmt=fmt,
)
[docs]def run_notebook( # noqa: PLR0913
raw_notebook: Path,
unexecuted_notebook: Path,
executed_notebook: Path,
notebook_parameters: dict[str, str] | None = None,
notebook_rewriter: Callable[[Path, Path], None] | None = None,
notebook_executor: Callable[[Path, Path, dict[str, Any]], Any] | None = None,
) -> None:
"""
Run a notebook
This loads the notebook ``raw_notebook`` using jupytext, then writes it
as an ``.ipynb`` file to ``unexecuted_notebook``. It then runs this
unexecuted notebook with papermill, writing it to ``executed_notebook``.
Parameters
----------
raw_notebook
Notebook from which to start
unexecuted_notebook
Where to write the unexecuted notebook
executed_notebook
Where to write the executed notebook
notebook_parameters
Parameters to pass to the target notebook
These parameters will replace the contents of a cell tagged "parameters".
See the
`papermill documentation <https://papermill.readthedocs.io/en/latest/usage-parameterize.html#designate-parameters-for-a-cell>`_
for more information about parameterizing a notebook.
notebook_rewriter
Function to use to re-write the raw notebooks as unexecuted notebooks
that can be executed by `notebook_executor`. We use jupytext for this
(specifically :func:`rewrite_notebook_default`) by default and you
shouldn't need to change this.
notebook_executor
Function to use to execute the notebooks. We use `papermill.execute_notebook`
for this by default and you shouldn't need to change this.
"""
if notebook_parameters is None:
notebook_parameters = {}
if notebook_rewriter is None:
notebook_rewriter = rewrite_notebook_default
if notebook_executor is None:
notebook_executor = papermill.execute_notebook
notebook_rewriter(raw_notebook, unexecuted_notebook)
try:
if not executed_notebook.parent.exists():
logger.info(
"Creating directory (and any required parent directories): %s",
executed_notebook.parent,
)
executed_notebook.parent.mkdir(parents=True, exist_ok=True)
logger.info("Executing notebook: %s", unexecuted_notebook)
notebook_executor(
unexecuted_notebook,
executed_notebook,
notebook_parameters,
)
except Exception as exc:
raise NotebookExecutionException(exc, unexecuted_notebook) from exc