From 3c8c2bcafe1a4e1cb775ae325b8d0a25e775f686 Mon Sep 17 00:00:00 2001 From: "Peter St. John" Date: Tue, 20 Aug 2024 16:42:32 -0600 Subject: [PATCH] add jupyter notebook support in documentation (#109) * add jupyter notebook support in documentation * add ruff linting and formatting for jupyter notebooks * add the capability for notebook testing --- .devcontainer/devcontainer.json | 3 +- .github/workflows/pre-commit.yml | 2 +- .secrets.baseline | 18 +- .vscode/settings.json | 1 + docs/Dockerfile | 3 +- .../developer-guide/jupyter-notebooks.ipynb | 163 ++++++++++++++++++ docs/mkdocs.yml | 1 + docs/requirements.txt | 1 + requirements-test.txt | 1 + 9 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 docs/docs/developer-guide/jupyter-notebooks.ipynb diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bd18b93291..f745376fe6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -26,7 +26,8 @@ "streetsidesoftware.code-spell-checker", "ms-azuretools.vscode-docker", "charliermarsh.ruff", - "njpwerner.autodocstring" + "njpwerner.autodocstring", + "ms-toolsai.jupyter" ], "settings": { "python.analysis.extraPaths": [ diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index cf52196325..6bbce25366 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -19,7 +19,7 @@ jobs: python-version: '3.10' - uses: pre-commit/action@v3.0.1 - run: pip install -r requirements-dev.txt - - run: ruff check scripts/ sub-packages/ + - run: ruff check scripts/ sub-packages/ docs/ - run: tach check - uses: trufflesecurity/trufflehog@main with: diff --git a/.secrets.baseline b/.secrets.baseline index f5a4028396..b79565cdb9 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -90,6 +90,10 @@ { "path": "detect_secrets.filters.allowlist.is_line_allowlisted" }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, { "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", "min_level": 2 @@ -122,6 +126,16 @@ "path": "detect_secrets.filters.heuristic.is_templated_secret" } ], - "results": {}, - "generated_at": "2024-07-16T22:20:55Z" + "results": { + "docs/docs/developer-guide/jupyter-notebooks.ipynb": [ + { + "type": "Base64 High Entropy String", + "filename": "docs/docs/developer-guide/jupyter-notebooks.ipynb", + "hashed_secret": "9a4fdd92fe53aa1f5be34f8faf864679dfd36481", + "is_verified": true, + "line_number": 83 + } + ] + }, + "generated_at": "2024-08-20T19:17:56Z" } diff --git a/.vscode/settings.json b/.vscode/settings.json index f099449db7..42e72c307f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "allclose", "bionemo", "dtype", + "NBVAL", "nemo", "pretraining", "rampup", diff --git a/docs/Dockerfile b/docs/Dockerfile index 8560a3ae0e..97ee0308b5 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -2,4 +2,5 @@ FROM squidfunk/mkdocs-material:latest # Install plugins. COPY docs/requirements.txt /tmp/ -RUN pip install -r /tmp/requirements.txt +RUN apk add gcc python3-dev musl-dev linux-headers +RUN pip install --disable-pip-version-check --no-cache-dir -r /tmp/requirements.txt diff --git a/docs/docs/developer-guide/jupyter-notebooks.ipynb b/docs/docs/developer-guide/jupyter-notebooks.ipynb new file mode 100644 index 0000000000..0a21282ef6 --- /dev/null +++ b/docs/docs/developer-guide/jupyter-notebooks.ipynb @@ -0,0 +1,163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Jupyter Notebook Support\n", + "\n", + "Jupyter notebooks can be rendered as part of the documentation build system as an alternative to markdown files. The \n", + "docs site uses [mkdocs-jupyter](https://mkdocs-jupyter.danielfrg.com/) to build and render jupyter notebooks as markdown\n", + "files.\n", + "\n", + "*Note*: There are some limitations to jupyter rendering.\n", + "1. Notebooks are not executed as part of the docs publishing pipeline. CI tests to ensure notebook consistency are run\n", + " separately (see [Testing Jupyter Notebooks](#testing-jupyter-notebooks)).\n", + "2. Notebook markdown cells don't support the full range of mkdocs-material configuration, including things like\n", + " admonitions, referencing automatically-generated API documentation via mkdocstrings etc. (more\n", + " [here](https://github.com/squidfunk/mkdocs-material/discussions/4461))." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example code block\n", + "\n", + "Markdown headings can be used to create a TOC similarly to traditional mkdocs pages." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "b = 2\n", + "a + b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Embedded visualizations\n", + "\n", + "We can also embed images using standard approaches to embedding graphics in notebooks." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "xs = np.linspace(0, 2*np.pi, 100)\n", + "plt.plot(xs, np.sin(xs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Testing Jupyter Notebooks\n", + "\n", + "Jupyter notebooks are run as part of the CI build suite using\n", + "[`nbval`](https://github.com/computationalmodelling/nbval/tree/master). To run these tests locally, run\n", + "\n", + "```bash\n", + "pytest --nbval-lax docs/\n", + "```\n", + "\n", + "from the repository root. By default, `nbval` will only check that the notebook executes successfully. To add additional\n", + "checks to ensure the consistency of the output, add a `#NBVAL_CHECK_OUTPUT` marker comment, which will ensure that the\n", + "output of the saved jupyter notebook matches the output when the notebook is executed in CI.\n", + "\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.3.0a0+ebedce2\n" + ] + } + ], + "source": [ + "#NBVAL_CHECK_OUTPUT\n", + "\n", + "import torch\n", + "print(torch.__version__)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 6d87279b79..d557be9ba2 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -51,6 +51,7 @@ plugins: python: paths: - ../sub-packages/*/src + - mkdocs-jupyter markdown_extensions: - pymdownx.snippets: diff --git a/docs/requirements.txt b/docs/requirements.txt index 5fcd3cfa3f..6754b791b3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ mkdocs-minify-plugin mkdocstrings[python] mkdocs-gen-files pymdown-extensions +mkdocs-jupyter diff --git a/requirements-test.txt b/requirements-test.txt index 063e40bff6..7eb0bedc4f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -5,3 +5,4 @@ testbook==0.4.2 requests_mock==1.11.0 # For SwiftStack access awscli==1.33.33 +nbval==0.11.0