From fd8de74160ec074bb8622f5d69a4224927db03a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tun=C3=A7=20Ba=C5=9Far=20K=C3=B6se?= Date: Wed, 18 Sep 2024 14:51:44 +0300 Subject: [PATCH 1/5] Add `:only:` option for code cells --- myst_nb/core/read.py | 9 ++++++--- myst_nb/sphinx_.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/myst_nb/core/read.py b/myst_nb/core/read.py index 6951f34a..e4fa27de 100644 --- a/myst_nb/core/read.py +++ b/myst_nb/core/read.py @@ -86,6 +86,7 @@ def create_nb_reader( config=md_config, add_source_map=True, path=path, + builder=nb_config.builder_name ), md_config, {"type": "plugin", "name": "myst_nb_md"}, @@ -176,6 +177,7 @@ def read_myst_markdown_notebook( raw_directive="{raw-cell}", add_source_map=False, path: str | Path | None = None, + builder: str = "", ) -> nbf.NotebookNode: """Convert text written in the myst format to a notebook. @@ -260,9 +262,10 @@ def _flush_markdown(start_line, token, md_metadata): ) meta = nbf.from_dict(options) source_map.append(token_map[0] + 1) - notebook.cells.append( - nbf_version.new_code_cell(source="\n".join(body_lines), metadata=meta) - ) + if "only" not in options or options["only"] == builder: + notebook.cells.append( + nbf_version.new_code_cell(source="\n".join(body_lines), metadata=meta) + ) md_metadata = {} md_start_line = token_map[1] diff --git a/myst_nb/sphinx_.py b/myst_nb/sphinx_.py index 4885d9ff..a037f2ff 100644 --- a/myst_nb/sphinx_.py +++ b/myst_nb/sphinx_.py @@ -80,6 +80,7 @@ def parse(self, inputstring: str, document: nodes.document) -> None: # get notebook rendering configuration nb_config: NbParserConfig = self.env.mystnb_config + nb_config.builder_name = self.env.app.builder.name # create a reader for the notebook nb_reader = create_nb_reader(document_path, md_config, nb_config, inputstring) From ba8424a387b3c9deb65debac30ce0201c40dee2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tun=C3=A7=20Ba=C5=9Far=20K=C3=B6se?= Date: Fri, 20 Sep 2024 08:52:17 +0300 Subject: [PATCH 2/5] ruff format --- myst_nb/core/read.py | 7 +++++-- myst_nb/sphinx_.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/myst_nb/core/read.py b/myst_nb/core/read.py index e4fa27de..6683beb3 100644 --- a/myst_nb/core/read.py +++ b/myst_nb/core/read.py @@ -1,4 +1,5 @@ """Module for reading notebook formats from a string input.""" + from __future__ import annotations import dataclasses as dc @@ -86,7 +87,7 @@ def create_nb_reader( config=md_config, add_source_map=True, path=path, - builder=nb_config.builder_name + builder=nb_config.builder_name, ), md_config, {"type": "plugin", "name": "myst_nb_md"}, @@ -264,7 +265,9 @@ def _flush_markdown(start_line, token, md_metadata): source_map.append(token_map[0] + 1) if "only" not in options or options["only"] == builder: notebook.cells.append( - nbf_version.new_code_cell(source="\n".join(body_lines), metadata=meta) + nbf_version.new_code_cell( + source="\n".join(body_lines), metadata=meta + ) ) md_metadata = {} md_start_line = token_map[1] diff --git a/myst_nb/sphinx_.py b/myst_nb/sphinx_.py index a037f2ff..80f4a68d 100644 --- a/myst_nb/sphinx_.py +++ b/myst_nb/sphinx_.py @@ -1,4 +1,5 @@ """The sphinx parser implementation for myst-nb.""" + from __future__ import annotations from collections import defaultdict From 8025dd39962432b4ce92694820c67cd327c2e421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tun=C3=A7=20Ba=C5=9Far=20K=C3=B6se?= Date: Fri, 20 Sep 2024 10:49:13 +0300 Subject: [PATCH 3/5] Add tests and docs --- docs/computation/execute.md | 24 ++++++++++++++ tests/conftest.py | 9 ++++++ tests/notebooks/with_only.md | 24 ++++++++++++++ tests/test_execute.py | 45 ++++++++++++++++++++++++++ tests/test_execute/test_only_html.xml | 16 +++++++++ tests/test_execute/test_only_latex.xml | 16 +++++++++ 6 files changed, 134 insertions(+) create mode 100644 tests/notebooks/with_only.md create mode 100644 tests/test_execute/test_only_html.xml create mode 100644 tests/test_execute/test_only_latex.xml diff --git a/docs/computation/execute.md b/docs/computation/execute.md index 0b280224..4c6e6df1 100644 --- a/docs/computation/execute.md +++ b/docs/computation/execute.md @@ -227,3 +227,27 @@ which produces: ```{nb-exec-table} ``` + +(execute/builder-dep)= +## Builder-dependent execution + +```{warning} +This is an experimental feature that is **not** part of the core `MyST` markup specification, and may be removed in the future. Using `:only:` may also not work well with caching and may require deleting previously built files when switching builders. +``` + +It may be desirable to execute different code depending on the Sphinx builder being used. +For example, one may want to have different setup code for plots to be displayed in a website compared to those in a PDF file obtained via LaTeX. +One can use the `only` option in situations like these, like in the following example: + +````md +```{code-cell} +:only: html +import matplotlib.pyplot as plt +plt.style.use('ggplot') +``` + +```{code-cell} +:only: latex +plt.style.use('fivethirtyeight') +``` +```` diff --git a/tests/conftest.py b/tests/conftest.py index 7d8f6436..56477402 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -104,6 +104,15 @@ def get_html(self, index=0): pytest.fail("html not output") return bs4.BeautifulSoup(_path.read_text(), "html.parser") + def get_latex(self, index=0): + """Return the built LaTeX file.""" + # name = self.files[index][0] + # not sure why latex output is named python.tex + _path = self.app.outdir / "python.tex" # (name + ".tex") + if not _path.exists(): + pytest.fail("tex not output") + return _path.read_text(encoding="utf-8") + def get_nb(self, index=0): """Return the output notebook (after any execution).""" name = self.files[index][0] diff --git a/tests/notebooks/with_only.md b/tests/notebooks/with_only.md new file mode 100644 index 00000000..013c3491 --- /dev/null +++ b/tests/notebooks/with_only.md @@ -0,0 +1,24 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: '0.8' + jupytext_version: 1.4.1+dev +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Test the `:only:` option + +```{code-cell} +:only: html +1+1 +``` + +```{code-cell} +:only: latex +2+2 +``` diff --git a/tests/test_execute.py b/tests/test_execute.py index f39d1df0..df71cb19 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -1,4 +1,5 @@ """Test sphinx builds which execute notebooks.""" + import os from pathlib import Path @@ -327,6 +328,50 @@ def test_nb_exec_table(sphinx_run, file_regression): assert any("nb_exec_table" in row.text for row in rows) +@pytest.mark.sphinx_params( + "with_only.md", conf={"nb_execution_mode": "auto"}, buildername="html" +) +def test_only_html(sphinx_run, file_regression): + """Test that the table gets output into the HTML, + including a row for the executed notebook. + """ + sphinx_run.build() + # print(sphinx_run.status()) + assert not sphinx_run.warnings() + file_regression.check( + sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" + ) + # print(sphinx_run.get_html()) + output = sphinx_run.get_html().select("div.output div.highlight pre") + assert len(output) == 1 # check that other cell is not present + assert "2" in output # check value to ensure correct cell is present + + +@pytest.mark.sphinx_params( + "with_only.md", conf={"nb_execution_mode": "auto"}, buildername="latex" +) +def test_only_latex(sphinx_run, file_regression): + """Test that the table gets output into the HTML, + including a row for the executed notebook. + """ + sphinx_run.build() + # print(sphinx_run.status()) + assert not sphinx_run.warnings() + file_regression.check( + sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" + ) + # print(sphinx_run.get_html()) + output = sphinx_run.get_latex() + correct = ( + r"\begin{sphinxVerbatim}[commandchars=\\\{\}]" "\n4\n" r"\end{sphinxVerbatim}" + ) + assert correct in output # check value to ensure correct cell is present + wrong = ( + r"\begin{sphinxVerbatim}[commandchars=\\\{\}]" "\n2\n" r"\end{sphinxVerbatim}" + ) + assert wrong not in output # check value to ensure other cell is not present + + @pytest.mark.sphinx_params( "custom-formats.Rmd", conf={ diff --git a/tests/test_execute/test_only_html.xml b/tests/test_execute/test_only_html.xml new file mode 100644 index 00000000..73925712 --- /dev/null +++ b/tests/test_execute/test_only_html.xml @@ -0,0 +1,16 @@ + +
+ + Test the + <literal> + :only: + option + <container cell_index="1" cell_metadata="{'only': 'html'}" classes="cell" exec_count="1" nb_element="cell_code"> + <container classes="cell_input" nb_element="cell_code_source"> + <literal_block language="ipython3" xml:space="preserve"> + 1+1 + <container classes="cell_output" nb_element="cell_code_output"> + <container nb_element="mime_bundle"> + <container mime_type="text/plain"> + <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> + 2 diff --git a/tests/test_execute/test_only_latex.xml b/tests/test_execute/test_only_latex.xml new file mode 100644 index 00000000..8279ec51 --- /dev/null +++ b/tests/test_execute/test_only_latex.xml @@ -0,0 +1,16 @@ +<document source="with_only"> + <section ids="test-the-only-option" names="test\ the\ :only:\ option"> + <title> + Test the + <literal> + :only: + option + <container cell_index="1" cell_metadata="{'only': 'latex'}" classes="cell" exec_count="1" nb_element="cell_code"> + <container classes="cell_input" nb_element="cell_code_source"> + <literal_block language="ipython3" xml:space="preserve"> + 2+2 + <container classes="cell_output" nb_element="cell_code_output"> + <container nb_element="mime_bundle"> + <container mime_type="text/plain"> + <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> + 4 From 69f2057b9bae2246f08baa04326457cc5da3ac2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tun=C3=A7=20Ba=C5=9Far=20K=C3=B6se?= <tunc.kose@aalto.fi> Date: Mon, 23 Sep 2024 11:31:09 +0300 Subject: [PATCH 4/5] Fix tests --- tests/conftest.py | 5 ++--- tests/test_execute.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 56477402..f7921492 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -106,9 +106,8 @@ def get_html(self, index=0): def get_latex(self, index=0): """Return the built LaTeX file.""" - # name = self.files[index][0] - # not sure why latex output is named python.tex - _path = self.app.outdir / "python.tex" # (name + ".tex") + name = self.files[index][0] + _path = self.app.outdir / (name + ".tex") if not _path.exists(): pytest.fail("tex not output") return _path.read_text(encoding="utf-8") diff --git a/tests/test_execute.py b/tests/test_execute.py index df71cb19..90e24b52 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -344,7 +344,7 @@ def test_only_html(sphinx_run, file_regression): # print(sphinx_run.get_html()) output = sphinx_run.get_html().select("div.output div.highlight pre") assert len(output) == 1 # check that other cell is not present - assert "2" in output # check value to ensure correct cell is present + assert "2" in output[0].text # check value to ensure correct cell is present @pytest.mark.sphinx_params( From 9d66cef9c0c775f2c531f442b181f11310c36c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tun=C3=A7=20Ba=C5=9Far=20K=C3=B6se?= <tunc.kose@aalto.fi> Date: Mon, 23 Sep 2024 13:02:04 +0300 Subject: [PATCH 5/5] Set output filename explicitly in test --- tests/test_execute.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_execute.py b/tests/test_execute.py index 90e24b52..847b6840 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -348,7 +348,12 @@ def test_only_html(sphinx_run, file_regression): @pytest.mark.sphinx_params( - "with_only.md", conf={"nb_execution_mode": "auto"}, buildername="latex" + "with_only.md", + conf={ + "nb_execution_mode": "auto", + "latex_documents": [('with_only', 'with_only.tex', "project", "author", 'manual')] + }, + buildername="latex" ) def test_only_latex(sphinx_run, file_regression): """Test that the table gets output into the HTML,