"""
Prototype demo:
python holoviews/ipython/convert.py Conversion_Example.ipynb | python
"""
import ast
from nbconvert.preprocessors import Preprocessor
[docs]def wrap_cell_expression(source, template='{expr}'):
"""
If a cell ends in an expression that could be displaying a HoloViews
object (as determined using the AST), wrap it with a given prefix
and suffix string.
If the cell doesn't end in an expression, return the source unchanged.
"""
cell_output_types = (ast.IfExp, ast.BoolOp, ast.BinOp, ast.Call,
ast.Name, ast.Attribute)
try:
node = ast.parse(comment_out_magics(source))
except SyntaxError:
return source
filtered = source.splitlines()
if node.body != []:
last_expr = node.body[-1]
if not isinstance(last_expr, ast.Expr):
pass # Not an expression
elif isinstance(last_expr.value, cell_output_types):
# CAREFUL WITH UTF8!
expr_end_slice = filtered[last_expr.lineno-1][:last_expr.col_offset]
expr_start_slice = filtered[last_expr.lineno-1][last_expr.col_offset:]
start = '\n'.join(filtered[:last_expr.lineno-1]
+ ([expr_end_slice] if expr_end_slice else []))
ending = '\n'.join(([expr_start_slice] if expr_start_slice else [])
+ filtered[last_expr.lineno:])
# BUG!! Adds newline for 'foo'; <expr>
return start + '\n' + template.format(expr=ending)
return source
[docs]def filter_magic(source, magic, strip=True):
"""
Given the source of a cell, filter out the given magic and collect
the lines using the magic into a list.
If strip is True, the IPython syntax part of the magic (e.g. %magic
or %%magic) is stripped from the returned lines.
"""
filtered, magic_lines=[],[]
for line in source.splitlines():
if line.strip().startswith(magic):
magic_lines.append(line)
else:
filtered.append(line)
if strip:
magic_lines = [el.replace(magic,'') for el in magic_lines]
return '\n'.join(filtered), magic_lines
[docs]def strip_magics(source):
"""
Given the source of a cell, filter out all cell and line magics.
"""
filtered=[]
for line in source.splitlines():
if not line.startswith('%'):
filtered.append(line)
return '\n'.join(filtered)
[docs]def replace_line_magic(source, magic, template='{line}'):
"""
Given a cell's source, replace line magics using a formatting
template, where {line} is the string that follows the magic.
"""
filtered = []
for line in source.splitlines():
if line.strip().startswith(magic):
substitution = template.format(line=line.replace(magic, ''))
filtered.append(substitution)
else:
filtered.append(line)
return '\n'.join(filtered)
[docs]class OptsMagicProcessor(Preprocessor):
"""
Preprocessor to convert notebooks to Python source to convert use of
opts magic to use the util.opts utility instead.
"""
[docs] def preprocess_cell(self, cell, resources, index):
if cell['cell_type'] == 'code':
source = replace_line_magic(cell['source'], '%opts',
template='hv.util.opts({line!r})')
source, opts_lines = filter_magic(source, '%%opts')
if opts_lines:
# Escape braces (e.g. normalization options) as they pass through format
template = 'hv.util.opts({options!r}, {{expr}})'.format(
options=' '.join(opts_lines).replace('{','{{').replace('}','}}'))
source = wrap_cell_expression(source, template)
cell['source'] = source
return cell, resources
def __call__(self, nb, resources): return self.preprocess(nb,resources)
[docs]class OutputMagicProcessor(Preprocessor):
"""
Preprocessor to convert notebooks to Python source to convert use of
output magic to use the util.output utility instead.
"""
[docs] def preprocess_cell(self, cell, resources, index):
if cell['cell_type'] == 'code':
source = replace_line_magic(cell['source'], '%output',
template='hv.util.output({line!r})')
source, output_lines = filter_magic(source, '%%output')
if output_lines:
template = f'hv.util.output({output_lines[-1]!r}, {{expr}})'
source = wrap_cell_expression(source, template)
cell['source'] = source
return cell, resources
def __call__(self, nb, resources): return self.preprocess(nb,resources)
[docs]class StripMagicsProcessor(Preprocessor):
"""
Preprocessor to convert notebooks to Python source to strips out all
magics. To be applied after the preprocessors that can handle
holoviews magics appropriately.
"""
[docs] def preprocess_cell(self, cell, resources, index):
if cell['cell_type'] == 'code':
cell['source'] = strip_magics(cell['source'])
return cell, resources
def __call__(self, nb, resources): return self.preprocess(nb,resources)
[docs]class Substitute(Preprocessor):
"""
An nbconvert preprocessor that substitutes one set of HTML data
output for another, adding annotation to the output as required.
The constructor accepts the notebook format version and a
substitutions dictionary:
{source_html:(target_html, annotation)}
Where the annotation may be None (i.e. no annotation).
"""
annotation = '<center><b>%s</b></center>'
def __init__(self, version, substitutions, **kw):
self.nbversion = version
self.substitutions = substitutions
super(Preprocessor, self).__init__(**kw)
def __call__(self, nb, resources): # Temporary hack around 'enabled' flag
return self.preprocess(nb,resources)
[docs] def replace(self, src):
"Given some source html substitute and annotated as applicable"
for html in self.substitutions.keys():
if src == html:
annotation = self.annotation % self.substitutions[src][1]
return annotation + self.substitutions[src][0]
return src
[docs] def preprocess_cell(self, cell, resources, index):
v4 = (self.nbversion[0] == 4)
if cell['cell_type'] == 'code':
for outputs in cell['outputs']:
output_key = ('execute_result' if v4 else 'pyout')
if outputs['output_type'] == output_key:
# V1-3
if not v4 and 'html' in outputs:
outputs['html'] = self.replace(outputs['html'])
# V4
for data in outputs.get('data',[]):
if v4 and data == 'text/html':
substitution = self.replace(outputs['data']['text/html'])
outputs['data']['text/html'] = substitution
return cell, resources