Skip to content

Commit 99faab7

Browse files
authored
Merge pull request #160 from akhmerov/script_outputs
use nbconvert to write scripts
2 parents 43280f8 + e7bd80c commit 99faab7

File tree

4 files changed

+85
-29
lines changed

4 files changed

+85
-29
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
run: |
2828
python -m pip install --upgrade pip
2929
pip install -r requirements.txt
30+
python -m bash_kernel.install # For testing a non-standard kernel
3031
pip install .
3132
3233
- name: Run tests

jupyter_sphinx/execute.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Execution and managing kernels."""
22

33
import os
4+
import warnings
45
from pathlib import Path
6+
from logging import Logger
57

68
from sphinx.transforms import SphinxTransform
79
from sphinx.errors import ExtensionError
@@ -18,6 +20,22 @@
1820
else:
1921
from nbclient.client import execute as executenb
2022

23+
import traitlets
24+
# Workaround of https://github.com/ipython/traitlets/issues/606
25+
if traitlets.version_info < (5, 1):
26+
class LoggerAdapterWrapper(Logger):
27+
"""Wrap a logger adapter, while pretending to be a logger."""
28+
def __init__(self, wrapped):
29+
self._wrapped = wrapped
30+
31+
def __getattribute__(self, attr):
32+
if attr == "_wrapped":
33+
return object.__getattribute__(self, attr)
34+
return self._wrapped.__getattribute__(attr)
35+
else:
36+
def LoggerAdapterWrapper(logger_adapter):
37+
return logger_adapter
38+
2139
import nbformat
2240

2341
import jupyter_sphinx as js
@@ -262,20 +280,19 @@ def write_notebook_output(notebook, output_dir, notebook_name, location=None):
262280
resources,
263281
os.path.join(output_dir, notebook_name + ".ipynb"),
264282
)
265-
# Write a script too. Note that utf-8 is the de facto
266-
# standard encoding for notebooks.
267-
ext = notebook.metadata.get("language_info", {}).get("file_extension", None)
268-
if ext is None:
269-
ext = ".txt"
270-
js.logger.warning(
271-
"Notebook code has no file extension metadata, " "defaulting to `.txt`",
272-
location=location,
273-
)
274-
contents = "\n\n".join(cell.source for cell in notebook.cells)
275283

276-
notebook_file = notebook_name + ext
284+
exporter = nbconvert.exporters.ScriptExporter(
285+
log=LoggerAdapterWrapper(js.logger)
286+
)
287+
with warnings.catch_warnings():
288+
# See https://github.com/jupyter/nbconvert/issues/1388
289+
warnings.simplefilter('ignore', DeprecationWarning)
290+
contents, resources = exporter.from_notebook_node(notebook)
291+
292+
notebook_file = notebook_name + resources['output_extension']
277293
output_dir = Path(output_dir)
278-
(output_dir / notebook_file).write_text(contents, encoding = "utf8")
294+
# utf-8 is the de-facto standard encoding for notebooks.
295+
(output_dir / notebook_file).write_text(contents, encoding="utf8")
279296

280297

281298
def contains_widgets(notebook):

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ nbformat
77
# For documentation building and testing
88
matplotlib
99
pytest
10+
11+
# A non-standard kernel
12+
bash_kernel

tests/test_execute.py

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import shutil
33
import os
44
import sys
5+
import warnings
56
from io import StringIO
67
from unittest.mock import Mock
78
from pathlib import Path
@@ -35,7 +36,7 @@ def doctree():
3536
def doctree(
3637
source,
3738
config=None,
38-
return_warnings=False,
39+
return_all=False,
3940
entrypoint="jupyter_sphinx",
4041
buildername='html'
4142
):
@@ -59,9 +60,8 @@ def doctree(
5960
app.build()
6061

6162
doctree = app.env.get_and_resolve_doctree("index", app.builder)
62-
63-
if return_warnings:
64-
return doctree, warnings.getvalue()
63+
if return_all:
64+
return doctree, app, warnings.getvalue()
6565
else:
6666
return doctree
6767

@@ -295,8 +295,6 @@ def test_emphasize_lines(doctree):
295295
"""
296296
tree = doctree(source)
297297
cell0, cell1 = tree.traverse(JupyterCellNode)
298-
(cellinput0, celloutput0) = cell0.children
299-
(cellinput1, celloutput1) = cell1.children
300298

301299
assert cell0.attributes["emphasize_lines"] == [1, 3, 4, 5]
302300
assert cell1.attributes["emphasize_lines"] == [2, 4]
@@ -314,9 +312,8 @@ def test_execution_environment_carries_over(doctree):
314312
a
315313
"""
316314
tree = doctree(source)
317-
cell0, cell1 = tree.traverse(JupyterCellNode)
318-
(cellinput0, celloutput0) = cell0.children
319-
(cellinput1, celloutput1) = cell1.children
315+
_, cell1 = tree.traverse(JupyterCellNode)
316+
(_, celloutput1) = cell1.children
320317
assert celloutput1.children[0].rawsource.strip() == "2"
321318

322319

@@ -336,9 +333,8 @@ def test_kernel_restart(doctree):
336333
a
337334
"""
338335
tree = doctree(source)
339-
cell0, cell1 = tree.traverse(JupyterCellNode)
340-
(cellinput0, celloutput0) = cell0.children
341-
(cellinput1, celloutput1) = cell1.children
336+
_, cell1 = tree.traverse(JupyterCellNode)
337+
(_, celloutput1) = cell1.children
342338
assert "NameError" in celloutput1.children[0].rawsource
343339

344340

@@ -359,7 +355,7 @@ def test_raises(doctree):
359355
"""
360356
tree = doctree(source)
361357
(cell,) = tree.traverse(JupyterCellNode)
362-
(cellinput, celloutput) = cell.children
358+
(_, celloutput) = cell.children
363359
assert "ValueError" in celloutput.children[0].rawsource
364360

365361
source = """
@@ -370,7 +366,7 @@ def test_raises(doctree):
370366
"""
371367
tree = doctree(source)
372368
(cell,) = tree.traverse(JupyterCellNode)
373-
(cellinput, celloutput) = cell.children
369+
(_, celloutput) = cell.children
374370
assert "ValueError" in celloutput.children[0].rawsource
375371

376372

@@ -407,7 +403,7 @@ def test_stdout(doctree):
407403
"""
408404
tree = doctree(source)
409405
(cell,) = tree.traverse(JupyterCellNode)
410-
(cellinput, celloutput) = cell.children
406+
(_, celloutput) = cell.children
411407
assert len(cell.children) == 2
412408
assert celloutput.children[0].rawsource.strip() == "hello world"
413409

@@ -420,7 +416,7 @@ def test_stderr(doctree):
420416
print('hello world', file=sys.stderr)
421417
"""
422418

423-
tree, warnings = doctree(source, return_warnings=True)
419+
tree, _, warnings = doctree(source, return_all=True)
424420
assert "hello world" in warnings
425421
(cell,) = tree.traverse(JupyterCellNode)
426422
(_, celloutput) = cell.children
@@ -556,7 +552,7 @@ def test_latex(doctree):
556552
for start, end in delimiter_pairs:
557553
tree = doctree(source.format(start, end))
558554
(cell,) = tree.traverse(JupyterCellNode)
559-
(cellinput, celloutput) = cell.children
555+
(_, celloutput) = cell.children
560556
assert celloutput.children[0].astext() == r"\int"
561557

562558

@@ -619,3 +615,42 @@ def test_download_role(text, reftarget, caption, tmp_path):
619615
assert_node(ret[0][0], [literal, caption])
620616
assert msg == []
621617

618+
619+
def test_save_script(doctree):
620+
source = """
621+
.. jupyter-kernel:: python3
622+
:id: test
623+
624+
.. jupyter-execute::
625+
626+
a = 1
627+
print(a)
628+
"""
629+
tree, app, _ = doctree(source, return_all=True)
630+
outdir = Path(app.outdir)
631+
saved_text = (outdir / '../jupyter_execute/test.py').read_text()
632+
assert saved_text.startswith('#!/usr/bin/env python')
633+
assert 'print(a)' in saved_text
634+
635+
636+
def test_bash_kernel(doctree):
637+
pytest.importorskip('bash_kernel')
638+
if sys.platform == 'win32':
639+
pytest.skip("Not trying bash on windows.")
640+
641+
source = """
642+
.. jupyter-kernel:: bash
643+
:id: test
644+
645+
.. jupyter-execute::
646+
647+
echo "foo"
648+
"""
649+
with warnings.catch_warnings():
650+
# See https://github.com/takluyver/bash_kernel/issues/105
651+
warnings.simplefilter('ignore', DeprecationWarning)
652+
_, app, _ = doctree(source, return_all=True)
653+
654+
outdir = Path(app.outdir)
655+
saved_text = (outdir / '../jupyter_execute/test.sh').read_text()
656+
assert 'echo "foo"' in saved_text

0 commit comments

Comments
 (0)