import copy
import logging
import traitlets as traits
from nbconvert.preprocessors import Preprocessor
from nbformat.notebooknode import NotebookNode
[docs]def merge(a, b, path=None, overwrite=True):
"""merges b into a
Examples
--------
>>> from pprint import pprint
>>> pprint(merge({'a':{'b':1},'c':3},{'a':{'b':2}}))
{'a': {'b': 2}, 'c': 3}
"""
if path is None:
path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
merge(a[key], b[key], path + [str(key)], overwrite)
elif a[key] == b[key]:
pass # same leaf value
elif overwrite:
a[key] = b[key] # overwrite leaf value
else:
raise Exception("Conflict at %s" % ".".join(path + [str(key)]))
else:
a[key] = b[key]
return a
[docs]class SplitOutputs(Preprocessor):
""" a preprocessor to split outputs into separate cells,
merging the cell and output metadata, with output metadata taking priority
"""
split = traits.Bool(True, help="whether to split outputs").tag(config=True)
[docs] def preprocess(self, nb, resources):
if not self.split:
return nb, resources
logging.info("splitting outputs into separate cells")
final_cells = []
for cell in nb.cells:
if not cell.cell_type == "code":
final_cells.append(cell)
continue
outputs = cell.pop("outputs")
cell.outputs = []
final_cells.append(cell)
for output in outputs:
meta = copy.deepcopy(cell.metadata)
# don't need the code to output
meta.get("ipub", NotebookNode({})).code = False
# don't create a new slide for each output,
# unless specified in output level metadata
if "slide" in meta.get("ipub", NotebookNode({})):
if meta.ipub.slide == "new":
meta.ipub.slide = True
else:
meta.ipub.slide = meta.ipub.slide
meta = merge(meta, output.get("metadata", {}))
new = NotebookNode(
{
"cell_type": "code",
"source": "",
"execution_count": None,
"metadata": meta,
"outputs": [output],
}
)
final_cells.append(new)
nb.cells = final_cells
return nb, resources