docs: handle input() and cell magic for notebook conversion (#3432)

This commit is contained in:
Eugene Yurtsev
2025-02-13 22:29:32 +00:00
committed by GitHub
parent 77d7c00ce8
commit 9111449ffd
3 changed files with 83 additions and 5 deletions
+23 -4
View File
@@ -1,4 +1,6 @@
import argparse
import ast
import glob
import os
import re
from pathlib import Path
@@ -7,7 +9,22 @@ from typing import Literal, Optional
import nbformat
from nbconvert.exporters import MarkdownExporter
from nbconvert.preprocessors import Preprocessor
import glob
def _uses_input(source: str) -> bool:
"""Parse the source code to determine if it uses the input() function."""
try:
tree = ast.parse(source)
except SyntaxError:
# If there's a syntax error, assume input() might be present to be safe.
return False
for node in ast.walk(tree):
if isinstance(node, ast.Call):
# Check if the function called is named 'input'
if isinstance(node.func, ast.Name) and node.func.id == "input":
return True
return False
class EscapePreprocessor(Preprocessor):
@@ -87,9 +104,11 @@ class EscapePreprocessor(Preprocessor):
elif cell.cell_type == "code":
# Determine if the cell has bash or cell magic
if cell.source.startswith("%") or cell.source.startswith("!"):
# update metadata to denote that it's not a python cell
cell.metadata["language_info"] = {"name": "unknown"}
source = cell.source
is_exec = not (
source.startswith("%") or source.startswith("!") or _uses_input(source)
)
cell.metadata["exec"] = is_exec
# Remove noqa comments
cell.source = re.sub(r"#\s*noqa.*$", "", cell.source, flags=re.MULTILINE)
@@ -6,7 +6,7 @@
{%- if 'magics_language' in cell.metadata -%}
{{ cell.metadata.magics_language}}
{%- elif 'name' in nb.metadata.get('language_info', {}) -%}
{{ nb.metadata.language_info.name }} exec="on" source="above" session="1" result="ansi"
{{ nb.metadata.language_info.name }}{% if cell.metadata.exec|default(false) %} exec="on" source="above" session="1" result="ansi"{% endif %}
{%- endif %}
{{ cell.source}}
```
@@ -0,0 +1,59 @@
import nbformat
from _scripts.notebook_convert import md_executable
EXPECTED_OUTPUT = """\
```python exec="on" source="above" session="1" result="ansi"
print("Hello, world!")
```
"""
def test_convert_normal_code_block() -> None:
notebook = nbformat.v4.new_notebook()
notebook.metadata.language_info = {"name": "python", "version": "3.11"}
notebook.cells.append(nbformat.v4.new_code_cell('print("Hello, world!")'))
markdown, _ = md_executable.from_notebook_node(notebook)
assert markdown == EXPECTED_OUTPUT
# We treat cell magic as a non-executable code block.
CELL_MAGIC_INPUT = """\
%%capture
print("Hello, world!")\
"""
CELL_MAGIC_OUTPUT = """\
```python
%%capture
print("Hello, world!")
```
"""
def test_convert_cell_magic() -> None:
notebook = nbformat.v4.new_notebook()
notebook.metadata.language_info = {"name": "python", "version": "3.11"}
notebook.cells.append(nbformat.v4.new_code_cell(CELL_MAGIC_INPUT))
markdown, _ = md_executable.from_notebook_node(notebook)
assert markdown == CELL_MAGIC_OUTPUT
STDIN_INPUT = """\
input("Enter your name: ")\
"""
STDIN_OUTPUT = """\
```python
input("Enter your name: ")
```
"""
def test_convert_input_cell() -> None:
notebook = nbformat.v4.new_notebook()
notebook.metadata.language_info = {"name": "python", "version": "3.11"}
notebook.cells.append(nbformat.v4.new_code_cell(STDIN_INPUT))
markdown, _ = md_executable.from_notebook_node(notebook)
assert markdown == STDIN_OUTPUT