From d8dd63931fda265906e8c6d06762ea6e86065616 2013-04-28 19:11:18 From: Jonathan Frederic Date: 2013-04-28 19:11:18 Subject: [PATCH] Create exceptions file to house all of the convert specific exceptions. Refactored 'exporter' Lots of changes.... --- diff --git a/api/exceptions.py b/api/exceptions.py new file mode 100644 index 0000000..f1904fa --- /dev/null +++ b/api/exceptions.py @@ -0,0 +1,21 @@ +"""Contains all of the exceptions used in NBConvert explicitly""" +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- +class ConversionException(Exception): + """An exception raised by the conversion process.""" + + pass \ No newline at end of file diff --git a/api/exporter.py b/api/exporter.py index 7bfa1bf..0800243 100755 --- a/api/exporter.py +++ b/api/exporter.py @@ -33,104 +33,91 @@ from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Bool from jinja2 import Environment, FileSystemLoader # local import (pre-transformers) +from exceptions import ConversionException from . import transformers as trans #TODO +from .latex_transformer import (LatexTransformer) #TODO +from .utils import markdown2rst #TODO + +import textwrap #TODO +#Jinja2 filters +from .jinja_filters import (python_comment, indent, + rm_fake, remove_ansi, markdown, highlight, highlight2latex, + ansi2html, markdown2latex, get_lines, escape_tex, FilterDataType, + rm_dollars, rm_math_space + ) + +#Try to import the Sphinx exporter. If the user doesn't have Sphinx isntalled +#on his/her machine, fail silently. try: from .sphinx_transformer import (SphinxTransformer) #TODO except ImportError: SphinxTransformer = None -from .latex_transformer import (LatexTransformer) #TODO +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- -# some jinja filters -from .jinja_filters import (python_comment, indent, - rm_fake, remove_ansi, markdown, highlight, highlight2latex, - ansi2html, markdown2latex, get_lines, escape_tex, FilterDataType, - rm_dollars, rm_math_space - ) +#Standard Jinja2 environment constants +TEMPLATE_PATH = "/../templates/" +TEMPLATE_SKELETON_PATH = "/../templates/skeleton/" +TEMPLATE_EXTENSION = ".tpl" -from .utils import markdown2rst #TODO +#Latex Jinja2 constants +LATEX_TEMPLATE_PATH = "/../templates/tex/" +LATEX_TEMPLATE_SKELETON_PATH = "/../templates/tex/skeleton/" +LATEX_TEMPLATE_EXTENSION = ".tplx" -import textwrap #TODO +#Special Jinja2 syntax that will not conflict when exporting latex. +LATEX_JINJA_COMMENT_BLOCK = ["((=", "=))"] +LATEX_JINJA_VARIABLE_BLOCK = ["(((", ")))"] +LATEX_JINJA_LOGIC_BLOCK = ["((*", "*))"] +#Jinja2 extensions to load. +JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols'] + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- +#TODO: Move to utils.strings def wrap(text, width=100): - """ try to detect and wrap paragraph""" + """ Try to detect and wrap paragraph""" splitt = text.split('\n') wrp = map(lambda x:textwrap.wrap(x,width),splitt) wrpd = map('\n'.join, wrp) return '\n'.join(wrpd) - - -# define differents environemnt with different -# delimiters not to conflict with languages inside - -env = Environment( - loader=FileSystemLoader([ - os.path.dirname(os.path.realpath(__file__))+'/../templates/', - os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/', - ]), - extensions=['jinja2.ext.loopcontrols'] - ) - - -texenv = Environment( - loader=FileSystemLoader([ - os.path.dirname(os.path.realpath(__file__))+'/../templates/tex/', - os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/tex/', - ]), - extensions=['jinja2.ext.loopcontrols'] - ) - - -texenv.block_start_string = '((*' -texenv.block_end_string = '*))' - -texenv.variable_start_string = '(((' -texenv.variable_end_string = ')))' - -texenv.comment_start_string = '((=' -texenv.comment_end_string = '=))' - -texenv.filters['escape_tex'] = escape_tex - #----------------------------------------------------------------------------- -# Class declarations +# Classes and functions #----------------------------------------------------------------------------- -class ConversionException(Exception): - pass - class Exporter(Configurable): """ A Jinja2 base converter templates Preprocess the ipynb files, feed it throug jinja templates, and spit an converted files and a data object with other data - should be mostly configurable """ pre_transformer_order = List(['haspyout_transformer'], - config=True, - help= """ - An ordered list of pre transformer to apply to the ipynb - file before running through templates - """ - ) - - tex_environement = Bool(False, - config=True, - help=""" is this a tex environment or not """) + config=True, + help= """ + An ordered list of pre transformer to apply to the ipynb + file before running through templates + """ + ) - template_file = Unicode('', - config=True, - help=""" Name of the template file to use """ ) - #------------------------------------------------------------------------- - # Instance-level attributes that are set in the constructor for this - # class. - #------------------------------------------------------------------------- + tex_environement = Bool( + False, + config=True, + help=" Whether or not the user is exporting to latex.") + template_file = Unicode( + '', config=True, + help="Name of the template file to use") - preprocessors = [] + #Processors that process the input data prior to the export, set in the + #constructor for this class. + preprocessors = [] def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw): """ Init a new converter. @@ -156,9 +143,34 @@ class Exporter(Configurable): """ super(ConverterTemplate, self).__init__(config=config, **kw) - # variable parameters depending on the pype of jinja environement - self.env = texenv if self.tex_environement else env - self.ext = '.tplx' if self.tex_environement else '.tpl' + #Create a Latex environment if the user is exporting latex. + if self.tex_environement: + self.ext = LATEX_TEMPLATE_EXTENSION + self.env = Environment( + loader=FileSystemLoader([ + os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_PATH, + os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_SKELETON_PATH, + ]), + extensions=JINJA_EXTENSIONS + ) + + #Set special Jinja2 syntax that will not conflict with latex. + self.env.block_start_string = LATEX_JINJA_LOGIC_BLOCK[0] + self.env.block_end_string = LATEX_JINJA_LOGIC_BLOCK[1] + self.env.variable_start_string = LATEX_JINJA_VARIABLE_BLOCK[0] + self.env.variable_end_string = LATEX_JINJA_VARIABLE_BLOCK[1] + self.env.comment_start_string = LATEX_JINJA_COMMENT_BLOCK[0] + self.env.comment_end_string = LATEX_JINJA_COMMENT_BLOCK[1] + + else: #Standard environment + self.ext = TEMPLATE_EXTENSION + self.env = Environment( + loader=FileSystemLoader([ + os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH, + os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH, + ]), + extensions=JINJA_EXTENSIONS + ) for name in self.pre_transformer_order: # get the user-defined transformer first @@ -167,25 +179,25 @@ class Exporter(Configurable): transformer = transformer(config=config) self.preprocessors.append(transformer) - ## for compat, remove later + #For compatibility, TODO: remove later. self.preprocessors.append(trans.coalesce_streams) self.preprocessors.append(trans.ExtractFigureTransformer(config=config)) self.preprocessors.append(trans.RevealHelpTransformer(config=config)) self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config)) + self.preprocessors.append(LatexTransformer(config=config)) + + #Only load the sphinx transformer if the file reference worked + #(Sphinx dependencies exist on the user's machine.) if SphinxTransformer: self.preprocessors.append(SphinxTransformer(config=config)) - self.preprocessors.append(LatexTransformer(config=config)) - ## + #Add filters to the Jinja2 environment self.env.filters['filter_data_type'] = FilterDataType(config=config) self.env.filters['pycomment'] = python_comment self.env.filters['indent'] = indent self.env.filters['rm_fake'] = rm_fake self.env.filters['rm_ansi'] = remove_ansi self.env.filters['markdown'] = markdown - self.env.filters['highlight'] = highlight2latex if self.tex_environement else highlight - self.env.filters['highlight2html'] = highlight - self.env.filters['highlight2latex'] = highlight2latex self.env.filters['ansi2html'] = ansi2html self.env.filters['markdown2latex'] = markdown2latex self.env.filters['markdown2rst'] = markdown2rst @@ -193,57 +205,78 @@ class Exporter(Configurable): self.env.filters['wrap'] = wrap self.env.filters['rm_dollars'] = rm_dollars self.env.filters['rm_math_space'] = rm_math_space + self.env.filters['highlight2html'] = highlight + self.env.filters['highlight2latex'] = highlight2latex - ## user filter will overwrite - for key, filtr in jinja_filters.iteritems(): - if isinstance(filtr, MetaHasTraits): - self.env.filters[key] = filtr(config=config) - else : - self.env.filters[key] = filtr - + #Latex specific filters + if self.tex_environement: + self.env.filters['escape_tex'] = escape_tex + self.env.filters['highlight'] = highlight2latex + else: + self.env.filters['highlight'] = highlight + + #Load user filters. Overwrite existing filters if need be. + for key, user_filter in jinja_filters.iteritems(): + if isinstance(user_filter, MetaHasTraits): + self.env.filters[key] = user_filter(config=config) + else: + self.env.filters[key] = user_filter + + #Load the template file. self.template = self.env.get_template(self.template_file+self.ext) - def process(self, nb): - """ - preprocess the notebook json for easier use with the templates. - will call all the `preprocessor`s in order before returning it. + def _preprocess(self, nb): + """ Preprocess the notebook using the transformers specific + for the current export format. + + nb: Notebook to preprocess """ - # dict of 'resources' that could be made by the preprocessors - # like key/value data to extract files from ipynb like in latex conversion + #Dict of 'resources' that can be filled by the preprocessors. resources = {} - for preprocessor in self.preprocessors: - nb, resources = preprocessor(nb, resources) - + #Run each transformer on the notebook. Carry the output along + #to each transformer + for transformer in self.preprocessors: + nb, resources = transformer(nb, resources) return nb, resources - def convert(self, nb): - """ convert the ipynb file - return both the converted ipynb file and a dict containing potential - other resources + def export(self, nb): + """Export notebook object + + nb: Notebook object to export. + + Returns both the converted ipynb file and a dict containing the + resources created along the way via the transformers and Jinja2 + processing. """ - nb, resources = self.process(nb) + + nb, resources = self._preprocess(nb) return self.template.render(nb=nb, resources=resources), resources def from_filename(self, filename): - """read and convert a notebook from a file name""" - with io.open(filename) as f: - return self.convert(nbformat.read(f, 'json')) + """Read and export a notebook from a filename - def from_file(self, filelike): - """read and convert a notebook from a filelike object + filename: Filename of the notebook file to export. - filelike object will just be "read" and should be json format.. + Returns both the converted ipynb file and a dict containing the + resources created along the way via the transformers and Jinja2 + processing. """ - return self.convert(nbformat.read(filelike, 'json')) + with io.open(filename) as f: + return self.export(nbformat.read(f, 'json')) + + def from_file(self, file_stream): + """Read and export a notebook from a filename - def from_json(self, json): - """ not implemented + file_stream: File handle of file that contains notebook data. - Should convert from a json object + Returns both the converted ipynb file and a dict containing the + resources created along the way via the transformers and Jinja2 + processing. """ - raise NotImplementedError('not implemented (yet?)') + + return self.export(nbformat.read(file_stream, 'json')) \ No newline at end of file