##// END OF EJS Templates
Rearranged htmlnotebook.txt
Rearranged htmlnotebook.txt

File last commit:

r11476:6a905927 merge
r11538:f7a90f06
Show More
exporter.py
441 lines | 15.6 KiB | text/x-python | PythonLexer
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """This module defines Exporter, a highly configurable converter
that uses Jinja2 to export notebook files into different formats.
Matthias BUSSONNIER
starting templates
r9578 """
Matthias BUSSONNIER
fix some relative path issues
r9819
Matthias BUSSONNIER
starting templates
r9578 #-----------------------------------------------------------------------------
Matthias BUSSONNIER
document
r9665 # Copyright (c) 2013, the IPython Development Team.
Matthias BUSSONNIER
starting templates
r9578 #
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
Jonathan Frederic
Cleanup and refactor of API, almost complete....
r10677
Jonathan Frederic
Refactoring for the rename of ConverterTemplate to Exporter.
r10430 from __future__ import print_function, absolute_import
Matthias BUSSONNIER
document
r9665
# Stdlib imports
import io
Matthias BUSSONNIER
fix some relative path issues
r9819 import os
Jonathan Frederic
Fixed problem with data type filter construction.
r10631 import inspect
Jonathan Frederic
Added writers and supporting code.
r11367 import types
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 import copy
import collections
import datetime
Matthias BUSSONNIER
document
r9665
Brian E. Granger
Fixing import for nbconvert.
r11089 # other libs/dependencies
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
Brian E. Granger
Fixing import for nbconvert.
r11089
Matthias BUSSONNIER
document
r9665 # IPython imports
from IPython.config.configurable import Configurable
Matthias BUSSONNIER
start tinkerign with config system
r10862 from IPython.config import Config
Matthias BUSSONNIER
document
r9665 from IPython.nbformat import current as nbformat
Jonathan Frederic
Added writers and supporting code.
r11367 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
from IPython.utils.importstring import import_item
Jonathan Frederic
Removed uneccessary jinja_filters class. Merged code into exporter class....
r10432 from IPython.utils.text import indent
Matthias BUSSONNIER
do a markdown converter
r9701
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 from IPython.nbconvert import transformers as nbtransformers
Brian E. Granger
Fixing import for nbconvert.
r11089 from IPython.nbconvert import filters
Jonathan Frederic
Refactoring for the rename of ConverterTemplate to Exporter.
r10430
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431 #-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
Matthias BUSSONNIER
document
r9665
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431 #Jinja2 extensions to load.
JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
Matthias BUSSONNIER
fix filters documentation....
r10838 default_filters = {
'indent': indent,
MinRK
add markdown2html filter...
r11268 'markdown': filters.markdown2html,
Brian E. Granger
Fixing import for nbconvert.
r11089 'ansi2html': filters.ansi2html,
'filter_data_type': filters.DataTypeFilter,
'get_lines': filters.get_lines,
'highlight': filters.highlight,
'highlight2html': filters.highlight,
'highlight2latex': filters.highlight2latex,
'markdown2latex': filters.markdown2latex,
'markdown2rst': filters.markdown2rst,
'pycomment': filters.python_comment,
'rm_ansi': filters.remove_ansi,
'rm_dollars': filters.strip_dollars,
'rm_fake': filters.rm_fake,
MinRK
add html_text and add_anchor filters...
r11302 'html_text' : filters.html_text,
'add_anchor': filters.add_anchor,
Brian E. Granger
Fixing import for nbconvert.
r11089 'ansi2latex': filters.ansi2latex,
'rm_math_space': filters.rm_math_space,
'wrap': filters.wrap
Matthias BUSSONNIER
fix filters documentation....
r10838 }
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431 #-----------------------------------------------------------------------------
Jonathan Frederic
Cleanup and refactor of API, almost complete....
r10677 # Class
Matthias BUSSONNIER
document
r9665 #-----------------------------------------------------------------------------
Jonathan Frederic
Cleanup and refactor of API, almost complete....
r10677
Jonathan Frederic
Forgot to checkin new ResourcesDict class...
r11398 class ResourcesDict(collections.defaultdict):
def __missing__(self, key):
return ''
Jonathan Frederic
Refactoring for the rename of ConverterTemplate to Exporter.
r10430 class Exporter(Configurable):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Exports notebooks into other file formats. Uses Jinja 2 templating engine
to output new formats. Inherit from this class if you are creating a new
template type along with new filters/transformers. If the filters/
transformers provided by default suffice, there is no need to inherit from
this class. Instead, override the template_file and file_extension
traits via a config file.
Matthias BUSSONNIER
fix filters documentation....
r10838 {filters}
"""
Matthias BUSSONNIER
finish up config merging
r10874
# finish the docstring
Matthias BUSSONNIER
fix filters documentation....
r10838 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431 template_file = Unicode(
'', config=True,
help="Name of the template file to use")
Jonathan Frederic
Added datetime access to Jinja
r9768
Jonathan Frederic
Added latexexporter
r10587 file_extension = Unicode(
Jonathan Frederic
Removed "profiles"... Templates that are shipped with nbconvert by default should...
r10435 'txt', config=True,
help="Extension of the file that should be written to disk"
)
Jonathan Frederic
Finished rename/refact on API namespace
r10690 template_path = Unicode(
MinRK
Don't hardcode posix paths in nbconvert...
r11165 os.path.join("..", "templates"), config=True,
Jonathan Frederic
Finished rename/refact on API namespace
r10690 help="Path where the template files are located.")
template_skeleton_path = Unicode(
MinRK
Don't hardcode posix paths in nbconvert...
r11165 os.path.join("..", "templates", "skeleton"), config=True,
Jonathan Frederic
Finished rename/refact on API namespace
r10690 help="Path where the template skeleton files are located.")
#Jinja block definitions
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 jinja_comment_block_start = Unicode("", config=True)
jinja_comment_block_end = Unicode("", config=True)
jinja_variable_block_start = Unicode("", config=True)
jinja_variable_block_end = Unicode("", config=True)
jinja_logic_block_start = Unicode("", config=True)
jinja_logic_block_end = Unicode("", config=True)
Jonathan Frederic
Finished rename/refact on API namespace
r10690
Jonathan Frederic
Fixed all broken references, refactored some stuff here and there,...
r10624 #Extension that the template files use.
Jonathan Frederic
Finished rename/refact on API namespace
r10690 template_extension = Unicode(".tpl", config=True)
Jonathan Frederic
Fixed all broken references, refactored some stuff here and there,...
r10624
Jonathan Frederic
Added writers and supporting code.
r11367 #Configurability, allows the user to easily add filters and transformers.
transformers = List(config=True,
help="""List of transformers, by name or namespace, to enable.""")
Matthias BUSSONNIER
stateless converter
r9640
Jonathan Frederic
Added writers and supporting code.
r11367 filters = Dict(config=True,
help="""Dictionary of filters, by name and namespace, to add to the Jinja
environment.""")
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383
default_transformers = List([nbtransformers.coalesce_streams,
Jonathan Frederic
Moved default transformer registration into base
r11429 nbtransformers.SVG2PDFTransformer,
nbtransformers.ExtractFigureTransformer,
nbtransformers.CSSHTMLHeaderTransformer,
nbtransformers.RevealHelpTransformer,
nbtransformers.LatexTransformer,
nbtransformers.SphinxTransformer],
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 config=True,
help="""List of transformers available by default, by name, namespace,
instance, or type.""")
Jonathan Frederic
Moved default transformer registration into base
r11429
Jonathan Frederic
Added writers and supporting code.
r11367 def __init__(self, config=None, extra_loaders=None, **kw):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Public constructor
Jonathan Frederic
Post code-review, extended refactor.
r10485
Jonathan Frederic
Finished rename/refact on API namespace
r10690 Parameters
----------
config : config
User configuration instance.
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 extra_loaders : list[of Jinja Loaders]
ordered list of Jinja loder to find templates. Will be tried in order
before the default FileSysteme ones.
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Jonathan Frederic
Removed "profiles"... Templates that are shipped with nbconvert by default should...
r10435 #Call the base class constructor
Matthias BUSSONNIER
fix config inheriting
r10963 c = self.default_config
if config:
c.merge(config)
super(Exporter, self).__init__(config=c, **kw)
Matthias BUSSONNIER
document
r9665
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 #Init
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 self._init_environment(extra_loaders=extra_loaders)
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 self._init_transformers()
self._init_filters()
Matthias BUSSONNIER
create configurable preprocessors
r9624
Matthias BUSSONNIER
start tinkerign with config system
r10862
@property
def default_config(self):
Matthias BUSSONNIER
fix config inheriting
r10963 return Config()
Jonathan Frederic
Finished a rough draft of the exporters.
r10588
Jonathan Frederic
Fixes "bugs with mutable defaults args", thanks @Carreau
r11376 def from_notebook_node(self, nb, resources=None, **kw):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Convert a notebook from a notebook node instance.
Parameters
----------
nb : Notebook node
Jonathan Frederic
Added writers and supporting code.
r11367 resources : dict (**kw)
of additional resources that can be accessed read/write by
transformers and filters.
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 nb_copy = copy.deepcopy(nb)
resources = self._init_resources(resources)
Jonathan Frederic
Changes after in person review with @ellisonbg including TODO tags
r11379
Jonathan Frederic
Added writers and supporting code.
r11367 #Preprocess
Jonathan Frederic
Changes after in person review with @ellisonbg including TODO tags
r11379 nb_copy, resources = self._transform(nb_copy, resources)
Matthias BUSSONNIER
starting templates
r9578
Jonathan Frederic
Added writers and supporting code.
r11367 #Convert
self.template = self.environment.get_template(self.template_file + self.template_extension)
Jonathan Frederic
Make copy of notebook node object to make sure the source doesn't get modified
r11375 output = self.template.render(nb=nb_copy, resources=resources)
Jonathan Frederic
Added writers and supporting code.
r11367 return output, resources
Matthias BUSSONNIER
starting templates
r9578
Jonathan Frederic
Added writers and supporting code.
r11367
Jonathan Frederic
Changes after in person review with @ellisonbg including TODO tags
r11379 def from_filename(self, filename, resources=None, **kw):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Convert a notebook from a notebook file.
Parameters
----------
filename : str
Full filename of the notebook file to open and convert.
"""
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383
#Pull the metadata from the filesystem.
Jonathan Frederic
Fixed case where no resources are provided
r11425 if resources is None:
resources = ResourcesDict()
if not 'metadata' in resources or resources['metadata'] == '':
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 resources['metadata'] = ResourcesDict()
basename = os.path.basename(filename)
notebook_name = basename[:basename.rfind('.')]
resources['metadata']['name'] = notebook_name
modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
Jonathan Frederic
Finished rename/refact on API namespace
r10690
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431 with io.open(filename) as f:
Jonathan Frederic
Added writers and supporting code.
r11367 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
Jonathan Frederic
Create exceptions file to house all of the convert specific exceptions....
r10431
Jonathan Frederic
Removed uneccessary jinja_filters class. Merged code into exporter class....
r10432
Jonathan Frederic
Fixes "bugs with mutable defaults args", thanks @Carreau
r11376 def from_file(self, file_stream, resources=None, **kw):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Convert a notebook from a notebook file.
Parameters
----------
file_stream : file-like object
Notebook file-like object to convert.
"""
Jonathan Frederic
Added writers and supporting code.
r11367 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
Jonathan Frederic
More clean-up. Nomenclature changes
r10578
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 def register_transformer(self, transformer, enabled=False):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Register a transformer.
Transformers are classes that act upon the notebook before it is
passed into the Jinja templating engine. Transformers are also
capable of passing additional information to the Jinja
templating engine.
Parameters
----------
transformer : transformer
"""
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 if transformer is None:
raise TypeError('transformer')
isclass = inspect.isclass(transformer)
constructed = not isclass
Jonathan Frederic
Added writers and supporting code.
r11367
#Handle transformer's registration based on it's type
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 if constructed and isinstance(transformer, types.StringTypes):
#Transformer is a string, import the namespace and recursively call
#this register_transformer method
transformer_cls = import_item(transformer)
return self.register_transformer(transformer_cls, enabled)
Jonathan Frederic
Finished rename/refact on API namespace
r10690
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 if constructed and hasattr(transformer, '__call__'):
Jonathan Frederic
Added writers and supporting code.
r11367 #Transformer is a function, no need to construct it.
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 #Register and return the transformer.
if enabled:
transformer.enabled = True
Jonathan Frederic
Added writers and supporting code.
r11367 self._transformers.append(transformer)
Jonathan Frederic
Fixed problem with data type filter construction.
r10631 return transformer
Jonathan Frederic
Added writers and supporting code.
r11367
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 elif isclass and isinstance(transformer, MetaHasTraits):
Jonathan Frederic
Added writers and supporting code.
r11367 #Transformer is configurable. Make sure to pass in new default for
#the enabled flag if one was specified.
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 self.register_transformer(transformer(parent=self), enabled)
Jonathan Frederic
Added writers and supporting code.
r11367
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 elif isclass:
Jonathan Frederic
Added writers and supporting code.
r11367 #Transformer is not configurable, construct it
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 self.register_transformer(transformer(), enabled)
Jonathan Frederic
Added writers and supporting code.
r11367
Jonathan Frederic
Added latexexporter
r10587 else:
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 #Transformer is an instance of something without a __call__
#attribute.
raise TypeError('transformer')
Jonathan Frederic
Added latexexporter
r10587
Jonathan Frederic
More clean-up. Nomenclature changes
r10578 def register_filter(self, name, filter):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Register a filter.
A filter is a function that accepts and acts on one string.
The filters are accesible within the Jinja templating engine.
Parameters
----------
name : str
name to give the filter in the Jinja engine
filter : filter
"""
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 if filter is None:
raise TypeError('filter')
isclass = inspect.isclass(filter)
constructed = not isclass
#Handle filter's registration based on it's type
if constructed and isinstance(filter, types.StringTypes):
#filter is a string, import the namespace and recursively call
#this register_filter method
filter_cls = import_item(filter)
return self.register_filter(name, filter_cls)
if constructed and hasattr(filter, '__call__'):
#filter is a function, no need to construct it.
Jonathan Frederic
Fixed problem with data type filter construction.
r10631 self.environment.filters[name] = filter
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 return filter
elif isclass and isinstance(filter, MetaHasTraits):
#filter is configurable. Make sure to pass in new default for
#the enabled flag if one was specified.
self.register_filter(name, filter(parent=self))
elif isclass:
#filter is not configurable, construct it
self.register_filter(name, filter())
Jonathan Frederic
More clean-up. Nomenclature changes
r10578 else:
Jonathan Frederic
Fixed logic for register filter and transformer
r11428 #filter is an instance of something without a __call__
#attribute.
raise TypeError('filter')
Jonathan Frederic
More clean-up. Nomenclature changes
r10578
Jonathan Frederic
Finished a rough draft of the exporters.
r10588
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 def _init_environment(self, extra_loaders=None):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Create the Jinja templating environment.
"""
MinRK
fix path prefix in nbconvert...
r11197 here = os.path.dirname(os.path.realpath(__file__))
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 loaders = []
if extra_loaders:
loaders.extend(extra_loaders)
loaders.append(FileSystemLoader([
MinRK
Don't hardcode posix paths in nbconvert...
r11165 os.path.join(here, self.template_path),
os.path.join(here, self.template_skeleton_path),
Matthias BUSSONNIER
Allow custom nbconvert template loaders...
r11279 ]))
self.environment = Environment(
loader= ChoiceLoader(loaders),
Jonathan Frederic
Post code-review, extended refactor.
r10485 extensions=JINJA_EXTENSIONS
)
Jonathan Frederic
Finished rename/refact on API namespace
r10690
#Set special Jinja2 syntax that will not conflict with latex.
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_logic_block_start:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.block_start_string = self.jinja_logic_block_start
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_logic_block_end:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.block_end_string = self.jinja_logic_block_end
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_variable_block_start:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.variable_start_string = self.jinja_variable_block_start
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_variable_block_end:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.variable_end_string = self.jinja_variable_block_end
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_comment_block_start:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.comment_start_string = self.jinja_comment_block_start
Jonathan Frederic
Fixed error due to Unicode traitlets not supporting None type.
r10693 if self.jinja_comment_block_end:
Jonathan Frederic
Finished rename/refact on API namespace
r10690 self.environment.comment_end_string = self.jinja_comment_block_end
Jonathan Frederic
Post code-review, extended refactor.
r10485
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383
def _init_transformers(self):
"""
Register all of the transformers needed for this exporter, disabled
unless specified explicitly.
"""
self._transformers = []
#Load default transformers (not necessarly enabled by default).
if self.default_transformers:
for transformer in self.default_transformers:
self.register_transformer(transformer)
#Load user transformers. Enable by default.
if self.transformers:
for transformer in self.transformers:
self.register_transformer(transformer, enabled=True)
def _init_filters(self):
"""
Register all of the filters required for the exporter.
"""
#Add default filters to the Jinja2 environment
for key, value in default_filters.iteritems():
self.register_filter(key, value)
#Load user filters. Overwrite existing filters if need be.
if self.filters:
for key, user_filter in self.filters.iteritems():
self.register_filter(key, user_filter)
def _init_resources(self, resources):
Jonathan Frederic
Post code-review, extended refactor.
r10485
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383 #Make sure the resources dict is of ResourcesDict type.
if resources is None:
resources = ResourcesDict()
if not isinstance(resources, ResourcesDict):
new_resources = ResourcesDict()
new_resources.update(resources)
resources = new_resources
#Make sure the metadata extension exists in resources
if 'metadata' in resources:
if not isinstance(resources['metadata'], ResourcesDict):
resources['metadata'] = ResourcesDict(resources['metadata'])
else:
resources['metadata'] = ResourcesDict()
Jonathan Frederic
Only set name if it doesn't exist!
r11402 if not resources['metadata']['name']:
Jonathan Frederic
Added commit accidently removed...
r11403 resources['metadata']['name'] = 'Notebook'
Jonathan Frederic
Transformers in traitlet lists now, new _init_ methods,...
r11383
#Set the output extension
resources['output_extension'] = self.file_extension
return resources
Jonathan Frederic
Post code-review, extended refactor.
r10485
Jonathan Frederic
Changes after in person review with @ellisonbg including TODO tags
r11379 def _transform(self, nb, resources):
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Preprocess the notebook before passing it into the Jinja engine.
To preprocess the notebook is to apply all of the
Parameters
----------
nb : notebook node
notebook that is being exported.
Matthias BUSSONNIER
allow to pass a resource dict with notebook...
r10837 resources : a dict of additional resources that
can be accessed read/write by transformers
and filters.
Jonathan Frederic
Finished rename/refact on API namespace
r10690 """
Jonathan Frederic
Changed copy import to be explicit in code
r11399 # Do a copy.deepcopy first,
Matthias BUSSONNIER
to clean (transformer deepcopy)
r10867 # we are never safe enough with what the transformers could do.
Jonathan Frederic
Changed copy import to be explicit in code
r11399 nbc = copy.deepcopy(nb)
resc = copy.deepcopy(resources)
Jonathan Frederic
Added writers and supporting code.
r11367
Jonathan Frederic
Removed uneccessary jinja_filters class. Merged code into exporter class....
r10432 #Run each transformer on the notebook. Carry the output along
#to each transformer
Jonathan Frederic
Added writers and supporting code.
r11367 for transformer in self._transformers:
nbc, resc = transformer(nbc, resc)
return nbc, resc