From 963b08f3cba16d90b230cb1d7ecbe39c7442fd08 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 21 Jun 2024 19:11:34 +0530 Subject: [PATCH 01/13] Initial implementation for stripping cells --- jupyterlite_sphinx/jupyterlite_sphinx.py | 37 +++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index af4bc93..84f1e2e 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -24,6 +24,8 @@ from ._try_examples import examples_to_notebook, insert_try_examples_directive +import nbformat + try: import voici except ImportError: @@ -338,10 +340,42 @@ def run(self): notebooks_dir = Path(self.env.app.srcdir) / CONTENT_DIR / notebook_name + # Create a temporary directory and copy the notebooks there. This is done + # internally so that we don't overwrite existing notebooks in the source + # directories of the docs (if we are modifying their contents). Therefore + # we copy from an intermediate directory to the final one where the notebooks + # are expected to be found. + + import tempfile + temp_dir = tempfile.mkdtemp() + # Copy notebooks in notebooks_dir to temp_dir + shutil.copy(notebook, temp_dir) + + if self.env.config.strip_tagged_cells: + print(f"{notebook}: Removing cells tagged with 'strip' metadata set to 'true'") + + # Note: the directives meant to be stripped must be inside their own + # cell so that the cell itself gets removed from the notebook. This + # is so that we don't end up removing useful data or directives that + # are not meant to be removed. + with open(Path(temp_dir) / notebook_name) as f: + print(f"Opened {notebook_name}") + nb = nbformat.read(f, as_version=4) + nb.cells = [ + cell + for cell in nb.cells + if "true" not in cell.metadata.get("strip", []) + ] + print(f"Writing stripped notebook to {temp_dir}") + nbformat.write(nb, Path(temp_dir) / notebook_name) + # Copy the Notebook for NotebookLite to find os.makedirs(os.path.dirname(notebooks_dir), exist_ok=True) try: - shutil.copyfile(notebook, str(notebooks_dir)) + # copy notebook from temp_dir to notebooks_dir + print(f"Copying {notebook_name} from {temp_dir} to {notebooks_dir}") + shutil.copy(Path(temp_dir) / notebook_name, notebooks_dir) + shutil.rmtree(temp_dir) except shutil.SameFileError: pass else: @@ -749,6 +783,7 @@ def setup(app): app.add_config_value("jupyterlite_contents", None, rebuild="html") app.add_config_value("jupyterlite_bind_ipynb_suffix", True, rebuild="html") app.add_config_value("jupyterlite_silence", True, rebuild=True) + app.add_config_value("strip_tagged_cells", False, rebuild=True) # Pass a dictionary of additional options to the JupyterLite build command app.add_config_value("jupyterlite_build_command_options", None, rebuild="html") From f04f5c37f6d12d00a645102e112c70fd3748d225 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 21 Jun 2024 19:12:23 +0530 Subject: [PATCH 02/13] Add docs about configuring `strip_tagged_cells` --- docs/configuration.md | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 25111c7..9b72259 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -53,6 +53,64 @@ to your JupyterLite deployment. jupyterlite_config = "jupyterlite_config.json" ``` +## Strip particular tagged cells from IPython Notebooks + +When using the `NotebookLite` or the `JupyterLite` directives with a notebook passed, you can add strip particular tagged cells from the notebook before rendering it in the JupyterLite console. + +You can enable this behaviour by setting the following config: + +```python +strip_tagged_cells = True +``` + +and then tag the cells you want to strip with the tag `strip` in the JSON metadata of the cell, like this: + +```json +{ + "tags": [ + "strip": "true" + ] +} +``` + +This is useful when you want to remove some cells from the rendered notebook in the JupyterLite console, for example, cells that are used for adding reST-based directives or other Sphinx-specific content. + +For example, you can use this feature to remove the `toctree` directive from the rendered notebook in the JupyterLite console: + +```ipynb +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [ + "strip": "true" + ] + }, + "source": [ + "# Table of Contents\n", + "\n", + "```{toctree}\n", + ":maxdepth: 2\n", + "\n", + "directives/jupyterlite\n", + "directives/notebooklite\n", + "directives/replite\n", + "directives/voici\n", + "directives/try_examples\n", + "full\n", + "changelog\n", + "```" + ] + } + ] +} +``` + +where the cell with the `toctree` directive will be removed from the rendered notebook in the JupyterLite console. + +Note that this feature is only available for the `NotebookLite` and `JupyterLite` directives and works with the `ipynb` files passed to them, and therefore does not work with the `TryExamples` directive. + ## Disable the `.ipynb` docs source binding By default, jupyterlite-sphinx binds the `.ipynb` source suffix so that it renders Notebooks included in the doctree with JupyterLite. From ff738bca37349f64992c5d35b223d252270b6a77 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:36:39 +0530 Subject: [PATCH 03/13] Style fixes --- jupyterlite_sphinx/jupyterlite_sphinx.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 84f1e2e..4b15845 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -347,12 +347,15 @@ def run(self): # are expected to be found. import tempfile + temp_dir = tempfile.mkdtemp() # Copy notebooks in notebooks_dir to temp_dir shutil.copy(notebook, temp_dir) if self.env.config.strip_tagged_cells: - print(f"{notebook}: Removing cells tagged with 'strip' metadata set to 'true'") + print( + f"{notebook}: Removing cells tagged with 'strip' metadata set to 'true'" + ) # Note: the directives meant to be stripped must be inside their own # cell so that the cell itself gets removed from the notebook. This From 23d2cae06579740fbfda9e17396a91ddfef4857f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:39:41 +0530 Subject: [PATCH 04/13] Temporary: add an example --- docs/conf.py | 1 + docs/directives/my_notebook.ipynb | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 262e234..55d9a77 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,6 +11,7 @@ jupyterlite_contents = "./custom_contents" jupyterlite_bind_ipynb_suffix = False +strip_tagged_cells = True master_doc = "index" diff --git a/docs/directives/my_notebook.ipynb b/docs/directives/my_notebook.ipynb index 2cfef7b..3dbfa2a 100644 --- a/docs/directives/my_notebook.ipynb +++ b/docs/directives/my_notebook.ipynb @@ -23,7 +23,9 @@ "cell_type": "code", "execution_count": 2, "id": "84361143", - "metadata": {}, + "metadata": { + "strip": "true" + }, "outputs": [ { "name": "stdout", @@ -34,7 +36,8 @@ } ], "source": [ - "foo()" + "foo()\n", + "# This cell contains \"strip\": \"true\" in the metadata and therefore won't exist" ] }, { @@ -43,7 +46,9 @@ "id": "b9f8bed0", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# This cell will exist because it hasn't been tagged" + ] } ], "metadata": { From d7139d62feee707714ec59f384357a6bf3af6f04 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 27 Jun 2024 01:15:33 +0530 Subject: [PATCH 05/13] Fix docs build warning --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 9b72259..c5e0a0c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -77,7 +77,7 @@ This is useful when you want to remove some cells from the rendered notebook in For example, you can use this feature to remove the `toctree` directive from the rendered notebook in the JupyterLite console: -```ipynb +```json { "cells": [ { From 992231b6d966096427f182cdf992c6f28d31245f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 27 Jun 2024 02:04:52 +0530 Subject: [PATCH 06/13] Add note about Voici directives, reformat docs --- docs/configuration.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index c5e0a0c..6e07c16 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -55,9 +55,10 @@ jupyterlite_config = "jupyterlite_config.json" ## Strip particular tagged cells from IPython Notebooks -When using the `NotebookLite` or the `JupyterLite` directives with a notebook passed, you can add strip particular tagged cells from the notebook before rendering it in the JupyterLite console. +When using the `NotebookLite`, `JupyterLite`, or `Voici` directives with a notebook passed, you can +strip particular tagged cells from the notebook before rendering it in the JupyterLite console. -You can enable this behaviour by setting the following config: +This behaviour can be enabled by setting the following config: ```python strip_tagged_cells = True @@ -73,9 +74,12 @@ and then tag the cells you want to strip with the tag `strip` in the JSON metada } ``` -This is useful when you want to remove some cells from the rendered notebook in the JupyterLite console, for example, cells that are used for adding reST-based directives or other Sphinx-specific content. +This is useful when you want to remove some cells from the rendered notebook in the JupyterLite +console, for example, cells that are used for adding reST-based directives or other +Sphinx-specific content. -For example, you can use this feature to remove the `toctree` directive from the rendered notebook in the JupyterLite console: +For example, you can use this feature to remove the `toctree` directive from the rendered notebook +in the JupyterLite console: ```json { @@ -107,9 +111,12 @@ For example, you can use this feature to remove the `toctree` directive from the } ``` -where the cell with the `toctree` directive will be removed from the rendered notebook in the JupyterLite console. +where the cell with the `toctree` directive will be removed from the rendered notebook in +the JupyterLite console. -Note that this feature is only available for the `NotebookLite` and `JupyterLite` directives and works with the `ipynb` files passed to them, and therefore does not work with the `TryExamples` directive. +Note that this feature is only available for the `NotebookLite`, `JupyterLite`, and the +`Voici` directives and works with the `ipynb` files passed to them, and therefore does +not work with the `TryExamples` directive. ## Disable the `.ipynb` docs source binding From 31466befd0e5f288d924ddefa1be18596d08abbc Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:42:30 +0530 Subject: [PATCH 07/13] Remove temporary directory handling --- jupyterlite_sphinx/jupyterlite_sphinx.py | 50 +++++++++++------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 4b15845..c554cc7 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -340,19 +340,12 @@ def run(self): notebooks_dir = Path(self.env.app.srcdir) / CONTENT_DIR / notebook_name - # Create a temporary directory and copy the notebooks there. This is done - # internally so that we don't overwrite existing notebooks in the source - # directories of the docs (if we are modifying their contents). Therefore - # we copy from an intermediate directory to the final one where the notebooks - # are expected to be found. + notebook_is_stripped: bool = self.env.config.strip_tagged_cells - import tempfile - - temp_dir = tempfile.mkdtemp() - # Copy notebooks in notebooks_dir to temp_dir - shutil.copy(notebook, temp_dir) + # Create a folder to copy the notebooks to and for NotebookLite to find + os.makedirs(os.path.dirname(notebooks_dir), exist_ok=True) - if self.env.config.strip_tagged_cells: + if notebook_is_stripped: print( f"{notebook}: Removing cells tagged with 'strip' metadata set to 'true'" ) @@ -361,24 +354,27 @@ def run(self): # cell so that the cell itself gets removed from the notebook. This # is so that we don't end up removing useful data or directives that # are not meant to be removed. - with open(Path(temp_dir) / notebook_name) as f: - print(f"Opened {notebook_name}") - nb = nbformat.read(f, as_version=4) - nb.cells = [ - cell - for cell in nb.cells - if "true" not in cell.metadata.get("strip", []) - ] - print(f"Writing stripped notebook to {temp_dir}") - nbformat.write(nb, Path(temp_dir) / notebook_name) - # Copy the Notebook for NotebookLite to find - os.makedirs(os.path.dirname(notebooks_dir), exist_ok=True) + nb = nbformat.read(notebook, as_version=4) + print(f"Opened {notebook_name}") + nb = nbformat.read(notebook, as_version=4) + nb.cells = [ + cell + for cell in nb.cells + if "true" not in cell.metadata.get("strip", []) + ] + print(f"Writing stripped notebook to {notebooks_dir}") + nbformat.write(nb, notebooks_dir, version=4) + try: - # copy notebook from temp_dir to notebooks_dir - print(f"Copying {notebook_name} from {temp_dir} to {notebooks_dir}") - shutil.copy(Path(temp_dir) / notebook_name, notebooks_dir) - shutil.rmtree(temp_dir) + # if notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. + # if it is True, then it is already copied to notebooks_dir by nbformat.write + # above. + if not notebook_is_stripped: + print( + f"Notebooks are not stripped, copying {notebook_name} to {notebooks_dir}" + ) + shutil.copy(notebook, notebooks_dir) except shutil.SameFileError: pass else: From 6fb1b2f93748ff84de6dcb06570a91a28ce80666 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:44:13 +0530 Subject: [PATCH 08/13] Remove try-except block for `shutil.SameFileError` --- jupyterlite_sphinx/jupyterlite_sphinx.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index c554cc7..92eafe3 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -366,17 +366,14 @@ def run(self): print(f"Writing stripped notebook to {notebooks_dir}") nbformat.write(nb, notebooks_dir, version=4) - try: - # if notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. - # if it is True, then it is already copied to notebooks_dir by nbformat.write - # above. + # If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. + # If it is True, then they have already been copied to notebooks_dir by the + # nbformat.write() function above. if not notebook_is_stripped: print( f"Notebooks are not stripped, copying {notebook_name} to {notebooks_dir}" ) shutil.copy(notebook, notebooks_dir) - except shutil.SameFileError: - pass else: notebook_name = None From 8a2993d5e342f23c455675ae71da6ade108b36e1 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:46:30 +0530 Subject: [PATCH 09/13] Address PEP8 changes from black --- jupyterlite_sphinx/jupyterlite_sphinx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 92eafe3..605338c 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -366,9 +366,9 @@ def run(self): print(f"Writing stripped notebook to {notebooks_dir}") nbformat.write(nb, notebooks_dir, version=4) - # If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. - # If it is True, then they have already been copied to notebooks_dir by the - # nbformat.write() function above. + # If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. + # If it is True, then they have already been copied to notebooks_dir by the + # nbformat.write() function above. if not notebook_is_stripped: print( f"Notebooks are not stripped, copying {notebook_name} to {notebooks_dir}" From a50e099a754788e7133374a072cdcb7d03cdd7f5 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:56:21 +0530 Subject: [PATCH 10/13] Remove `print()` statements, clean up code --- jupyterlite_sphinx/jupyterlite_sphinx.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 605338c..66a9f49 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -346,10 +346,6 @@ def run(self): os.makedirs(os.path.dirname(notebooks_dir), exist_ok=True) if notebook_is_stripped: - print( - f"{notebook}: Removing cells tagged with 'strip' metadata set to 'true'" - ) - # Note: the directives meant to be stripped must be inside their own # cell so that the cell itself gets removed from the notebook. This # is so that we don't end up removing useful data or directives that @@ -363,17 +359,13 @@ def run(self): for cell in nb.cells if "true" not in cell.metadata.get("strip", []) ] - print(f"Writing stripped notebook to {notebooks_dir}") nbformat.write(nb, notebooks_dir, version=4) - # If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. - # If it is True, then they have already been copied to notebooks_dir by the - # nbformat.write() function above. - if not notebook_is_stripped: - print( - f"Notebooks are not stripped, copying {notebook_name} to {notebooks_dir}" - ) - shutil.copy(notebook, notebooks_dir) + # If notebook_is_stripped is False, then copy the notebook(s) to notebooks_dir. + # If it is True, then they have already been copied to notebooks_dir by the + # nbformat.write() function above. + else: + shutil.copy(notebook, notebooks_dir) else: notebook_name = None From 2af69e5c3655713bb99ce7579a56760a923ec4a6 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:58:33 +0530 Subject: [PATCH 11/13] Change `strip` to `jupyterlite_sphinx_strip` --- docs/configuration.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 6e07c16..a501577 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -64,12 +64,13 @@ This behaviour can be enabled by setting the following config: strip_tagged_cells = True ``` -and then tag the cells you want to strip with the tag `strip` in the JSON metadata of the cell, like this: +and then tag the cells you want to strip with the tag `jupyterlite_sphinx_strip` in the JSON metadata +of the cell, like this: ```json { "tags": [ - "strip": "true" + "jupyterlite_sphinx_strip": "true" ] } ``` From 1d9bd8b88421bed927c6114237d8e46839a9d3d7 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 11 Jul 2024 11:08:04 +0530 Subject: [PATCH 12/13] Documentation cleanups --- docs/configuration.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index a501577..38ba2a7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -55,7 +55,7 @@ jupyterlite_config = "jupyterlite_config.json" ## Strip particular tagged cells from IPython Notebooks -When using the `NotebookLite`, `JupyterLite`, or `Voici` directives with a notebook passed, you can +When using the `NotebookLite`, `JupyterLite`, or `Voici` directives with a notebook passed to them, you can strip particular tagged cells from the notebook before rendering it in the JupyterLite console. This behaviour can be enabled by setting the following config: @@ -64,7 +64,7 @@ This behaviour can be enabled by setting the following config: strip_tagged_cells = True ``` -and then tag the cells you want to strip with the tag `jupyterlite_sphinx_strip` in the JSON metadata +and then by tagging the cells you want to strip with the tag `jupyterlite_sphinx_strip` in the JSON metadata of the cell, like this: ```json @@ -89,7 +89,7 @@ in the JupyterLite console: "cell_type": "markdown", "metadata": { "tags": [ - "strip": "true" + "jupyterlite_sphinx_strip": "true" ] }, "source": [ @@ -116,8 +116,8 @@ where the cell with the `toctree` directive will be removed from the rendered no the JupyterLite console. Note that this feature is only available for the `NotebookLite`, `JupyterLite`, and the -`Voici` directives and works with the `ipynb` files passed to them, and therefore does -not work with the `TryExamples` directive. +`Voici` directives and works with the `.ipynb` files passed to them. It is not implemented +for the `TryExamples` directive. ## Disable the `.ipynb` docs source binding From b85346296fa7b4c1711138b6d660264c9c753e9e Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 11 Jul 2024 11:08:48 +0530 Subject: [PATCH 13/13] Revert changes made to notebook --- docs/directives/my_notebook.ipynb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/directives/my_notebook.ipynb b/docs/directives/my_notebook.ipynb index 3dbfa2a..2cfef7b 100644 --- a/docs/directives/my_notebook.ipynb +++ b/docs/directives/my_notebook.ipynb @@ -23,9 +23,7 @@ "cell_type": "code", "execution_count": 2, "id": "84361143", - "metadata": { - "strip": "true" - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -36,8 +34,7 @@ } ], "source": [ - "foo()\n", - "# This cell contains \"strip\": \"true\" in the metadata and therefore won't exist" + "foo()" ] }, { @@ -46,9 +43,7 @@ "id": "b9f8bed0", "metadata": {}, "outputs": [], - "source": [ - "# This cell will exist because it hasn't been tagged" - ] + "source": [] } ], "metadata": {