##// END OF EJS Templates
move NotebookNode to top-level...
MinRK -
Show More
@@ -0,0 +1,21 b''
1 """NotebookNode - adding attribute access to dicts"""
2
3 from IPython.utils.ipstruct import Struct
4
5 class NotebookNode(Struct):
6 """A dict-like node with attribute-access"""
7 pass
8
9 def from_dict(d):
10 """Convert dict to dict-like NotebookNode
11
12 Recursively converts any dict in the container to a NotebookNode
13 """
14 if isinstance(d, dict):
15 return NotebookNode({k:from_dict(v) for k,v in d.items()})
16 elif isinstance(d, (tuple, list)):
17 return [from_dict(i) for i in d]
18 else:
19 return d
20
21
@@ -1,174 +1,174 b''
1 1 """Module containing single call export functions."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from functools import wraps
7 7
8 from IPython.nbformat.v4 import NotebookNode
8 from IPython.nbformat import NotebookNode
9 9 from IPython.utils.decorators import undoc
10 10 from IPython.utils.py3compat import string_types
11 11
12 12 from .exporter import Exporter
13 13 from .templateexporter import TemplateExporter
14 14 from .html import HTMLExporter
15 15 from .slides import SlidesExporter
16 16 from .latex import LatexExporter
17 17 from .pdf import PDFExporter
18 18 from .markdown import MarkdownExporter
19 19 from .python import PythonExporter
20 20 from .rst import RSTExporter
21 21 from .notebook import NotebookExporter
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Classes
25 25 #-----------------------------------------------------------------------------
26 26
27 27 @undoc
28 28 def DocDecorator(f):
29 29
30 30 #Set docstring of function
31 31 f.__doc__ = f.__doc__ + """
32 nb : :class:`~{nbnode_mod}.NotebookNode`
32 nb : :class:`~IPython.nbformat.NotebookNode`
33 33 The notebook to export.
34 34 config : config (optional, keyword arg)
35 35 User configuration instance.
36 36 resources : dict (optional, keyword arg)
37 37 Resources used in the conversion process.
38 38
39 39 Returns
40 40 -------
41 41 tuple- output, resources, exporter_instance
42 42 output : str
43 43 Jinja 2 output. This is the resulting converted notebook.
44 44 resources : dictionary
45 45 Dictionary of resources used prior to and during the conversion
46 46 process.
47 47 exporter_instance : Exporter
48 48 Instance of the Exporter class used to export the document. Useful
49 49 to caller because it provides a 'file_extension' property which
50 50 specifies what extension the output should be saved as.
51 51
52 52 Notes
53 53 -----
54 54 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT
55 """.format(nbnode_mod=NotebookNode.__module__)
55 """
56 56
57 57 @wraps(f)
58 58 def decorator(*args, **kwargs):
59 59 return f(*args, **kwargs)
60 60
61 61 return decorator
62 62
63 63
64 64 #-----------------------------------------------------------------------------
65 65 # Functions
66 66 #-----------------------------------------------------------------------------
67 67
68 68 __all__ = [
69 69 'export',
70 70 'export_html',
71 71 'export_custom',
72 72 'export_slides',
73 73 'export_latex',
74 74 'export_pdf',
75 75 'export_markdown',
76 76 'export_python',
77 77 'export_rst',
78 78 'export_by_name',
79 79 'get_export_names',
80 80 'ExporterNameError'
81 81 ]
82 82
83 83
84 84 class ExporterNameError(NameError):
85 85 pass
86 86
87 87 @DocDecorator
88 88 def export(exporter, nb, **kw):
89 89 """
90 90 Export a notebook object using specific exporter class.
91 91
92 92 Parameters
93 93 ----------
94 94 exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance
95 95 Class type or instance of the exporter that should be used. If the
96 96 method initializes it's own instance of the class, it is ASSUMED that
97 97 the class type provided exposes a constructor (``__init__``) with the same
98 98 signature as the base Exporter class.
99 99 """
100 100
101 101 #Check arguments
102 102 if exporter is None:
103 103 raise TypeError("Exporter is None")
104 104 elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
105 105 raise TypeError("exporter does not inherit from Exporter (base)")
106 106 if nb is None:
107 107 raise TypeError("nb is None")
108 108
109 109 #Create the exporter
110 110 resources = kw.pop('resources', None)
111 111 if isinstance(exporter, Exporter):
112 112 exporter_instance = exporter
113 113 else:
114 114 exporter_instance = exporter(**kw)
115 115
116 116 #Try to convert the notebook using the appropriate conversion function.
117 117 if isinstance(nb, NotebookNode):
118 118 output, resources = exporter_instance.from_notebook_node(nb, resources)
119 119 elif isinstance(nb, string_types):
120 120 output, resources = exporter_instance.from_filename(nb, resources)
121 121 else:
122 122 output, resources = exporter_instance.from_file(nb, resources)
123 123 return output, resources
124 124
125 125 exporter_map = dict(
126 126 custom=TemplateExporter,
127 127 html=HTMLExporter,
128 128 slides=SlidesExporter,
129 129 latex=LatexExporter,
130 130 pdf=PDFExporter,
131 131 markdown=MarkdownExporter,
132 132 python=PythonExporter,
133 133 rst=RSTExporter,
134 134 notebook=NotebookExporter,
135 135 )
136 136
137 137 def _make_exporter(name, E):
138 138 """make an export_foo function from a short key and Exporter class E"""
139 139 def _export(nb, **kw):
140 140 return export(E, nb, **kw)
141 141 _export.__doc__ = """Export a notebook object to {0} format""".format(name)
142 142 return _export
143 143
144 144 g = globals()
145 145
146 146 for name, E in exporter_map.items():
147 147 g['export_%s' % name] = DocDecorator(_make_exporter(name, E))
148 148
149 149 @DocDecorator
150 150 def export_by_name(format_name, nb, **kw):
151 151 """
152 152 Export a notebook object to a template type by its name. Reflection
153 153 (Inspect) is used to find the template's corresponding explicit export
154 154 method defined in this module. That method is then called directly.
155 155
156 156 Parameters
157 157 ----------
158 158 format_name : str
159 159 Name of the template style to export to.
160 160 """
161 161
162 162 function_name = "export_" + format_name.lower()
163 163
164 164 if function_name in globals():
165 165 return globals()[function_name](nb, **kw)
166 166 else:
167 167 raise ExporterNameError("template for `%s` not found" % function_name)
168 168
169 169
170 170 def get_export_names():
171 171 """Return a list of the currently supported export targets
172 172
173 173 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT"""
174 174 return sorted(exporter_map.keys())
@@ -1,259 +1,259 b''
1 1 """This module defines a base Exporter class. For Jinja template-based export,
2 2 see templateexporter.py.
3 3 """
4 4
5 5
6 6 from __future__ import print_function, absolute_import
7 7
8 8 import io
9 9 import os
10 10 import copy
11 11 import collections
12 12 import datetime
13 13
14 14 from IPython.config.configurable import LoggingConfigurable
15 15 from IPython.config import Config
16 16 from IPython import nbformat
17 17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
18 18 from IPython.utils.importstring import import_item
19 19 from IPython.utils import text, py3compat
20 20
21 21
22 22 class ResourcesDict(collections.defaultdict):
23 23 def __missing__(self, key):
24 24 return ''
25 25
26 26
27 27 class Exporter(LoggingConfigurable):
28 28 """
29 29 Class containing methods that sequentially run a list of preprocessors on a
30 30 NotebookNode object and then return the modified NotebookNode object and
31 31 accompanying resources dict.
32 32 """
33 33
34 34 file_extension = Unicode(
35 35 'txt', config=True,
36 36 help="Extension of the file that should be written to disk"
37 37 )
38 38
39 39 # MIME type of the result file, for HTTP response headers.
40 40 # This is *not* a traitlet, because we want to be able to access it from
41 41 # the class, not just on instances.
42 42 output_mimetype = ''
43 43
44 44 #Configurability, allows the user to easily add filters and preprocessors.
45 45 preprocessors = List(config=True,
46 46 help="""List of preprocessors, by name or namespace, to enable.""")
47 47
48 48 _preprocessors = List()
49 49
50 50 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
51 51 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
52 52 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
53 53 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
54 54 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
55 55 'IPython.nbconvert.preprocessors.LatexPreprocessor',
56 56 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
57 57 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
58 58 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
59 59 config=True,
60 60 help="""List of preprocessors available by default, by name, namespace,
61 61 instance, or type.""")
62 62
63 63
64 64 def __init__(self, config=None, **kw):
65 65 """
66 66 Public constructor
67 67
68 68 Parameters
69 69 ----------
70 70 config : config
71 71 User configuration instance.
72 72 """
73 73 with_default_config = self.default_config
74 74 if config:
75 75 with_default_config.merge(config)
76 76
77 77 super(Exporter, self).__init__(config=with_default_config, **kw)
78 78
79 79 self._init_preprocessors()
80 80
81 81
82 82 @property
83 83 def default_config(self):
84 84 return Config()
85 85
86 86 def from_notebook_node(self, nb, resources=None, **kw):
87 87 """
88 88 Convert a notebook from a notebook node instance.
89 89
90 90 Parameters
91 91 ----------
92 nb : :class:`~IPython.nbformat.current.NotebookNode`
92 nb : :class:`~IPython.nbformat.NotebookNode`
93 93 Notebook node (dict-like with attr-access)
94 94 resources : dict
95 95 Additional resources that can be accessed read/write by
96 96 preprocessors and filters.
97 97 **kw
98 98 Ignored (?)
99 99 """
100 100 nb_copy = copy.deepcopy(nb)
101 101 resources = self._init_resources(resources)
102 102
103 103 if 'language' in nb['metadata']:
104 104 resources['language'] = nb['metadata']['language'].lower()
105 105
106 106 # Preprocess
107 107 nb_copy, resources = self._preprocess(nb_copy, resources)
108 108
109 109 return nb_copy, resources
110 110
111 111
112 112 def from_filename(self, filename, resources=None, **kw):
113 113 """
114 114 Convert a notebook from a notebook file.
115 115
116 116 Parameters
117 117 ----------
118 118 filename : str
119 119 Full filename of the notebook file to open and convert.
120 120 """
121 121
122 122 # Pull the metadata from the filesystem.
123 123 if resources is None:
124 124 resources = ResourcesDict()
125 125 if not 'metadata' in resources or resources['metadata'] == '':
126 126 resources['metadata'] = ResourcesDict()
127 127 basename = os.path.basename(filename)
128 128 notebook_name = basename[:basename.rfind('.')]
129 129 resources['metadata']['name'] = notebook_name
130 130
131 131 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
132 132 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
133 133
134 134 with io.open(filename, encoding='utf-8') as f:
135 135 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
136 136
137 137
138 138 def from_file(self, file_stream, resources=None, **kw):
139 139 """
140 140 Convert a notebook from a notebook file.
141 141
142 142 Parameters
143 143 ----------
144 144 file_stream : file-like object
145 145 Notebook file-like object to convert.
146 146 """
147 147 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
148 148
149 149
150 150 def register_preprocessor(self, preprocessor, enabled=False):
151 151 """
152 152 Register a preprocessor.
153 153 Preprocessors are classes that act upon the notebook before it is
154 154 passed into the Jinja templating engine. preprocessors are also
155 155 capable of passing additional information to the Jinja
156 156 templating engine.
157 157
158 158 Parameters
159 159 ----------
160 160 preprocessor : preprocessor
161 161 """
162 162 if preprocessor is None:
163 163 raise TypeError('preprocessor')
164 164 isclass = isinstance(preprocessor, type)
165 165 constructed = not isclass
166 166
167 167 # Handle preprocessor's registration based on it's type
168 168 if constructed and isinstance(preprocessor, py3compat.string_types):
169 169 # Preprocessor is a string, import the namespace and recursively call
170 170 # this register_preprocessor method
171 171 preprocessor_cls = import_item(preprocessor)
172 172 return self.register_preprocessor(preprocessor_cls, enabled)
173 173
174 174 if constructed and hasattr(preprocessor, '__call__'):
175 175 # Preprocessor is a function, no need to construct it.
176 176 # Register and return the preprocessor.
177 177 if enabled:
178 178 preprocessor.enabled = True
179 179 self._preprocessors.append(preprocessor)
180 180 return preprocessor
181 181
182 182 elif isclass and isinstance(preprocessor, MetaHasTraits):
183 183 # Preprocessor is configurable. Make sure to pass in new default for
184 184 # the enabled flag if one was specified.
185 185 self.register_preprocessor(preprocessor(parent=self), enabled)
186 186
187 187 elif isclass:
188 188 # Preprocessor is not configurable, construct it
189 189 self.register_preprocessor(preprocessor(), enabled)
190 190
191 191 else:
192 192 # Preprocessor is an instance of something without a __call__
193 193 # attribute.
194 194 raise TypeError('preprocessor')
195 195
196 196
197 197 def _init_preprocessors(self):
198 198 """
199 199 Register all of the preprocessors needed for this exporter, disabled
200 200 unless specified explicitly.
201 201 """
202 202 self._preprocessors = []
203 203
204 204 # Load default preprocessors (not necessarly enabled by default).
205 205 for preprocessor in self.default_preprocessors:
206 206 self.register_preprocessor(preprocessor)
207 207
208 208 # Load user-specified preprocessors. Enable by default.
209 209 for preprocessor in self.preprocessors:
210 210 self.register_preprocessor(preprocessor, enabled=True)
211 211
212 212
213 213 def _init_resources(self, resources):
214 214
215 215 #Make sure the resources dict is of ResourcesDict type.
216 216 if resources is None:
217 217 resources = ResourcesDict()
218 218 if not isinstance(resources, ResourcesDict):
219 219 new_resources = ResourcesDict()
220 220 new_resources.update(resources)
221 221 resources = new_resources
222 222
223 223 #Make sure the metadata extension exists in resources
224 224 if 'metadata' in resources:
225 225 if not isinstance(resources['metadata'], ResourcesDict):
226 226 resources['metadata'] = ResourcesDict(resources['metadata'])
227 227 else:
228 228 resources['metadata'] = ResourcesDict()
229 229 if not resources['metadata']['name']:
230 230 resources['metadata']['name'] = 'Notebook'
231 231
232 232 #Set the output extension
233 233 resources['output_extension'] = self.file_extension
234 234 return resources
235 235
236 236
237 237 def _preprocess(self, nb, resources):
238 238 """
239 239 Preprocess the notebook before passing it into the Jinja engine.
240 240 To preprocess the notebook is to apply all of the
241 241
242 242 Parameters
243 243 ----------
244 244 nb : notebook node
245 245 notebook that is being exported.
246 246 resources : a dict of additional resources that
247 247 can be accessed read/write by preprocessors
248 248 """
249 249
250 250 # Do a copy.deepcopy first,
251 251 # we are never safe enough with what the preprocessors could do.
252 252 nbc = copy.deepcopy(nb)
253 253 resc = copy.deepcopy(resources)
254 254
255 255 #Run each preprocessor on the notebook. Carry the output along
256 256 #to each preprocessor
257 257 for preprocessor in self._preprocessors:
258 258 nbc, resc = preprocessor(nbc, resc)
259 259 return nbc, resc
@@ -1,321 +1,321 b''
1 1 """This module defines TemplateExporter, a highly configurable converter
2 2 that uses Jinja2 to export notebook files into different formats.
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from __future__ import print_function, absolute_import
18 18
19 19 # Stdlib imports
20 20 import os
21 21
22 22 # other libs/dependencies are imported at runtime
23 23 # to move ImportErrors to runtime when the requirement is actually needed
24 24
25 25 # IPython imports
26 26 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any
27 27 from IPython.utils.importstring import import_item
28 28 from IPython.utils import py3compat, text
29 29
30 30 from IPython.nbconvert import filters
31 31 from .exporter import Exporter
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Globals and constants
35 35 #-----------------------------------------------------------------------------
36 36
37 37 #Jinja2 extensions to load.
38 38 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
39 39
40 40 default_filters = {
41 41 'indent': text.indent,
42 42 'markdown2html': filters.markdown2html,
43 43 'ansi2html': filters.ansi2html,
44 44 'filter_data_type': filters.DataTypeFilter,
45 45 'get_lines': filters.get_lines,
46 46 'highlight2html': filters.Highlight2HTML,
47 47 'highlight2latex': filters.Highlight2Latex,
48 48 'ipython2python': filters.ipython2python,
49 49 'posix_path': filters.posix_path,
50 50 'markdown2latex': filters.markdown2latex,
51 51 'markdown2rst': filters.markdown2rst,
52 52 'comment_lines': filters.comment_lines,
53 53 'strip_ansi': filters.strip_ansi,
54 54 'strip_dollars': filters.strip_dollars,
55 55 'strip_files_prefix': filters.strip_files_prefix,
56 56 'html2text' : filters.html2text,
57 57 'add_anchor': filters.add_anchor,
58 58 'ansi2latex': filters.ansi2latex,
59 59 'wrap_text': filters.wrap_text,
60 60 'escape_latex': filters.escape_latex,
61 61 'citation2latex': filters.citation2latex,
62 62 'path2url': filters.path2url,
63 63 'add_prompts': filters.add_prompts,
64 64 'ascii_only': filters.ascii_only,
65 65 'prevent_list_blocks': filters.prevent_list_blocks,
66 66 }
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Class
70 70 #-----------------------------------------------------------------------------
71 71
72 72 class TemplateExporter(Exporter):
73 73 """
74 74 Exports notebooks into other file formats. Uses Jinja 2 templating engine
75 75 to output new formats. Inherit from this class if you are creating a new
76 76 template type along with new filters/preprocessors. If the filters/
77 77 preprocessors provided by default suffice, there is no need to inherit from
78 78 this class. Instead, override the template_file and file_extension
79 79 traits via a config file.
80 80
81 81 {filters}
82 82 """
83 83
84 84 # finish the docstring
85 85 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
86 86
87 87
88 88 template_file = Unicode(u'default',
89 89 config=True,
90 90 help="Name of the template file to use")
91 91 def _template_file_changed(self, name, old, new):
92 92 if new == 'default':
93 93 self.template_file = self.default_template
94 94 else:
95 95 self.template_file = new
96 96 self.template = None
97 97 self._load_template()
98 98
99 99 default_template = Unicode(u'')
100 100 template = Any()
101 101 environment = Any()
102 102
103 103 template_path = List(['.'], config=True)
104 104 def _template_path_changed(self, name, old, new):
105 105 self._load_template()
106 106
107 107 default_template_path = Unicode(
108 108 os.path.join("..", "templates"),
109 109 help="Path where the template files are located.")
110 110
111 111 template_skeleton_path = Unicode(
112 112 os.path.join("..", "templates", "skeleton"),
113 113 help="Path where the template skeleton files are located.")
114 114
115 115 #Jinja block definitions
116 116 jinja_comment_block_start = Unicode("", config=True)
117 117 jinja_comment_block_end = Unicode("", config=True)
118 118 jinja_variable_block_start = Unicode("", config=True)
119 119 jinja_variable_block_end = Unicode("", config=True)
120 120 jinja_logic_block_start = Unicode("", config=True)
121 121 jinja_logic_block_end = Unicode("", config=True)
122 122
123 123 #Extension that the template files use.
124 124 template_extension = Unicode(".tpl", config=True)
125 125
126 126 filters = Dict(config=True,
127 127 help="""Dictionary of filters, by name and namespace, to add to the Jinja
128 128 environment.""")
129 129
130 130 raw_mimetypes = List(config=True,
131 131 help="""formats of raw cells to be included in this Exporter's output."""
132 132 )
133 133 def _raw_mimetypes_default(self):
134 134 return [self.output_mimetype, '']
135 135
136 136
137 137 def __init__(self, config=None, extra_loaders=None, **kw):
138 138 """
139 139 Public constructor
140 140
141 141 Parameters
142 142 ----------
143 143 config : config
144 144 User configuration instance.
145 145 extra_loaders : list[of Jinja Loaders]
146 146 ordered list of Jinja loader to find templates. Will be tried in order
147 147 before the default FileSystem ones.
148 148 template : str (optional, kw arg)
149 149 Template to use when exporting.
150 150 """
151 151 super(TemplateExporter, self).__init__(config=config, **kw)
152 152
153 153 #Init
154 154 self._init_template()
155 155 self._init_environment(extra_loaders=extra_loaders)
156 156 self._init_filters()
157 157
158 158
159 159 def _load_template(self):
160 160 """Load the Jinja template object from the template file
161 161
162 162 This is a no-op if the template attribute is already defined,
163 163 or the Jinja environment is not setup yet.
164 164
165 165 This is triggered by various trait changes that would change the template.
166 166 """
167 167 from jinja2 import TemplateNotFound
168 168
169 169 if self.template is not None:
170 170 return
171 171 # called too early, do nothing
172 172 if self.environment is None:
173 173 return
174 174 # Try different template names during conversion. First try to load the
175 175 # template by name with extension added, then try loading the template
176 176 # as if the name is explicitly specified, then try the name as a
177 177 # 'flavor', and lastly just try to load the template by module name.
178 178 try_names = []
179 179 if self.template_file:
180 180 try_names.extend([
181 181 self.template_file + self.template_extension,
182 182 self.template_file,
183 183 ])
184 184 for try_name in try_names:
185 185 self.log.debug("Attempting to load template %s", try_name)
186 186 try:
187 187 self.template = self.environment.get_template(try_name)
188 188 except (TemplateNotFound, IOError):
189 189 pass
190 190 except Exception as e:
191 191 self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True)
192 192 else:
193 193 self.log.info("Loaded template %s", try_name)
194 194 break
195 195
196 196 def from_notebook_node(self, nb, resources=None, **kw):
197 197 """
198 198 Convert a notebook from a notebook node instance.
199 199
200 200 Parameters
201 201 ----------
202 nb : :class:`~IPython.nbformat.current.NotebookNode`
202 nb : :class:`~IPython.nbformat.NotebookNode`
203 203 Notebook node
204 204 resources : dict
205 205 Additional resources that can be accessed read/write by
206 206 preprocessors and filters.
207 207 """
208 208 nb_copy, resources = super(TemplateExporter, self).from_notebook_node(nb, resources, **kw)
209 209 resources.setdefault('raw_mimetypes', self.raw_mimetypes)
210 210
211 211 self._load_template()
212 212
213 213 if self.template is not None:
214 214 output = self.template.render(nb=nb_copy, resources=resources)
215 215 else:
216 216 raise IOError('template file "%s" could not be found' % self.template_file)
217 217 return output, resources
218 218
219 219
220 220 def register_filter(self, name, jinja_filter):
221 221 """
222 222 Register a filter.
223 223 A filter is a function that accepts and acts on one string.
224 224 The filters are accesible within the Jinja templating engine.
225 225
226 226 Parameters
227 227 ----------
228 228 name : str
229 229 name to give the filter in the Jinja engine
230 230 filter : filter
231 231 """
232 232 if jinja_filter is None:
233 233 raise TypeError('filter')
234 234 isclass = isinstance(jinja_filter, type)
235 235 constructed = not isclass
236 236
237 237 #Handle filter's registration based on it's type
238 238 if constructed and isinstance(jinja_filter, py3compat.string_types):
239 239 #filter is a string, import the namespace and recursively call
240 240 #this register_filter method
241 241 filter_cls = import_item(jinja_filter)
242 242 return self.register_filter(name, filter_cls)
243 243
244 244 if constructed and hasattr(jinja_filter, '__call__'):
245 245 #filter is a function, no need to construct it.
246 246 self.environment.filters[name] = jinja_filter
247 247 return jinja_filter
248 248
249 249 elif isclass and isinstance(jinja_filter, MetaHasTraits):
250 250 #filter is configurable. Make sure to pass in new default for
251 251 #the enabled flag if one was specified.
252 252 filter_instance = jinja_filter(parent=self)
253 253 self.register_filter(name, filter_instance )
254 254
255 255 elif isclass:
256 256 #filter is not configurable, construct it
257 257 filter_instance = jinja_filter()
258 258 self.register_filter(name, filter_instance)
259 259
260 260 else:
261 261 #filter is an instance of something without a __call__
262 262 #attribute.
263 263 raise TypeError('filter')
264 264
265 265
266 266 def _init_template(self):
267 267 """
268 268 Make sure a template name is specified. If one isn't specified, try to
269 269 build one from the information we know.
270 270 """
271 271 self._template_file_changed('template_file', self.template_file, self.template_file)
272 272
273 273
274 274 def _init_environment(self, extra_loaders=None):
275 275 """
276 276 Create the Jinja templating environment.
277 277 """
278 278 from jinja2 import Environment, ChoiceLoader, FileSystemLoader
279 279 here = os.path.dirname(os.path.realpath(__file__))
280 280 loaders = []
281 281 if extra_loaders:
282 282 loaders.extend(extra_loaders)
283 283
284 284 paths = self.template_path
285 285 paths.extend([os.path.join(here, self.default_template_path),
286 286 os.path.join(here, self.template_skeleton_path)])
287 287 loaders.append(FileSystemLoader(paths))
288 288
289 289 self.environment = Environment(
290 290 loader= ChoiceLoader(loaders),
291 291 extensions=JINJA_EXTENSIONS
292 292 )
293 293
294 294 #Set special Jinja2 syntax that will not conflict with latex.
295 295 if self.jinja_logic_block_start:
296 296 self.environment.block_start_string = self.jinja_logic_block_start
297 297 if self.jinja_logic_block_end:
298 298 self.environment.block_end_string = self.jinja_logic_block_end
299 299 if self.jinja_variable_block_start:
300 300 self.environment.variable_start_string = self.jinja_variable_block_start
301 301 if self.jinja_variable_block_end:
302 302 self.environment.variable_end_string = self.jinja_variable_block_end
303 303 if self.jinja_comment_block_start:
304 304 self.environment.comment_start_string = self.jinja_comment_block_start
305 305 if self.jinja_comment_block_end:
306 306 self.environment.comment_end_string = self.jinja_comment_block_end
307 307
308 308
309 309 def _init_filters(self):
310 310 """
311 311 Register all of the filters required for the exporter.
312 312 """
313 313
314 314 #Add default filters to the Jinja2 environment
315 315 for key, value in default_filters.items():
316 316 self.register_filter(key, value)
317 317
318 318 #Load user filters. Overwrite existing filters if need be.
319 319 if self.filters:
320 320 for key, user_filter in self.filters.items():
321 321 self.register_filter(key, user_filter)
@@ -1,143 +1,144 b''
1 1 """The IPython notebook format
2 2
3 3 Use this module to read or write notebook files as particular nbformat versions.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 from IPython.utils.log import get_logger
10 10
11 11 from . import v1
12 12 from . import v2
13 13 from . import v3
14 14 from . import v4
15 15
16 16 versions = {
17 17 1: v1,
18 18 2: v2,
19 19 3: v3,
20 20 4: v4,
21 21 }
22 22
23 23 from .validator import validate, ValidationError
24 24 from .converter import convert
25 25 from . import reader
26 from .notebooknode import from_dict, NotebookNode
26 27
27 28 from .v4 import (
28 29 nbformat as current_nbformat,
29 30 nbformat_minor as current_nbformat_minor,
30 31 )
31 32
32 33 class NBFormatError(ValueError):
33 34 pass
34 35
35 36 # no-conversion singleton
36 37 NO_CONVERT = object()
37 38
38 39 def reads(s, as_version, **kwargs):
39 40 """Read a notebook from a string and return the NotebookNode object as the given version.
40 41
41 42 The string can contain a notebook of any version.
42 43 The notebook will be returned `as_version`, converting, if necessary.
43 44
44 45 Notebook format errors will be logged.
45 46
46 47 Parameters
47 48 ----------
48 49 s : unicode
49 50 The raw unicode string to read the notebook from.
50 51 as_version : int
51 52 The version of the notebook format to return.
52 53 The notebook will be converted, if necessary.
53 54 Pass nbformat.NO_CONVERT to prevent conversion.
54 55
55 56 Returns
56 57 -------
57 58 nb : NotebookNode
58 59 The notebook that was read.
59 60 """
60 61 nb = reader.reads(s, **kwargs)
61 62 if as_version is not NO_CONVERT:
62 63 nb = convert(nb, as_version)
63 64 try:
64 65 validate(nb)
65 66 except ValidationError as e:
66 67 get_logger().error("Notebook JSON is invalid: %s", e)
67 68 return nb
68 69
69 70
70 71 def writes(nb, version, **kwargs):
71 72 """Write a notebook to a string in a given format in the given nbformat version.
72 73
73 74 Any notebook format errors will be logged.
74 75
75 76 Parameters
76 77 ----------
77 78 nb : NotebookNode
78 79 The notebook to write.
79 80 version : int
80 81 The nbformat version to write.
81 82 If nb is not this version, it will be converted.
82 83 Pass nbformat.NO_CONVERT to prevent conversion.
83 84
84 85 Returns
85 86 -------
86 87 s : unicode
87 88 The notebook as a JSON string.
88 89 """
89 90 if version is not NO_CONVERT:
90 91 nb = convert(nb, version)
91 92 else:
92 93 version, _ = reader.get_version(nb)
93 94 try:
94 95 validate(nb)
95 96 except ValidationError as e:
96 97 get_logger().error("Notebook JSON is invalid: %s", e)
97 98 return versions[version].writes_json(nb, **kwargs)
98 99
99 100
100 101 def read(fp, as_version, **kwargs):
101 102 """Read a notebook from a file as a NotebookNode of the given version.
102 103
103 104 The string can contain a notebook of any version.
104 105 The notebook will be returned `as_version`, converting, if necessary.
105 106
106 107 Notebook format errors will be logged.
107 108
108 109 Parameters
109 110 ----------
110 111 fp : file
111 112 Any file-like object with a read method.
112 113 as_version: int
113 114 The version of the notebook format to return.
114 115 The notebook will be converted, if necessary.
115 116 Pass nbformat.NO_CONVERT to prevent conversion.
116 117
117 118 Returns
118 119 -------
119 120 nb : NotebookNode
120 121 The notebook that was read.
121 122 """
122 123 return reads(fp.read(), as_version, **kwargs)
123 124
124 125
125 126 def write(fp, nb, version, **kwargs):
126 127 """Write a notebook to a file in a given nbformat version.
127 128
128 129 The file-like object must accept unicode input.
129 130
130 131 Parameters
131 132 ----------
132 133 fp : file
133 134 Any file-like object with a write method that accepts unicode.
134 135 nb : NotebookNode
135 136 The notebook to write.
136 137 version : int
137 138 The nbformat version to write.
138 139 If nb is not this version, it will be converted.
139 140 """
140 141 s = writes(nb, version, **kwargs)
141 142 if isinstance(s, bytes):
142 143 s = s.decode('utf8')
143 144 return fp.write(s)
@@ -1,19 +1,19 b''
1 1 """The main API for the v4 notebook format."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from .nbbase import (
7 NotebookNode, from_dict,
8 7 nbformat, nbformat_minor, nbformat_schema,
9 8 new_code_cell, new_markdown_cell, new_notebook,
10 9 new_output, output_from_msg,
11 10 )
12 11
13 from .nbjson import reads as reads_json, writes as writes_json
14 from .nbjson import reads as read_json, writes as write_json
15 from .nbjson import to_notebook as to_notebook_json
12 from .nbjson import reads, writes, to_notebook
13 reads_json = reads
14 writes_json = writes
15 to_notebook_json = to_notebook
16 16
17 17 from .convert import downgrade, upgrade
18 18
19 19
@@ -1,149 +1,137 b''
1 1 """Python API for composing notebook elements
2 2
3 3 The Python representation of a notebook is a nested structure of
4 4 dictionary subclasses that support attribute access
5 5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
6 6 helpers to build the structs in the right form.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 from IPython.utils.ipstruct import Struct
12 from ..notebooknode import from_dict, NotebookNode
13 13
14 14 # Change this when incrementing the nbformat version
15 15 nbformat = 4
16 16 nbformat_minor = 0
17 17 nbformat_schema = 'nbformat.v4.schema.json'
18 18
19 19
20 20 def validate(node, ref=None):
21 21 """validate a v4 node"""
22 22 from .. import validate
23 23 return validate(node, ref=ref, version=nbformat)
24 24
25 25
26 class NotebookNode(Struct):
27 pass
28
29 def from_dict(d):
30 if isinstance(d, dict):
31 return NotebookNode({k:from_dict(v) for k,v in d.items()})
32 elif isinstance(d, (tuple, list)):
33 return [from_dict(i) for i in d]
34 else:
35 return d
36
37
38 26 def new_output(output_type, data=None, **kwargs):
39 27 """Create a new output, to go in the ``cell.outputs`` list of a code cell."""
40 28 output = NotebookNode(output_type=output_type)
41 29
42 30 # populate defaults:
43 31 if output_type == 'stream':
44 32 output.name = u'stdout'
45 33 output.text = u''
46 34 elif output_type in {'execute_result', 'display_data'}:
47 35 output.metadata = NotebookNode()
48 36 output.data = NotebookNode()
49 37 # load from args:
50 38 output.update(from_dict(kwargs))
51 39 if data is not None:
52 40 output.data = from_dict(data)
53 41 # validate
54 42 validate(output, output_type)
55 43 return output
56 44
57 45
58 46 def output_from_msg(msg):
59 47 """Create a NotebookNode for an output from a kernel's IOPub message.
60 48
61 49 Returns
62 50 -------
63 51
64 52 NotebookNode: the output as a notebook node.
65 53
66 54 Raises
67 55 ------
68 56
69 57 ValueError: if the message is not an output message.
70 58
71 59 """
72 60 msg_type = msg['header']['msg_type']
73 61 content = msg['content']
74 62
75 63 if msg_type == 'execute_result':
76 64 return new_output(output_type=msg_type,
77 65 metadata=content['metadata'],
78 66 data=content['data'],
79 67 execution_count=content['execution_count'],
80 68 )
81 69 elif msg_type == 'stream':
82 70 return new_output(output_type=msg_type,
83 71 name=content['name'],
84 72 text=content['text'],
85 73 )
86 74 elif msg_type == 'display_data':
87 75 return new_output(output_type=msg_type,
88 76 metadata=content['metadata'],
89 77 data=content['data'],
90 78 )
91 79 elif msg_type == 'error':
92 80 return new_output(output_type=msg_type,
93 81 ename=content['ename'],
94 82 evalue=content['evalue'],
95 83 traceback=content['traceback'],
96 84 )
97 85 else:
98 86 raise ValueError("Unrecognized output msg type: %r" % msg_type)
99 87
100 88
101 89 def new_code_cell(source='', **kwargs):
102 90 """Create a new code cell"""
103 91 cell = NotebookNode(
104 92 cell_type='code',
105 93 metadata=NotebookNode(),
106 94 execution_count=None,
107 95 source=source,
108 96 outputs=[],
109 97 )
110 98 cell.update(from_dict(kwargs))
111 99
112 100 validate(cell, 'code_cell')
113 101 return cell
114 102
115 103 def new_markdown_cell(source='', **kwargs):
116 104 """Create a new markdown cell"""
117 105 cell = NotebookNode(
118 106 cell_type='markdown',
119 107 source=source,
120 108 metadata=NotebookNode(),
121 109 )
122 110 cell.update(from_dict(kwargs))
123 111
124 112 validate(cell, 'markdown_cell')
125 113 return cell
126 114
127 115 def new_raw_cell(source='', **kwargs):
128 116 """Create a new raw cell"""
129 117 cell = NotebookNode(
130 118 cell_type='raw',
131 119 source=source,
132 120 metadata=NotebookNode(),
133 121 )
134 122 cell.update(from_dict(kwargs))
135 123
136 124 validate(cell, 'raw_cell')
137 125 return cell
138 126
139 127 def new_notebook(**kwargs):
140 128 """Create a new notebook"""
141 129 nb = NotebookNode(
142 130 nbformat=nbformat,
143 131 nbformat_minor=nbformat_minor,
144 132 metadata=NotebookNode(),
145 133 cells=[],
146 134 )
147 135 nb.update(from_dict(kwargs))
148 136 validate(nb)
149 137 return nb
@@ -1,60 +1,67 b''
1 1 """Read and write notebooks in JSON format."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import copy
7 7 import json
8 8
9 9 from IPython.utils import py3compat
10 10
11 11 from .nbbase import from_dict
12 12 from .rwbase import (
13 13 NotebookReader, NotebookWriter, rejoin_lines, split_lines, strip_transient
14 14 )
15 15
16 16
17 17 class BytesEncoder(json.JSONEncoder):
18 18 """A JSON encoder that accepts b64 (and other *ascii*) bytestrings."""
19 19 def default(self, obj):
20 20 if isinstance(obj, bytes):
21 21 return obj.decode('ascii')
22 22 return json.JSONEncoder.default(self, obj)
23 23
24 24
25 25 class JSONReader(NotebookReader):
26 26
27 27 def reads(self, s, **kwargs):
28 """Read a JSON string into a Notebook object"""
28 29 nb = json.loads(s, **kwargs)
29 30 nb = self.to_notebook(nb, **kwargs)
30 31 return nb
31 32
32 33 def to_notebook(self, d, **kwargs):
33 nb = rejoin_lines(from_dict(d))
34 """Convert a disk-format notebook dict to in-memory NotebookNode
35
36 handles multi-line values as strings, scrubbing of transient values, etc.
37 """
38 nb = from_dict(d)
39 nb = rejoin_lines(nb)
34 40 nb = strip_transient(nb)
35 41 return nb
36 42
37 43
38 44 class JSONWriter(NotebookWriter):
39 45
40 46 def writes(self, nb, **kwargs):
47 """Serialize a NotebookNode object as a JSON string"""
41 48 kwargs['cls'] = BytesEncoder
42 49 kwargs['indent'] = 1
43 50 kwargs['sort_keys'] = True
44 51 kwargs['separators'] = (',',': ')
45 52 # don't modify in-memory dict
46 53 nb = copy.deepcopy(nb)
47 54 if kwargs.pop('split_lines', True):
48 55 nb = split_lines(nb)
49 56 nb = strip_transient(nb)
50 57 return py3compat.str_to_unicode(json.dumps(nb, **kwargs), 'utf-8')
51 58
52 59
53 60 _reader = JSONReader()
54 61 _writer = JSONWriter()
55 62
56 63 reads = _reader.reads
57 64 read = _reader.read
58 65 to_notebook = _reader.to_notebook
59 66 write = _writer.write
60 67 writes = _writer.writes
General Comments 0
You need to be logged in to leave comments. Login now