##// END OF EJS Templates
Remove docstring_nbformat_mod decorator
Thomas Kluyver -
Show More
@@ -1,277 +1,276 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 # 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 io
21 21 import os
22 22 import copy
23 23 import collections
24 24 import datetime
25 25
26 26
27 27 # IPython imports
28 28 from IPython.config.configurable import LoggingConfigurable
29 29 from IPython.config import Config
30 30 from IPython.nbformat import current as nbformat
31 31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
32 32 from IPython.utils.importstring import import_item
33 33 from IPython.utils import text, py3compat
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Class
37 37 #-----------------------------------------------------------------------------
38 38
39 39 class ResourcesDict(collections.defaultdict):
40 40 def __missing__(self, key):
41 41 return ''
42 42
43 43
44 44 class Exporter(LoggingConfigurable):
45 45 """
46 46 Class containing methods that sequentially run a list of preprocessors on a
47 47 NotebookNode object and then return the modified NotebookNode object and
48 48 accompanying resources dict.
49 49 """
50 50
51 51 file_extension = Unicode(
52 52 'txt', config=True,
53 53 help="Extension of the file that should be written to disk"
54 54 )
55 55
56 56 # MIME type of the result file, for HTTP response headers.
57 57 # This is *not* a traitlet, because we want to be able to access it from
58 58 # the class, not just on instances.
59 59 output_mimetype = ''
60 60
61 61 #Configurability, allows the user to easily add filters and preprocessors.
62 62 preprocessors = List(config=True,
63 63 help="""List of preprocessors, by name or namespace, to enable.""")
64 64
65 65 _preprocessors = List()
66 66
67 67 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
68 68 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
69 69 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
70 70 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
71 71 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 72 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 73 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 74 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
75 75 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
76 76 config=True,
77 77 help="""List of preprocessors available by default, by name, namespace,
78 78 instance, or type.""")
79 79
80 80
81 81 def __init__(self, config=None, **kw):
82 82 """
83 83 Public constructor
84 84
85 85 Parameters
86 86 ----------
87 87 config : config
88 88 User configuration instance.
89 89 """
90 90 with_default_config = self.default_config
91 91 if config:
92 92 with_default_config.merge(config)
93 93
94 94 super(Exporter, self).__init__(config=with_default_config, **kw)
95 95
96 96 self._init_preprocessors()
97 97
98 98
99 99 @property
100 100 def default_config(self):
101 101 return Config()
102 102
103 @nbformat.docstring_nbformat_mod
104 103 def from_notebook_node(self, nb, resources=None, **kw):
105 104 """
106 105 Convert a notebook from a notebook node instance.
107 106
108 107 Parameters
109 108 ----------
110 nb : :class:`~{nbformat_mod}.nbbase.NotebookNode`
109 nb : :class:`~IPython.nbformat.current.NotebookNode`
111 110 Notebook node
112 111 resources : dict
113 112 Additional resources that can be accessed read/write by
114 113 preprocessors and filters.
115 114 **kw
116 115 Ignored (?)
117 116 """
118 117 nb_copy = copy.deepcopy(nb)
119 118 resources = self._init_resources(resources)
120 119
121 120 if 'language' in nb['metadata']:
122 121 resources['language'] = nb['metadata']['language'].lower()
123 122
124 123 # Preprocess
125 124 nb_copy, resources = self._preprocess(nb_copy, resources)
126 125
127 126 return nb_copy, resources
128 127
129 128
130 129 def from_filename(self, filename, resources=None, **kw):
131 130 """
132 131 Convert a notebook from a notebook file.
133 132
134 133 Parameters
135 134 ----------
136 135 filename : str
137 136 Full filename of the notebook file to open and convert.
138 137 """
139 138
140 139 # Pull the metadata from the filesystem.
141 140 if resources is None:
142 141 resources = ResourcesDict()
143 142 if not 'metadata' in resources or resources['metadata'] == '':
144 143 resources['metadata'] = ResourcesDict()
145 144 basename = os.path.basename(filename)
146 145 notebook_name = basename[:basename.rfind('.')]
147 146 resources['metadata']['name'] = notebook_name
148 147
149 148 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
150 149 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
151 150
152 151 with io.open(filename, encoding='utf-8') as f:
153 152 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
154 153
155 154
156 155 def from_file(self, file_stream, resources=None, **kw):
157 156 """
158 157 Convert a notebook from a notebook file.
159 158
160 159 Parameters
161 160 ----------
162 161 file_stream : file-like object
163 162 Notebook file-like object to convert.
164 163 """
165 164 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
166 165
167 166
168 167 def register_preprocessor(self, preprocessor, enabled=False):
169 168 """
170 169 Register a preprocessor.
171 170 Preprocessors are classes that act upon the notebook before it is
172 171 passed into the Jinja templating engine. preprocessors are also
173 172 capable of passing additional information to the Jinja
174 173 templating engine.
175 174
176 175 Parameters
177 176 ----------
178 177 preprocessor : preprocessor
179 178 """
180 179 if preprocessor is None:
181 180 raise TypeError('preprocessor')
182 181 isclass = isinstance(preprocessor, type)
183 182 constructed = not isclass
184 183
185 184 # Handle preprocessor's registration based on it's type
186 185 if constructed and isinstance(preprocessor, py3compat.string_types):
187 186 # Preprocessor is a string, import the namespace and recursively call
188 187 # this register_preprocessor method
189 188 preprocessor_cls = import_item(preprocessor)
190 189 return self.register_preprocessor(preprocessor_cls, enabled)
191 190
192 191 if constructed and hasattr(preprocessor, '__call__'):
193 192 # Preprocessor is a function, no need to construct it.
194 193 # Register and return the preprocessor.
195 194 if enabled:
196 195 preprocessor.enabled = True
197 196 self._preprocessors.append(preprocessor)
198 197 return preprocessor
199 198
200 199 elif isclass and isinstance(preprocessor, MetaHasTraits):
201 200 # Preprocessor is configurable. Make sure to pass in new default for
202 201 # the enabled flag if one was specified.
203 202 self.register_preprocessor(preprocessor(parent=self), enabled)
204 203
205 204 elif isclass:
206 205 # Preprocessor is not configurable, construct it
207 206 self.register_preprocessor(preprocessor(), enabled)
208 207
209 208 else:
210 209 # Preprocessor is an instance of something without a __call__
211 210 # attribute.
212 211 raise TypeError('preprocessor')
213 212
214 213
215 214 def _init_preprocessors(self):
216 215 """
217 216 Register all of the preprocessors needed for this exporter, disabled
218 217 unless specified explicitly.
219 218 """
220 219 self._preprocessors = []
221 220
222 221 # Load default preprocessors (not necessarly enabled by default).
223 222 for preprocessor in self.default_preprocessors:
224 223 self.register_preprocessor(preprocessor)
225 224
226 225 # Load user-specified preprocessors. Enable by default.
227 226 for preprocessor in self.preprocessors:
228 227 self.register_preprocessor(preprocessor, enabled=True)
229 228
230 229
231 230 def _init_resources(self, resources):
232 231
233 232 #Make sure the resources dict is of ResourcesDict type.
234 233 if resources is None:
235 234 resources = ResourcesDict()
236 235 if not isinstance(resources, ResourcesDict):
237 236 new_resources = ResourcesDict()
238 237 new_resources.update(resources)
239 238 resources = new_resources
240 239
241 240 #Make sure the metadata extension exists in resources
242 241 if 'metadata' in resources:
243 242 if not isinstance(resources['metadata'], ResourcesDict):
244 243 resources['metadata'] = ResourcesDict(resources['metadata'])
245 244 else:
246 245 resources['metadata'] = ResourcesDict()
247 246 if not resources['metadata']['name']:
248 247 resources['metadata']['name'] = 'Notebook'
249 248
250 249 #Set the output extension
251 250 resources['output_extension'] = self.file_extension
252 251 return resources
253 252
254 253
255 254 def _preprocess(self, nb, resources):
256 255 """
257 256 Preprocess the notebook before passing it into the Jinja engine.
258 257 To preprocess the notebook is to apply all of the
259 258
260 259 Parameters
261 260 ----------
262 261 nb : notebook node
263 262 notebook that is being exported.
264 263 resources : a dict of additional resources that
265 264 can be accessed read/write by preprocessors
266 265 """
267 266
268 267 # Do a copy.deepcopy first,
269 268 # we are never safe enough with what the preprocessors could do.
270 269 nbc = copy.deepcopy(nb)
271 270 resc = copy.deepcopy(resources)
272 271
273 272 #Run each preprocessor on the notebook. Carry the output along
274 273 #to each preprocessor
275 274 for preprocessor in self._preprocessors:
276 275 nbc, resc = preprocessor(nbc, resc)
277 276 return nbc, resc
@@ -1,322 +1,320 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 from IPython.nbformat.current import docstring_nbformat_mod
31 30 from IPython.nbconvert import filters
32 31 from .exporter import Exporter
33 32
34 33 #-----------------------------------------------------------------------------
35 34 # Globals and constants
36 35 #-----------------------------------------------------------------------------
37 36
38 37 #Jinja2 extensions to load.
39 38 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
40 39
41 40 default_filters = {
42 41 'indent': text.indent,
43 42 'markdown2html': filters.markdown2html,
44 43 'ansi2html': filters.ansi2html,
45 44 'filter_data_type': filters.DataTypeFilter,
46 45 'get_lines': filters.get_lines,
47 46 'highlight2html': filters.Highlight2HTML,
48 47 'highlight2latex': filters.Highlight2Latex,
49 48 'ipython2python': filters.ipython2python,
50 49 'posix_path': filters.posix_path,
51 50 'markdown2latex': filters.markdown2latex,
52 51 'markdown2rst': filters.markdown2rst,
53 52 'comment_lines': filters.comment_lines,
54 53 'strip_ansi': filters.strip_ansi,
55 54 'strip_dollars': filters.strip_dollars,
56 55 'strip_files_prefix': filters.strip_files_prefix,
57 56 'html2text' : filters.html2text,
58 57 'add_anchor': filters.add_anchor,
59 58 'ansi2latex': filters.ansi2latex,
60 59 'wrap_text': filters.wrap_text,
61 60 'escape_latex': filters.escape_latex,
62 61 'citation2latex': filters.citation2latex,
63 62 'path2url': filters.path2url,
64 63 'add_prompts': filters.add_prompts,
65 64 'ascii_only': filters.ascii_only,
66 65 }
67 66
68 67 #-----------------------------------------------------------------------------
69 68 # Class
70 69 #-----------------------------------------------------------------------------
71 70
72 71 class TemplateExporter(Exporter):
73 72 """
74 73 Exports notebooks into other file formats. Uses Jinja 2 templating engine
75 74 to output new formats. Inherit from this class if you are creating a new
76 75 template type along with new filters/preprocessors. If the filters/
77 76 preprocessors provided by default suffice, there is no need to inherit from
78 77 this class. Instead, override the template_file and file_extension
79 78 traits via a config file.
80 79
81 80 {filters}
82 81 """
83 82
84 83 # finish the docstring
85 84 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
86 85
87 86
88 87 template_file = Unicode(u'default',
89 88 config=True,
90 89 help="Name of the template file to use")
91 90 def _template_file_changed(self, name, old, new):
92 91 if new == 'default':
93 92 self.template_file = self.default_template
94 93 else:
95 94 self.template_file = new
96 95 self.template = None
97 96 self._load_template()
98 97
99 98 default_template = Unicode(u'')
100 99 template = Any()
101 100 environment = Any()
102 101
103 102 template_path = List(['.'], config=True)
104 103 def _template_path_changed(self, name, old, new):
105 104 self._load_template()
106 105
107 106 default_template_path = Unicode(
108 107 os.path.join("..", "templates"),
109 108 help="Path where the template files are located.")
110 109
111 110 template_skeleton_path = Unicode(
112 111 os.path.join("..", "templates", "skeleton"),
113 112 help="Path where the template skeleton files are located.")
114 113
115 114 #Jinja block definitions
116 115 jinja_comment_block_start = Unicode("", config=True)
117 116 jinja_comment_block_end = Unicode("", config=True)
118 117 jinja_variable_block_start = Unicode("", config=True)
119 118 jinja_variable_block_end = Unicode("", config=True)
120 119 jinja_logic_block_start = Unicode("", config=True)
121 120 jinja_logic_block_end = Unicode("", config=True)
122 121
123 122 #Extension that the template files use.
124 123 template_extension = Unicode(".tpl", config=True)
125 124
126 125 filters = Dict(config=True,
127 126 help="""Dictionary of filters, by name and namespace, to add to the Jinja
128 127 environment.""")
129 128
130 129 raw_mimetypes = List(config=True,
131 130 help="""formats of raw cells to be included in this Exporter's output."""
132 131 )
133 132 def _raw_mimetypes_default(self):
134 133 return [self.output_mimetype, '']
135 134
136 135
137 136 def __init__(self, config=None, extra_loaders=None, **kw):
138 137 """
139 138 Public constructor
140 139
141 140 Parameters
142 141 ----------
143 142 config : config
144 143 User configuration instance.
145 144 extra_loaders : list[of Jinja Loaders]
146 145 ordered list of Jinja loader to find templates. Will be tried in order
147 146 before the default FileSystem ones.
148 147 template : str (optional, kw arg)
149 148 Template to use when exporting.
150 149 """
151 150 super(TemplateExporter, self).__init__(config=config, **kw)
152 151
153 152 #Init
154 153 self._init_template()
155 154 self._init_environment(extra_loaders=extra_loaders)
156 155 self._init_filters()
157 156
158 157
159 158 def _load_template(self):
160 159 """Load the Jinja template object from the template file
161 160
162 161 This is a no-op if the template attribute is already defined,
163 162 or the Jinja environment is not setup yet.
164 163
165 164 This is triggered by various trait changes that would change the template.
166 165 """
167 166 from jinja2 import TemplateNotFound
168 167
169 168 if self.template is not None:
170 169 return
171 170 # called too early, do nothing
172 171 if self.environment is None:
173 172 return
174 173 # Try different template names during conversion. First try to load the
175 174 # template by name with extension added, then try loading the template
176 175 # as if the name is explicitly specified, then try the name as a
177 176 # 'flavor', and lastly just try to load the template by module name.
178 177 try_names = []
179 178 if self.template_file:
180 179 try_names.extend([
181 180 self.template_file + self.template_extension,
182 181 self.template_file,
183 182 ])
184 183 for try_name in try_names:
185 184 self.log.debug("Attempting to load template %s", try_name)
186 185 try:
187 186 self.template = self.environment.get_template(try_name)
188 187 except (TemplateNotFound, IOError):
189 188 pass
190 189 except Exception as e:
191 190 self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True)
192 191 else:
193 192 self.log.info("Loaded template %s", try_name)
194 193 break
195 194
196 @docstring_nbformat_mod
197 195 def from_notebook_node(self, nb, resources=None, **kw):
198 196 """
199 197 Convert a notebook from a notebook node instance.
200 198
201 199 Parameters
202 200 ----------
203 nb : :class:`~{nbformat_mod}.nbbase.NotebookNode`
201 nb : :class:`~IPython.nbformat.current.NotebookNode`
204 202 Notebook node
205 203 resources : dict
206 204 Additional resources that can be accessed read/write by
207 205 preprocessors and filters.
208 206 """
209 207 nb_copy, resources = super(TemplateExporter, self).from_notebook_node(nb, resources, **kw)
210 208 resources.setdefault('raw_mimetypes', self.raw_mimetypes)
211 209
212 210 self._load_template()
213 211
214 212 if self.template is not None:
215 213 output = self.template.render(nb=nb_copy, resources=resources)
216 214 else:
217 215 raise IOError('template file "%s" could not be found' % self.template_file)
218 216 return output, resources
219 217
220 218
221 219 def register_filter(self, name, jinja_filter):
222 220 """
223 221 Register a filter.
224 222 A filter is a function that accepts and acts on one string.
225 223 The filters are accesible within the Jinja templating engine.
226 224
227 225 Parameters
228 226 ----------
229 227 name : str
230 228 name to give the filter in the Jinja engine
231 229 filter : filter
232 230 """
233 231 if jinja_filter is None:
234 232 raise TypeError('filter')
235 233 isclass = isinstance(jinja_filter, type)
236 234 constructed = not isclass
237 235
238 236 #Handle filter's registration based on it's type
239 237 if constructed and isinstance(jinja_filter, py3compat.string_types):
240 238 #filter is a string, import the namespace and recursively call
241 239 #this register_filter method
242 240 filter_cls = import_item(jinja_filter)
243 241 return self.register_filter(name, filter_cls)
244 242
245 243 if constructed and hasattr(jinja_filter, '__call__'):
246 244 #filter is a function, no need to construct it.
247 245 self.environment.filters[name] = jinja_filter
248 246 return jinja_filter
249 247
250 248 elif isclass and isinstance(jinja_filter, MetaHasTraits):
251 249 #filter is configurable. Make sure to pass in new default for
252 250 #the enabled flag if one was specified.
253 251 filter_instance = jinja_filter(parent=self)
254 252 self.register_filter(name, filter_instance )
255 253
256 254 elif isclass:
257 255 #filter is not configurable, construct it
258 256 filter_instance = jinja_filter()
259 257 self.register_filter(name, filter_instance)
260 258
261 259 else:
262 260 #filter is an instance of something without a __call__
263 261 #attribute.
264 262 raise TypeError('filter')
265 263
266 264
267 265 def _init_template(self):
268 266 """
269 267 Make sure a template name is specified. If one isn't specified, try to
270 268 build one from the information we know.
271 269 """
272 270 self._template_file_changed('template_file', self.template_file, self.template_file)
273 271
274 272
275 273 def _init_environment(self, extra_loaders=None):
276 274 """
277 275 Create the Jinja templating environment.
278 276 """
279 277 from jinja2 import Environment, ChoiceLoader, FileSystemLoader
280 278 here = os.path.dirname(os.path.realpath(__file__))
281 279 loaders = []
282 280 if extra_loaders:
283 281 loaders.extend(extra_loaders)
284 282
285 283 paths = self.template_path
286 284 paths.extend([os.path.join(here, self.default_template_path),
287 285 os.path.join(here, self.template_skeleton_path)])
288 286 loaders.append(FileSystemLoader(paths))
289 287
290 288 self.environment = Environment(
291 289 loader= ChoiceLoader(loaders),
292 290 extensions=JINJA_EXTENSIONS
293 291 )
294 292
295 293 #Set special Jinja2 syntax that will not conflict with latex.
296 294 if self.jinja_logic_block_start:
297 295 self.environment.block_start_string = self.jinja_logic_block_start
298 296 if self.jinja_logic_block_end:
299 297 self.environment.block_end_string = self.jinja_logic_block_end
300 298 if self.jinja_variable_block_start:
301 299 self.environment.variable_start_string = self.jinja_variable_block_start
302 300 if self.jinja_variable_block_end:
303 301 self.environment.variable_end_string = self.jinja_variable_block_end
304 302 if self.jinja_comment_block_start:
305 303 self.environment.comment_start_string = self.jinja_comment_block_start
306 304 if self.jinja_comment_block_end:
307 305 self.environment.comment_end_string = self.jinja_comment_block_end
308 306
309 307
310 308 def _init_filters(self):
311 309 """
312 310 Register all of the filters required for the exporter.
313 311 """
314 312
315 313 #Add default filters to the Jinja2 environment
316 314 for key, value in default_filters.items():
317 315 self.register_filter(key, value)
318 316
319 317 #Load user filters. Overwrite existing filters if need be.
320 318 if self.filters:
321 319 for key, user_filter in self.filters.items():
322 320 self.register_filter(key, user_filter)
@@ -1,223 +1,212 b''
1 1 """The official API for working with notebooks in the current format version."""
2 2
3 3 from __future__ import print_function
4 4
5 from xml.etree import ElementTree as ET
6 5 import re
7 6
8 7 from IPython.utils.py3compat import unicode_type
9 8
10 9 from IPython.nbformat.v3 import (
11 10 NotebookNode,
12 11 new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet,
13 12 parse_filename, new_metadata, new_author, new_heading_cell, nbformat,
14 13 nbformat_minor, nbformat_schema, to_notebook_json
15 14 )
16 15 from IPython.nbformat import v3 as _v_latest
17 16
18 17 from .reader import reads as reader_reads
19 18 from .reader import versions
20 19 from .convert import convert
21 20 from .validator import validate
22 21
23 22 from IPython.utils.log import get_logger
24 23
25 24 __all__ = ['NotebookNode', 'new_code_cell', 'new_text_cell', 'new_notebook',
26 25 'new_output', 'new_worksheet', 'parse_filename', 'new_metadata', 'new_author',
27 26 'new_heading_cell', 'nbformat', 'nbformat_minor', 'nbformat_schema',
28 27 'to_notebook_json', 'convert', 'validate', 'NBFormatError', 'parse_py',
29 28 'reads_json', 'writes_json', 'reads_py', 'writes_py', 'reads', 'writes', 'read',
30 29 'write']
31 30
32 31 current_nbformat = nbformat
33 32 current_nbformat_minor = nbformat_minor
34 33 current_nbformat_module = _v_latest.__name__
35 34
36 35
37 def docstring_nbformat_mod(func):
38 """Decorator for docstrings referring to classes/functions accessed through
39 nbformat.current.
40
41 Put {nbformat_mod} in the docstring in place of 'IPython.nbformat.v3'.
42 """
43 func.__doc__ = func.__doc__.format(nbformat_mod=current_nbformat_module)
44 return func
45
46
47 36 class NBFormatError(ValueError):
48 37 pass
49 38
50 39
51 40 def parse_py(s, **kwargs):
52 41 """Parse a string into a (nbformat, string) tuple."""
53 42 nbf = current_nbformat
54 43 nbm = current_nbformat_minor
55 44
56 45 pattern = r'# <nbformat>(?P<nbformat>\d+[\.\d+]*)</nbformat>'
57 46 m = re.search(pattern,s)
58 47 if m is not None:
59 48 digits = m.group('nbformat').split('.')
60 49 nbf = int(digits[0])
61 50 if len(digits) > 1:
62 51 nbm = int(digits[1])
63 52
64 53 return nbf, nbm, s
65 54
66 55
67 56 def reads_json(nbjson, **kwargs):
68 57 """Read a JSON notebook from a string and return the NotebookNode
69 58 object. Report if any JSON format errors are detected.
70 59
71 60 """
72 61 nb = reader_reads(nbjson, **kwargs)
73 62 nb_current = convert(nb, current_nbformat)
74 63 errors = validate(nb_current)
75 64 if errors:
76 65 get_logger().error(
77 66 "Notebook JSON is invalid (%d errors detected during read)",
78 67 len(errors))
79 68 return nb_current
80 69
81 70
82 71 def writes_json(nb, **kwargs):
83 72 """Take a NotebookNode object and write out a JSON string. Report if
84 73 any JSON format errors are detected.
85 74
86 75 """
87 76 errors = validate(nb)
88 77 if errors:
89 78 get_logger().error(
90 79 "Notebook JSON is invalid (%d errors detected during write)",
91 80 len(errors))
92 81 nbjson = versions[current_nbformat].writes_json(nb, **kwargs)
93 82 return nbjson
94 83
95 84
96 85 def reads_py(s, **kwargs):
97 86 """Read a .py notebook from a string and return the NotebookNode object."""
98 87 nbf, nbm, s = parse_py(s, **kwargs)
99 88 if nbf in (2, 3):
100 89 nb = versions[nbf].to_notebook_py(s, **kwargs)
101 90 else:
102 91 raise NBFormatError('Unsupported PY nbformat version: %i' % nbf)
103 92 return nb
104 93
105 94
106 95 def writes_py(nb, **kwargs):
107 96 # nbformat 3 is the latest format that supports py
108 97 return versions[3].writes_py(nb, **kwargs)
109 98
110 99
111 100 # High level API
112 101
113 102
114 103 def reads(s, format, **kwargs):
115 104 """Read a notebook from a string and return the NotebookNode object.
116 105
117 106 This function properly handles notebooks of any version. The notebook
118 107 returned will always be in the current version's format.
119 108
120 109 Parameters
121 110 ----------
122 111 s : unicode
123 112 The raw unicode string to read the notebook from.
124 113 format : (u'json', u'ipynb', u'py')
125 114 The format that the string is in.
126 115
127 116 Returns
128 117 -------
129 118 nb : NotebookNode
130 119 The notebook that was read.
131 120 """
132 121 format = unicode_type(format)
133 122 if format == u'json' or format == u'ipynb':
134 123 return reads_json(s, **kwargs)
135 124 elif format == u'py':
136 125 return reads_py(s, **kwargs)
137 126 else:
138 127 raise NBFormatError('Unsupported format: %s' % format)
139 128
140 129
141 130 def writes(nb, format, **kwargs):
142 131 """Write a notebook to a string in a given format in the current nbformat version.
143 132
144 133 This function always writes the notebook in the current nbformat version.
145 134
146 135 Parameters
147 136 ----------
148 137 nb : NotebookNode
149 138 The notebook to write.
150 139 format : (u'json', u'ipynb', u'py')
151 140 The format to write the notebook in.
152 141
153 142 Returns
154 143 -------
155 144 s : unicode
156 145 The notebook string.
157 146 """
158 147 format = unicode_type(format)
159 148 if format == u'json' or format == u'ipynb':
160 149 return writes_json(nb, **kwargs)
161 150 elif format == u'py':
162 151 return writes_py(nb, **kwargs)
163 152 else:
164 153 raise NBFormatError('Unsupported format: %s' % format)
165 154
166 155
167 156 def read(fp, format, **kwargs):
168 157 """Read a notebook from a file and return the NotebookNode object.
169 158
170 159 This function properly handles notebooks of any version. The notebook
171 160 returned will always be in the current version's format.
172 161
173 162 Parameters
174 163 ----------
175 164 fp : file
176 165 Any file-like object with a read method.
177 166 format : (u'json', u'ipynb', u'py')
178 167 The format that the string is in.
179 168
180 169 Returns
181 170 -------
182 171 nb : NotebookNode
183 172 The notebook that was read.
184 173 """
185 174 return reads(fp.read(), format, **kwargs)
186 175
187 176
188 177 def write(nb, fp, format, **kwargs):
189 178 """Write a notebook to a file in a given format in the current nbformat version.
190 179
191 180 This function always writes the notebook in the current nbformat version.
192 181
193 182 Parameters
194 183 ----------
195 184 nb : NotebookNode
196 185 The notebook to write.
197 186 fp : file
198 187 Any file-like object with a write method.
199 188 format : (u'json', u'ipynb', u'py')
200 189 The format to write the notebook in.
201 190
202 191 Returns
203 192 -------
204 193 s : unicode
205 194 The notebook string.
206 195 """
207 196 return fp.write(writes(nb, format, **kwargs))
208 197
209 198 def _convert_to_metadata():
210 199 """Convert to a notebook having notebook metadata."""
211 200 import glob
212 201 for fname in glob.glob('*.ipynb'):
213 202 print('Converting file:',fname)
214 203 with open(fname,'r') as f:
215 204 nb = read(f,u'json')
216 205 md = new_metadata()
217 206 if u'name' in nb:
218 207 md.name = nb.name
219 208 del nb[u'name']
220 209 nb.metadata = md
221 210 with open(fname,'w') as f:
222 211 write(nb, f, u'json')
223 212
General Comments 0
You need to be logged in to leave comments. Login now