Skip to content

Commit 49d99f3

Browse files
Strip tagged cells from .ipynb notebooks passed to the NotebookLite and JupyterLite directives (#180)
* Initial implementation for stripping cells * Add docs about configuring `strip_tagged_cells` * Style fixes * Temporary: add an example * Fix docs build warning * Add note about Voici directives, reformat docs * Remove temporary directory handling * Remove try-except block for `shutil.SameFileError` * Address PEP8 changes from black * Remove `print()` statements, clean up code * Change `strip` to `jupyterlite_sphinx_strip` * Documentation cleanups * Revert changes made to notebook
1 parent 10033a3 commit 49d99f3

File tree

3 files changed

+95
-5
lines changed

3 files changed

+95
-5
lines changed

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
jupyterlite_contents = "./custom_contents"
1313
jupyterlite_bind_ipynb_suffix = False
14+
strip_tagged_cells = True
1415

1516
master_doc = "index"
1617

docs/configuration.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,72 @@ to your JupyterLite deployment.
5353
jupyterlite_config = "jupyterlite_config.json"
5454
```
5555

56+
## Strip particular tagged cells from IPython Notebooks
57+
58+
When using the `NotebookLite`, `JupyterLite`, or `Voici` directives with a notebook passed to them, you can
59+
strip particular tagged cells from the notebook before rendering it in the JupyterLite console.
60+
61+
This behaviour can be enabled by setting the following config:
62+
63+
```python
64+
strip_tagged_cells = True
65+
```
66+
67+
and then by tagging the cells you want to strip with the tag `jupyterlite_sphinx_strip` in the JSON metadata
68+
of the cell, like this:
69+
70+
```json
71+
{
72+
"tags": [
73+
"jupyterlite_sphinx_strip": "true"
74+
]
75+
}
76+
```
77+
78+
This is useful when you want to remove some cells from the rendered notebook in the JupyterLite
79+
console, for example, cells that are used for adding reST-based directives or other
80+
Sphinx-specific content.
81+
82+
For example, you can use this feature to remove the `toctree` directive from the rendered notebook
83+
in the JupyterLite console:
84+
85+
```json
86+
{
87+
"cells": [
88+
{
89+
"cell_type": "markdown",
90+
"metadata": {
91+
"tags": [
92+
"jupyterlite_sphinx_strip": "true"
93+
]
94+
},
95+
"source": [
96+
"# Table of Contents\n",
97+
"\n",
98+
"```{toctree}\n",
99+
":maxdepth: 2\n",
100+
"\n",
101+
"directives/jupyterlite\n",
102+
"directives/notebooklite\n",
103+
"directives/replite\n",
104+
"directives/voici\n",
105+
"directives/try_examples\n",
106+
"full\n",
107+
"changelog\n",
108+
"```"
109+
]
110+
}
111+
]
112+
}
113+
```
114+
115+
where the cell with the `toctree` directive will be removed from the rendered notebook in
116+
the JupyterLite console.
117+
118+
Note that this feature is only available for the `NotebookLite`, `JupyterLite`, and the
119+
`Voici` directives and works with the `.ipynb` files passed to them. It is not implemented
120+
for the `TryExamples` directive.
121+
56122
## Disable the `.ipynb` docs source binding
57123

58124
By default, jupyterlite-sphinx binds the `.ipynb` source suffix so that it renders Notebooks included in the doctree with JupyterLite.

jupyterlite_sphinx/jupyterlite_sphinx.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
from ._try_examples import examples_to_notebook, insert_try_examples_directive
2626

27+
import nbformat
28+
2729
try:
2830
import voici
2931
except ImportError:
@@ -338,12 +340,32 @@ def run(self):
338340

339341
notebooks_dir = Path(self.env.app.srcdir) / CONTENT_DIR / notebook_name
340342

341-
# Copy the Notebook for NotebookLite to find
343+
notebook_is_stripped: bool = self.env.config.strip_tagged_cells
344+
345+
# Create a folder to copy the notebooks to and for NotebookLite to find
342346
os.makedirs(os.path.dirname(notebooks_dir), exist_ok=True)
343-
try:
344-
shutil.copyfile(notebook, str(notebooks_dir))
345-
except shutil.SameFileError:
346-
pass
347+
348+
if notebook_is_stripped:
349+
# Note: the directives meant to be stripped must be inside their own
350+
# cell so that the cell itself gets removed from the notebook. This
351+
# is so that we don't end up removing useful data or directives that
352+
# are not meant to be removed.
353+
354+
nb = nbformat.read(notebook, as_version=4)
355+
print(f"Opened {notebook_name}")
356+
nb = nbformat.read(notebook, as_version=4)
357+
nb.cells = [
358+
cell
359+
for cell in nb.cells
360+
if "true" not in cell.metadata.get("strip", [])
361+
]
362+
nbformat.write(nb, notebooks_dir, version=4)
363+
364+
# If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir.
365+
# If it is True, then they have already been copied to notebooks_dir by the
366+
# nbformat.write() function above.
367+
else:
368+
shutil.copy(notebook, notebooks_dir)
347369
else:
348370
notebook_name = None
349371

@@ -749,6 +771,7 @@ def setup(app):
749771
app.add_config_value("jupyterlite_contents", None, rebuild="html")
750772
app.add_config_value("jupyterlite_bind_ipynb_suffix", True, rebuild="html")
751773
app.add_config_value("jupyterlite_silence", True, rebuild=True)
774+
app.add_config_value("strip_tagged_cells", False, rebuild=True)
752775

753776
# Pass a dictionary of additional options to the JupyterLite build command
754777
app.add_config_value("jupyterlite_build_command_options", None, rebuild="html")

0 commit comments

Comments
 (0)