From 0f8b9aaed6f61d593b16fb064c2642471d61798c Mon Sep 17 00:00:00 2001 From: Yuzhong Zhang Date: Tue, 1 Sep 2026 15:48:24 +0000 Subject: [PATCH] fix: render nested code-cell source instead of dropping it Nested `{code-cell}` directives (e.g. inside tab-set/note) never become notebook cells, but discarding them hid the source entirely. Keep the warning and fall back to a literal_block so the code still appears. Fixes #723 --- myst_nb/core/read.py | 12 ++++++++---- tests/notebooks/nested_code_cell.md | 23 +++++++++++++++++++++++ tests/test_text_based.py | 14 ++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/notebooks/nested_code_cell.md diff --git a/myst_nb/core/read.py b/myst_nb/core/read.py index 84a6b35b..b7d739e2 100644 --- a/myst_nb/core/read.py +++ b/myst_nb/core/read.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Callable, Iterator +from docutils import nodes from docutils.parsers.rst import Directive from markdown_it.renderer import RendererHTML from myst_parser.config.main import MdParserConfig @@ -382,10 +383,9 @@ class UnexpectedCellDirective(Directive): which are picked up by the MyST Markdown reader to convert them into notebooks. If any are left in the parsed Markdown, it probably means that they were nested - inside another directive, which is not allowed. - - Therefore, we log a warning if it is triggered, and discard it. + inside another directive, so they cannot be converted into notebook cells. + Therefore, we log a warning and fall back to rendering the source as a code block. """ optional_arguments = 1 @@ -408,4 +408,8 @@ def run(self): else: logger = DocutilsDocLogger(document) # type: ignore logger.warning(message, line=self.lineno, subtype="nbcell") - return [] + source = "\n".join(self.content) + node = nodes.literal_block(source, source) + if self.arguments: + node["language"] = self.arguments[0] + return [node] diff --git a/tests/notebooks/nested_code_cell.md b/tests/notebooks/nested_code_cell.md new file mode 100644 index 00000000..05edbb50 --- /dev/null +++ b/tests/notebooks/nested_code_cell.md @@ -0,0 +1,23 @@ +--- +file_format: mystnb +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Nested code cells + +## Control: standalone code cell (should render) + +```{code-cell} python +print("hello from standalone cell") +``` + +## Reproducer: code cell nested in another directive (should still render source) + +````{note} +```{code-cell} python +print("hello from nested cell") +``` +```` diff --git a/tests/test_text_based.py b/tests/test_text_based.py index 5140de47..e988d382 100644 --- a/tests/test_text_based.py +++ b/tests/test_text_based.py @@ -68,3 +68,17 @@ def test_basic_nometadata(sphinx_run): sphinx_run.build() # print(sphinx_run.status()) assert "Found an unexpected `code-cell`" in sphinx_run.warnings() + + +@pytest.mark.sphinx_params( + "nested_code_cell.md", + conf={"nb_execution_mode": "off", "source_suffix": {".md": "myst-nb"}}, +) +def test_nested_code_cell_renders_source(sphinx_run): + """Nested code-cell directives cannot become notebook cells, but source must show.""" + sphinx_run.build() + assert "Found an unexpected `code-cell`" in sphinx_run.warnings() + html = sphinx_run.get_html() + sources = [block.get_text() for block in html.find_all("div", class_="highlight")] + assert any("hello from standalone cell" in s for s in sources) + assert any("hello from nested cell" in s for s in sources)