mkdocs-jupyterlite 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mkdocs_jupyterlite-0.1.0/.github/workflows/docs.yml +28 -0
- mkdocs_jupyterlite-0.1.0/.github/workflows/publish.yml +30 -0
- mkdocs_jupyterlite-0.1.0/.gitignore +12 -0
- mkdocs_jupyterlite-0.1.0/PKG-INFO +55 -0
- mkdocs_jupyterlite-0.1.0/README.md +39 -0
- mkdocs_jupyterlite-0.1.0/docs/index.md +39 -0
- mkdocs_jupyterlite-0.1.0/docs/notebook.ipynb +57 -0
- mkdocs_jupyterlite-0.1.0/mkdocs.yml +14 -0
- mkdocs_jupyterlite-0.1.0/pyproject.toml +36 -0
- mkdocs_jupyterlite-0.1.0/src/mkdocs_jupyterlite/__init__.py +13 -0
- mkdocs_jupyterlite-0.1.0/src/mkdocs_jupyterlite/_build.py +74 -0
- mkdocs_jupyterlite-0.1.0/src/mkdocs_jupyterlite/_plugin.py +162 -0
- mkdocs_jupyterlite-0.1.0/src/mkdocs_jupyterlite/py.typed +0 -0
- mkdocs_jupyterlite-0.1.0/uv.lock +1618 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
|
|
16
|
+
- name: Configure Git Credentials
|
|
17
|
+
run: |
|
|
18
|
+
git config user.name github-actions[bot]
|
|
19
|
+
git config user.email github-actions[bot]@users.noreply.github.com
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
|
|
25
|
+
- name: Install UV
|
|
26
|
+
run: curl -LsSf https://astral.sh/uv/0.4.18/install.sh | sh
|
|
27
|
+
|
|
28
|
+
- run: uv run mkdocs gh-deploy
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
name: publish
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
jobs:
|
|
6
|
+
pypi-publish:
|
|
7
|
+
name: Upload release to PyPI
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
environment:
|
|
10
|
+
name: pypi
|
|
11
|
+
url: https://pypi.org/p/<your-pypi-project-name>
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
|
|
23
|
+
- name: Install UV
|
|
24
|
+
run: curl -LsSf https://astral.sh/uv/0.4.18/install.sh | sh
|
|
25
|
+
|
|
26
|
+
- name: Build package distributions
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Publish package distributions to PyPI
|
|
30
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mkdocs-jupyterlite
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Embed interactive JupyterLite notebooks in your MkDocs site.
|
|
5
|
+
Project-URL: Documentation, https://github.com/NickCrews/mkdocs-jupyterlite#readme
|
|
6
|
+
Project-URL: Homepage, https://github.com/NickCrews/mkdocs-jupyterlite
|
|
7
|
+
Project-URL: Source, https://github.com/NickCrews/mkdocs-jupyterlite
|
|
8
|
+
Project-URL: Tracker, https://github.com/NickCrews/mkdocs-jupyterlite/issues
|
|
9
|
+
Author-email: Nick Crews <nicholas.b.crews@gmail.com>
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: jupyterlab-server>=2.27.3
|
|
12
|
+
Requires-Dist: jupyterlite-core>=0.6.4
|
|
13
|
+
Requires-Dist: jupyterlite-pyodide-kernel>=0.6.1
|
|
14
|
+
Requires-Dist: mkdocs>=1.6.1
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# mkdocs-jupyterlite
|
|
18
|
+
|
|
19
|
+
A MkDocs plugin that enables embedding interactive jupyterlite notebooks in your docs.
|
|
20
|
+
|
|
21
|
+
Say you have a notebook `example.ipynb` in your awesome project, and you want
|
|
22
|
+
users to be able to play around with it.
|
|
23
|
+
In the past, you could use a tool like [Binder](https://mybinder.org/) to achieve this.
|
|
24
|
+
But, that requires a full docker environment and a remote server.
|
|
25
|
+
By using [JupyterLite](https://jupyterlite.readthedocs.io/),
|
|
26
|
+
you can run Jupyter notebooks directly in the browser without any server-side dependencies.
|
|
27
|
+
|
|
28
|
+
However, to use jupyterlite, you have to manually install jupyterlite and
|
|
29
|
+
then run a build step to package your notebooks, other files, and python
|
|
30
|
+
dependencies into a single static site.
|
|
31
|
+
|
|
32
|
+
This plugin automates that process for you.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
1. Install the plugin
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install mkdocs-jupyterlite
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. Configure in your `mkdocs.yml` file
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
plugins:
|
|
46
|
+
- search
|
|
47
|
+
- mkdocstrings
|
|
48
|
+
- etc
|
|
49
|
+
- jupyterlite:
|
|
50
|
+
enabled: true
|
|
51
|
+
notebook_patterns:
|
|
52
|
+
- "**/*.ipynb"
|
|
53
|
+
pip_urls:
|
|
54
|
+
- "https://pypi.org/simple"
|
|
55
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# mkdocs-jupyterlite
|
|
2
|
+
|
|
3
|
+
A MkDocs plugin that enables embedding interactive jupyterlite notebooks in your docs.
|
|
4
|
+
|
|
5
|
+
Say you have a notebook `example.ipynb` in your awesome project, and you want
|
|
6
|
+
users to be able to play around with it.
|
|
7
|
+
In the past, you could use a tool like [Binder](https://mybinder.org/) to achieve this.
|
|
8
|
+
But, that requires a full docker environment and a remote server.
|
|
9
|
+
By using [JupyterLite](https://jupyterlite.readthedocs.io/),
|
|
10
|
+
you can run Jupyter notebooks directly in the browser without any server-side dependencies.
|
|
11
|
+
|
|
12
|
+
However, to use jupyterlite, you have to manually install jupyterlite and
|
|
13
|
+
then run a build step to package your notebooks, other files, and python
|
|
14
|
+
dependencies into a single static site.
|
|
15
|
+
|
|
16
|
+
This plugin automates that process for you.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
1. Install the plugin
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install mkdocs-jupyterlite
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. Configure in your `mkdocs.yml` file
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
plugins:
|
|
30
|
+
- search
|
|
31
|
+
- mkdocstrings
|
|
32
|
+
- etc
|
|
33
|
+
- jupyterlite:
|
|
34
|
+
enabled: true
|
|
35
|
+
notebook_patterns:
|
|
36
|
+
- "**/*.ipynb"
|
|
37
|
+
pip_urls:
|
|
38
|
+
- "https://pypi.org/simple"
|
|
39
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# mkdocs-jupyterlite
|
|
2
|
+
|
|
3
|
+
A MkDocs plugin that enables embedding interactive jupyterlite notebooks in your docs.
|
|
4
|
+
|
|
5
|
+
Say you have a notebook `example.ipynb` in your awesome project, and you want
|
|
6
|
+
users to be able to play around with it.
|
|
7
|
+
In the past, you could use a tool like [Binder](https://mybinder.org/) to achieve this.
|
|
8
|
+
But, that requires a full docker environment and a remote server.
|
|
9
|
+
By using [JupyterLite](https://jupyterlite.readthedocs.io/),
|
|
10
|
+
you can run Jupyter notebooks directly in the browser without any server-side dependencies.
|
|
11
|
+
|
|
12
|
+
However, to use jupyterlite, you have to manually install jupyterlite and
|
|
13
|
+
then run a build step to package your notebooks, other files, and python
|
|
14
|
+
dependencies into a single static site.
|
|
15
|
+
|
|
16
|
+
This plugin automates that process for you.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Step 1: Install the `mkdocs-jupyterlite` package
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install mkdocs-jupyterlite
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Step 2: Configure your `mkdocs.yml` file
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
plugins:
|
|
30
|
+
- search
|
|
31
|
+
- mkdocstrings
|
|
32
|
+
- etc
|
|
33
|
+
- jupyterlite:
|
|
34
|
+
enabled: true
|
|
35
|
+
notebook_patterns:
|
|
36
|
+
- "**/*.ipynb"
|
|
37
|
+
pip_urls:
|
|
38
|
+
- "https://pypi.org/simple"
|
|
39
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "e311f1e4",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Notebook 1"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"cell_type": "code",
|
|
13
|
+
"execution_count": null,
|
|
14
|
+
"id": "2de0b9fe",
|
|
15
|
+
"metadata": {
|
|
16
|
+
"vscode": {
|
|
17
|
+
"languageId": "plaintext"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"outputs": [],
|
|
21
|
+
"source": [
|
|
22
|
+
"x = 1"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"cell_type": "markdown",
|
|
27
|
+
"id": "a6b80ff4",
|
|
28
|
+
"metadata": {},
|
|
29
|
+
"source": [
|
|
30
|
+
"## Heading 2"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"cell_type": "code",
|
|
35
|
+
"execution_count": null,
|
|
36
|
+
"id": "28f91d4a",
|
|
37
|
+
"metadata": {
|
|
38
|
+
"vscode": {
|
|
39
|
+
"languageId": "plaintext"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"outputs": [],
|
|
43
|
+
"source": [
|
|
44
|
+
"import ipywidgets\n",
|
|
45
|
+
"\n",
|
|
46
|
+
"ipywidgets.IntSlider()"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"metadata": {
|
|
51
|
+
"language_info": {
|
|
52
|
+
"name": "python"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"nbformat": 4,
|
|
56
|
+
"nbformat_minor": 5
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
site_name: mkdocs-jupyterlite
|
|
2
|
+
site_url: https://nickcrews.github.io/mkdocs-jupyterlite/
|
|
3
|
+
repo_url: https://github.com/nickcrews/mkdocs-jupyterlite/
|
|
4
|
+
|
|
5
|
+
nav:
|
|
6
|
+
- Home: index.md
|
|
7
|
+
- Notebook 1: notebook.ipynb
|
|
8
|
+
|
|
9
|
+
plugins:
|
|
10
|
+
- jupyterlite:
|
|
11
|
+
enabled: true
|
|
12
|
+
notebook_patterns:
|
|
13
|
+
- '*.ipynb'
|
|
14
|
+
pip_urls: []
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mkdocs-jupyterlite"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Embed interactive JupyterLite notebooks in your MkDocs site."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Nick Crews", email = "nicholas.b.crews@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"jupyterlab-server>=2.27.3",
|
|
12
|
+
"jupyterlite-core>=0.6.4",
|
|
13
|
+
"jupyterlite-pyodide-kernel>=0.6.1",
|
|
14
|
+
"mkdocs>=1.6.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Documentation = "https://github.com/NickCrews/mkdocs-jupyterlite#readme"
|
|
19
|
+
Homepage = "https://github.com/NickCrews/mkdocs-jupyterlite"
|
|
20
|
+
Source = "https://github.com/NickCrews/mkdocs-jupyterlite"
|
|
21
|
+
Tracker = "https://github.com/NickCrews/mkdocs-jupyterlite/issues"
|
|
22
|
+
|
|
23
|
+
[project.entry-points."mkdocs.plugins"]
|
|
24
|
+
jupyterlite = "mkdocs_jupyterlite:JupyterlitePlugin"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["hatchling"]
|
|
28
|
+
build-backend = "hatchling.build"
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=8.4.1",
|
|
33
|
+
]
|
|
34
|
+
lint = [
|
|
35
|
+
"ruff>=0.12.8",
|
|
36
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
from mkdocs_jupyterlite._plugin import JupyterlitePlugin as JupyterlitePlugin
|
|
5
|
+
from mkdocs_jupyterlite._plugin import (
|
|
6
|
+
JupyterlitePluginConfig as JupyterlitePluginConfig,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
__version__ = importlib.metadata.version(__name__)
|
|
11
|
+
except importlib.metadata.PackageNotFoundError as e:
|
|
12
|
+
warnings.warn(f"Could not determine version of {__name__}\n{e!s}", stacklevel=2)
|
|
13
|
+
__version__ = "unknown"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger("mkdocs.plugins.jupyterlite")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_site(
|
|
13
|
+
*,
|
|
14
|
+
notebooks: Iterable[Path],
|
|
15
|
+
pip_urls: Iterable[str],
|
|
16
|
+
output_dir: Path,
|
|
17
|
+
) -> None:
|
|
18
|
+
shutil.rmtree(output_dir, ignore_errors=True)
|
|
19
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
with tempfile.TemporaryDirectory() as working_dir_str:
|
|
21
|
+
working_dir = Path(working_dir_str)
|
|
22
|
+
write_jupyter_lite_config(
|
|
23
|
+
out_path=working_dir / "jupyter_lite_config.json",
|
|
24
|
+
pip_urls=pip_urls,
|
|
25
|
+
)
|
|
26
|
+
contents_args = []
|
|
27
|
+
for notebook in notebooks:
|
|
28
|
+
contents_args.extend(["--contents", str(notebook)])
|
|
29
|
+
cmd = [
|
|
30
|
+
"jupyter",
|
|
31
|
+
"lite",
|
|
32
|
+
"build",
|
|
33
|
+
# *(["--debug"] if debug else []),
|
|
34
|
+
"--debug",
|
|
35
|
+
*contents_args,
|
|
36
|
+
"--no-libarchive",
|
|
37
|
+
"--apps",
|
|
38
|
+
"notebooks",
|
|
39
|
+
"--no-unused-shared-packages",
|
|
40
|
+
"--output-dir",
|
|
41
|
+
str(output_dir),
|
|
42
|
+
]
|
|
43
|
+
log.info("[jupyterlite] running build command: " + " ".join(cmd))
|
|
44
|
+
try:
|
|
45
|
+
result = subprocess.run(
|
|
46
|
+
cmd,
|
|
47
|
+
# capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
cwd=working_dir,
|
|
50
|
+
check=True,
|
|
51
|
+
)
|
|
52
|
+
if result.stdout:
|
|
53
|
+
log.debug("[jupyterlite] build output:\n" + result.stdout)
|
|
54
|
+
except subprocess.CalledProcessError as e:
|
|
55
|
+
log.error("[jupyterlite] build failed")
|
|
56
|
+
if e.stdout:
|
|
57
|
+
log.debug("[jupyterlite] build stdout:\n" + e.stdout)
|
|
58
|
+
if e.stderr:
|
|
59
|
+
log.error("[jupyterlite] build stderr:\n" + e.stderr)
|
|
60
|
+
raise
|
|
61
|
+
assert output_dir.exists(), "Output directory was not created"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def write_jupyter_lite_config(
|
|
65
|
+
*,
|
|
66
|
+
out_path: Path,
|
|
67
|
+
pip_urls: Iterable[str],
|
|
68
|
+
) -> None:
|
|
69
|
+
config = {
|
|
70
|
+
"JupyterLiteAddon": {
|
|
71
|
+
"piplite_urls": list(pip_urls),
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
out_path.write_text(json.dumps(config, indent=2))
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import fnmatch
|
|
2
|
+
import logging
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
import markdown
|
|
10
|
+
import nbformat
|
|
11
|
+
from mkdocs.config.base import Config as BaseConfig
|
|
12
|
+
from mkdocs.config.config_options import Type as OptionType
|
|
13
|
+
from mkdocs.config.defaults import MkDocsConfig
|
|
14
|
+
from mkdocs.plugins import BasePlugin
|
|
15
|
+
from mkdocs.structure.files import File, Files
|
|
16
|
+
from mkdocs.structure.pages import Page
|
|
17
|
+
from mkdocs.structure.toc import TableOfContents, get_toc
|
|
18
|
+
from nbconvert import MarkdownExporter
|
|
19
|
+
|
|
20
|
+
from mkdocs_jupyterlite import _build
|
|
21
|
+
|
|
22
|
+
log = logging.getLogger("mkdocs.plugins.jupyterlite")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NotebookFile(File):
|
|
26
|
+
"""
|
|
27
|
+
Wraps a regular File object to make .ipynb files appear as valid documentation files.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, file: File) -> None:
|
|
31
|
+
self._file = file
|
|
32
|
+
|
|
33
|
+
def __getattr__(self, name: str) -> Any:
|
|
34
|
+
return self._file.__getattribute__(name)
|
|
35
|
+
|
|
36
|
+
def is_documentation_page(self) -> Literal[True]:
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class JupyterlitePluginConfig(BaseConfig):
|
|
41
|
+
enabled = OptionType(bool, default=True)
|
|
42
|
+
notebook_patterns = OptionType(list, default=[])
|
|
43
|
+
pip_urls = OptionType(list, default=[])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class JupyterlitePlugin(BasePlugin[JupyterlitePluginConfig]):
|
|
47
|
+
def __init__(self):
|
|
48
|
+
super().__init__()
|
|
49
|
+
if isinstance(self.config, dict):
|
|
50
|
+
plugin_config = JupyterlitePluginConfig()
|
|
51
|
+
plugin_config.load_dict(self.config)
|
|
52
|
+
self.config = plugin_config
|
|
53
|
+
self._jupyterlite_build_dir = tempfile.TemporaryDirectory()
|
|
54
|
+
|
|
55
|
+
def _cleanup(self) -> None:
|
|
56
|
+
log.info(
|
|
57
|
+
"[jupyterlite] cleaning up temporary build directory: "
|
|
58
|
+
+ str(self._jupyterlite_build_dir.name)
|
|
59
|
+
)
|
|
60
|
+
self._jupyterlite_build_dir.cleanup()
|
|
61
|
+
|
|
62
|
+
def on_files(self, files: Files, config: MkDocsConfig) -> Files:
|
|
63
|
+
outfiles = []
|
|
64
|
+
notebook_paths = []
|
|
65
|
+
for file in files:
|
|
66
|
+
if is_notebook(
|
|
67
|
+
relative_path=file.src_uri,
|
|
68
|
+
notebook_patterns=self.config.notebook_patterns,
|
|
69
|
+
):
|
|
70
|
+
log.info("[jupyterlite] including notebook: " + str(file.abs_src_path))
|
|
71
|
+
outfiles.append(NotebookFile(file))
|
|
72
|
+
notebook_paths.append(file.abs_src_path)
|
|
73
|
+
else:
|
|
74
|
+
log.debug("[jupyterlite] ignoring file: " + str(file.abs_src_path))
|
|
75
|
+
outfiles.append(file)
|
|
76
|
+
notebooks = [Path(config.docs_dir) / p for p in notebook_paths]
|
|
77
|
+
_build.build_site(
|
|
78
|
+
notebooks=notebooks,
|
|
79
|
+
pip_urls=self.config.pip_urls,
|
|
80
|
+
output_dir=Path(self._jupyterlite_build_dir.name),
|
|
81
|
+
)
|
|
82
|
+
return Files(outfiles)
|
|
83
|
+
|
|
84
|
+
def on_pre_page(
|
|
85
|
+
self, page: Page, /, *, config: MkDocsConfig, files: Files
|
|
86
|
+
) -> Page | None:
|
|
87
|
+
if not isinstance(page.file, NotebookFile):
|
|
88
|
+
return page
|
|
89
|
+
log.info("[jupyterlite] on_pre_page " + str(page.file.src_uri))
|
|
90
|
+
|
|
91
|
+
def new_render(self: Page, config: MkDocsConfig, files: Files) -> None:
|
|
92
|
+
body = f"""
|
|
93
|
+
<iframe src="{config.site_url}jupyterlite/notebooks/index.html?path={page.file.src_uri}"
|
|
94
|
+
width="100%"
|
|
95
|
+
height="800px"
|
|
96
|
+
frameborder="1">
|
|
97
|
+
</iframe>
|
|
98
|
+
"""
|
|
99
|
+
self.content = body
|
|
100
|
+
toc, title_in_notebook = get_nb_toc_and_title(page.file.abs_src_path)
|
|
101
|
+
self.toc = toc
|
|
102
|
+
if title_in_notebook:
|
|
103
|
+
self.title = title_in_notebook
|
|
104
|
+
|
|
105
|
+
# replace render with new_render for this object only
|
|
106
|
+
page.render = new_render.__get__(page, Page)
|
|
107
|
+
return page
|
|
108
|
+
|
|
109
|
+
def on_post_build(self, config: MkDocsConfig) -> None:
|
|
110
|
+
shutil.copytree(
|
|
111
|
+
self._jupyterlite_build_dir.name,
|
|
112
|
+
Path(config.site_dir) / "jupyterlite",
|
|
113
|
+
dirs_exist_ok=True,
|
|
114
|
+
)
|
|
115
|
+
self._cleanup()
|
|
116
|
+
|
|
117
|
+
def on_build_error(self, *, error: Exception) -> None:
|
|
118
|
+
self._cleanup()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def is_notebook(*, relative_path: str | Path, notebook_patterns: Iterable[str]) -> bool:
|
|
122
|
+
for pattern in notebook_patterns:
|
|
123
|
+
if fnmatch.fnmatch(relative_path, pattern):
|
|
124
|
+
return True
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# Hooks for development
|
|
129
|
+
def on_startup(command: str, dirty: bool) -> None:
|
|
130
|
+
log.info("[jupyterlite][development] plugin started.")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def on_page_markdown(markdown: str, page: Any, config: MkDocsConfig, files: Any) -> str:
|
|
134
|
+
log.info("[jupyterlite][development] plugin started.")
|
|
135
|
+
plugin = JupyterlitePlugin()
|
|
136
|
+
return plugin.on_page_markdown(markdown, page=page, config=config, files=files)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def on_post_page(output: str, page: Page, config: MkDocsConfig) -> str:
|
|
140
|
+
log.info("[jupyterlite][development] plugin started.")
|
|
141
|
+
plugin = JupyterlitePlugin()
|
|
142
|
+
return plugin.on_post_page(output, page=page, config=config)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def on_files(files: Files, config: MkDocsConfig) -> Files:
|
|
146
|
+
log.info("[jupyterlite][development] plugin started.")
|
|
147
|
+
plugin = JupyterlitePlugin()
|
|
148
|
+
return plugin.on_files(files, config)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_nb_toc_and_title(path: str | Path) -> tuple[TableOfContents, str | None]:
|
|
152
|
+
"""Returns a TOC and title (the first heading, if present) for the Notebook."""
|
|
153
|
+
notebook = nbformat.reads(Path(path).read_text(), as_version=4)
|
|
154
|
+
(markdown_source, _resources) = MarkdownExporter().from_notebook_node(notebook)
|
|
155
|
+
md = markdown.Markdown(extensions=["toc"])
|
|
156
|
+
md.convert(markdown_source)
|
|
157
|
+
toc = get_toc(md.toc_tokens)
|
|
158
|
+
title = None
|
|
159
|
+
for token in md.toc_tokens:
|
|
160
|
+
if token["level"] == 1 and title is None:
|
|
161
|
+
title = token["name"]
|
|
162
|
+
return toc, title
|
|
File without changes
|