Source code for holoviews.ipython.preprocessors

"""
Prototype demo:

python holoviews/ipython/convert.py Conversion_Example.ipynb | python
"""
import ast

from nbconvert.preprocessors import Preprocessor


[docs]def comment_out_magics(source): """ Utility used to make sure AST parser does not choke on unrecognized magics. """ filtered = [] for line in source.splitlines(): if line.strip().startswith('%'): filtered.append('# ' + line) else: filtered.append(line) return '\n'.join(filtered)
[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