##// END OF EJS Templates
Post code-review, extended refactor.
Jonathan Frederic -
Show More
@@ -0,0 +1,23 b''
1 """Latex transformer.
2
3 Module that allows latex output notebooks to be conditioned before
4 they are converted.
5 """
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2013, the IPython Development Team.
8 #
9 # Distributed under the terms of the Modified BSD License.
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Functions
16 #-----------------------------------------------------------------------------
17 def export_sphinx_report(nb, config=None):
18 pass
19 def export_sphinx_report(nb, fileName, config=None):
20 pass
21
22 #TODO: Add basic export/import utility functions.
23 __author__ = 'root'
@@ -0,0 +1,82 b''
1 # ANSI color functions:
2 import re
3 def remove_ansi(src):
4 """Strip all ANSI color escape sequences from input string.
5
6 Parameters
7 ----------
8 src : string
9
10 Returns
11 -------
12 string
13 """
14 return re.sub(r'\033\[(0|\d;\d\d)m', '', src)
15
16
17 def ansi2html(txt):
18 """Render ANSI colors as HTML colors
19
20 This is equivalent to util.fixConsole in utils.js
21
22 Parameters
23 ----------
24 txt : string
25
26 Returns
27 -------
28 string
29 """
30
31 ansi_colormap = {
32 '30': 'ansiblack',
33 '31': 'ansired',
34 '32': 'ansigreen',
35 '33': 'ansiyellow',
36 '34': 'ansiblue',
37 '35': 'ansipurple',
38 '36': 'ansicyan',
39 '37': 'ansigrey',
40 '01': 'ansibold',
41 }
42
43 # do ampersand first
44 txt = txt.replace('&', '&')
45 html_escapes = {
46 '<': '&lt;',
47 '>': '&gt;',
48 "'": '&apos;',
49 '"': '&quot;',
50 '`': '&#96;',
51 }
52 for c, escape in html_escapes.iteritems():
53 txt = txt.replace(c, escape)
54
55 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
56 m = ansi_re.search(txt)
57 opened = False
58 cmds = []
59 opener = ''
60 closer = ''
61 while m:
62 cmds = m.groups()[0].split(';')
63 closer = '</span>' if opened else ''
64 # True if there is there more than one element in cmds, *or*
65 # if there is only one but it is not equal to a string of zeroes.
66 opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0])
67 classes = []
68 for cmd in cmds:
69 if cmd in ansi_colormap:
70 classes.append(ansi_colormap.get(cmd))
71
72 if classes:
73 opener = '<span class="%s">' % (' '.join(classes))
74 else:
75 opener = ''
76 txt = re.sub(ansi_re, closer + opener, txt, 1)
77
78 m = ansi_re.search(txt)
79
80 if opened:
81 txt += '</span>'
82 return txt
@@ -0,0 +1,39 b''
1 # Our own imports
2 from utils.lexers import IPythonLexer
3
4 #-----------------------------------------------------------------------------
5 # Globals and constants
6 #-----------------------------------------------------------------------------
7 _multiline_outputs = ['text', 'html', 'svg', 'latex', 'javascript', 'json']
8
9
10 #-----------------------------------------------------------------------------
11 # Utility functions
12 #-----------------------------------------------------------------------------
13 def highlight(src, lang='ipython'):
14 """
15 Return a syntax-highlighted version of the input source as html output.
16 """
17 from pygments.formatters import HtmlFormatter
18 return pygment_highlight(src, HtmlFormatter(), lang)
19
20 def highlight2latex(src, lang='ipython'):
21 """
22 Return a syntax-highlighted version of the input source as latex output.
23 """
24 from pygments.formatters import LatexFormatter
25 return pygment_highlight(src, LatexFormatter(), lang)
26
27 def pygment_highlight(src, output_formatter, lang='ipython'):
28 """
29 Return a syntax-highlighted version of the input source
30 """
31 from pygments import highlight
32 from pygments.lexers import get_lexer_by_name
33
34 if lang == 'ipython':
35 lexer = IPythonLexer()
36 else:
37 lexer = get_lexer_by_name(lang, stripall=True)
38
39 return highlight(src, lexer, output_formatter)
@@ -0,0 +1,43 b''
1
2 def cell_preprocessor(function):
3 """ wrap a function to be executed on all cells of a notebook
4
5 wrapped function parameters :
6 cell : the cell
7 other : external resources
8 index : index of the cell
9 """
10 def wrappedfunc(nb, other):
11 for worksheet in nb.worksheets :
12 for index, cell in enumerate(worksheet.cells):
13 worksheet.cells[index], other = function(cell, other, index)
14 return nb, other
15 return wrappedfunc
16
17
18 @cell_preprocessor
19 def coalesce_streams(cell, other, count):
20 """merge consecutive sequences of stream output into single stream
21
22 to prevent extra newlines inserted at flush calls
23
24 TODO: handle \r deletion
25 """
26 outputs = cell.get('outputs', [])
27 if not outputs:
28 return cell, other
29 new_outputs = []
30 last = outputs[0]
31 new_outputs = [last]
32 for output in outputs[1:]:
33 if (output.output_type == 'stream' and
34 last.output_type == 'stream' and
35 last.stream == output.stream
36 ):
37 last.text += output.text
38 else:
39 new_outputs.append(output)
40
41 cell.outputs = new_outputs
42 return cell, other
43
1 NO CONTENT: modified file chmod 100644 => 100755
@@ -1,333 +1,266 b''
1 1 """Exporter for the notebook conversion pipeline.
2 2
3 3 This module defines Exporter, a highly configurable converter
4 4 that uses Jinja2 to export notebook files into different format.
5 5
6 6 You can register both pre-transformers that will act on the notebook format
7 7 befor conversion and jinja filter that would then be availlable in the templates
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (c) 2013, the IPython Development Team.
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21 from __future__ import print_function, absolute_import
22 22
23 23 # Stdlib imports
24 24 import io
25 25 import os
26 import re
27 26
28 27 # IPython imports
29 28 from IPython.config.configurable import Configurable
30 29 from IPython.nbformat import current as nbformat
31 30 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Bool
32 31 from IPython.utils.text import indent
33 32
34 33 # other libs/dependencies
35 34 from jinja2 import Environment, FileSystemLoader
36 35 from markdown import markdown
37 36
38 # local import (pre-transformers)
39 from exceptions import ConversionException
40 from . import transformers as trans #TODO
41 from .utils import get_lines #TODO
42 from .utils import remove_ansi #TODO
43 from .utils import highlight, ansi2html #TODO
37 # local import
38 import filters.strings
39 import filters.markdown
40 import filters.latex
41 import filters.datatypefilter
42 import filters.pygments
43 import filters.ansi
44
45 import transformers.extractfigure
46 import transformers.csshtmlheader
47 import transformers.revealhelp
48 import transformers.coalescestreams
44 49
45 import .utils.strings as strings
46 import .utils.markdown as markdown_utils
47 import .utils.datatypefilter.DataTypeFilter as DataTypeFilter
48 50
49 51 #-----------------------------------------------------------------------------
50 52 # Globals and constants
51 53 #-----------------------------------------------------------------------------
52 54
53 55 #Standard Jinja2 environment constants
54 56 TEMPLATE_PATH = "/../templates/"
55 57 TEMPLATE_SKELETON_PATH = "/../templates/skeleton/"
56 58 TEMPLATE_EXTENSION = ".tpl"
57 59
58 60 #Jinja2 extensions to load.
59 61 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
60 62
61 63 #-----------------------------------------------------------------------------
62 64 # Classes and functions
63 65 #-----------------------------------------------------------------------------
64 66 class Exporter(Configurable):
65 67 """ A Jinja2 base converter templates
66 68
67 Preprocess the ipynb files, feed it throug jinja templates,
69 Pre-process the IPYNB files, feed it through Jinja2 templates,
68 70 and spit an converted files and a data object with other data
69 71 should be mostly configurable
70 72 """
71 73
72 74 pre_transformer_order = List(['haspyout_transformer'],
73 75 config=True,
74 76 help= """
75 An ordered list of pre transformer to apply to the ipynb
77 An ordered list of pre-transformer to apply to the IPYNB
76 78 file before running through templates
77 79 """
78 80 )
79 81
80 #TODO: Flagged for removal.
81 tex_environement = Bool(
82 False,
83 config=True,
84 help=" Whether or not the user is exporting to latex.")
85
86 82 template_file = Unicode(
87 83 '', config=True,
88 84 help="Name of the template file to use")
89 85
90 86 fileext = Unicode(
91 87 'txt', config=True,
92 88 help="Extension of the file that should be written to disk"
93 89 )
94 90
95 91 stdout = Bool(
96 92 True, config=True,
97 help="""Whether to print the converted ipynb file to stdout
93 help="""Whether to print the converted IPYNB file to stdout
98 94 "use full do diff files without actually writing a new file"""
99 95 )
100 96
101 97 write = Bool(
102 98 False, config=True,
103 99 help="""Should the converted notebook file be written to disk
104 100 along with potential extracted resources."""
105 101 )
106 102
107 103 #Processors that process the input data prior to the export, set in the
108 104 #constructor for this class.
109 105 preprocessors = []
110 106
111 107 def __init__(self, preprocessors={}, jinja_filters={}, config=None, export_format, **kw):
112 108 """ Init a new converter.
113 109
114 110 config: the Configurable config object to pass around.
115 111
116 112 preprocessors: dict of **availlable** key/value function to run on
117 113 ipynb json data before conversion to extract/inline file.
118 114 See `transformer.py` and `ConfigurableTransformers`
119 115
120 116 set the order in which the transformers should apply
121 117 with the `pre_transformer_order` trait of this class
122 118
123 119 transformers registerd by this key will take precedence on
124 120 default one.
125 121
126 122 jinja_filters: dict of supplementary jinja filter that should be made
127 123 availlable in template. If those are of Configurable Class type,
128 124 they will be instanciated with the config object as argument.
129 125
130 126 user defined filter will overwrite the one availlable by default.
131 127 """
132 128
133 #Merge default config options with user specific override options.
134 default_config = self._get_default_options()
129 #Set the default options for the exporter.
130 default_config = self.config
131
132 #Set properties that must be set in the config class in order to
133 #propagate to other classes.
134 default_config.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text']
135 default_config.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text']
136
137 #Set default properties of the exporter.
138 #For most (or all cases), the template file name matches the format name.
139 self.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text']
140 self.template_file = export_format
141
135 142 if not config == None:
136 143 default_config._merge(config)
137 config = default_config
138
144 config = default_config
145
139 146 #Call the base class constructor
140 147 super(Exporter, self).__init__(config=config, **kw)
141 148
142 149 #Standard environment
143 150 self.ext = TEMPLATE_EXTENSION
144 self.env = Environment(
145 loader=FileSystemLoader([
146 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH,
147 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH,
148 ]),
149 extensions=JINJA_EXTENSIONS
150 )
151 self._init_environment()
151 152
152 for name in self.pre_transformer_order:
153 # get the user-defined transformer first
154 transformer = preprocessors.get(name, getattr(trans, name, None))
155 if isinstance(transformer, MetaHasTraits):
156 transformer = transformer(config=config)
157 self.preprocessors.append(transformer)
153 #TODO: Implement reflection style methods to get user transformers.
154 #for name in self.pre_transformer_order:
155 # # get the user-defined transformer first
156 # transformer = preprocessors.get(name, getattr(trans, name, None))
157 # if isinstance(transformer, MetaHasTraits):
158 # transformer = transformer(config=config)
159 # self.preprocessors.append(transformer)
158 160
159 161 #For compatibility, TODO: remove later.
160 self.preprocessors.append(trans.coalesce_streams)
161 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
162 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
163 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
164 self.preprocessors.append(LatexTransformer(config=config))
165
166 #Only load the sphinx transformer if the file reference worked
167 #(Sphinx dependencies exist on the user's machine.)
168 if SphinxTransformer:
169 self.preprocessors.append(SphinxTransformer(config=config))
162 self.preprocessors.append(transformers.coalescestreams.coalesce_streams)
163 self.preprocessors.append(transformers.extractfigure.ExtractFigureTransformer(config=config))
164 self.preprocessors.append(transformers.revealhelp.RevealHelpTransformer(config=config))
165 self.preprocessors.append(transformers.csshtmlheader.CSSHtmlHeaderTransformer(config=config))
170 166
171 167 #Add filters to the Jinja2 environment
172 self.env.filters['filter_data_type'] = DataTypeFilter(config=config)
173 self.env.filters['pycomment'] = _python_comment
174 self.env.filters['indent'] = indent
175 self.env.filters['rm_fake'] = _rm_fake
176 self.env.filters['rm_ansi'] = remove_ansi
177 self.env.filters['markdown'] = markdown
178 self.env.filters['ansi2html'] = ansi2html
179 self.env.filters['markdown2latex'] = markdown_utils.markdown2latex
180 self.env.filters['markdown2rst'] = markdown_utils.markdown2rst
181 self.env.filters['get_lines'] = get_lines
182 self.env.filters['wrap'] = strings.wrap
183 self.env.filters['rm_dollars'] = strings.strip_dollars
184 self.env.filters['rm_math_space'] = rm_math_space
185 self.env.filters['highlight2html'] = highlight
186 self.env.filters['highlight2latex'] = highlight2latex
187
188 #Latex specific filters
189 if self.tex_environement:
190 self.env.filters['escape_tex'] = _escape_tex
191 self.env.filters['highlight'] = highlight2latex
192 else:
193 self.env.filters['highlight'] = highlight
168 self._register_filters(config)
194 169
195 170 #Load user filters. Overwrite existing filters if need be.
196 171 for key, user_filter in jinja_filters.iteritems():
197 172 if isinstance(user_filter, MetaHasTraits):
198 173 self.env.filters[key] = user_filter(config=config)
199 174 else:
200 175 self.env.filters[key] = user_filter
201 176
202 177 #Load the template file.
203 178 self.template = self.env.get_template(self.template_file+self.ext)
204 179
205 180
206 181 def export(self, nb):
207 182 """Export notebook object
208 183
209 184 nb: Notebook object to export.
210 185
211 186 Returns both the converted ipynb file and a dict containing the
212 187 resources created along the way via the transformers and Jinja2
213 188 processing.
214 189 """
215 190
216 191 nb, resources = self._preprocess(nb)
217 192 return self.template.render(nb=nb, resources=resources), resources
218 193
219 194
220 195 def from_filename(self, filename):
221 196 """Read and export a notebook from a filename
222 197
223 198 filename: Filename of the notebook file to export.
224 199
225 200 Returns both the converted ipynb file and a dict containing the
226 201 resources created along the way via the transformers and Jinja2
227 202 processing.
228 203 """
229 204 with io.open(filename) as f:
230 205 return self.export(nbformat.read(f, 'json'))
231 206
232 207
233 208 def from_file(self, file_stream):
234 209 """Read and export a notebook from a filename
235 210
236 211 file_stream: File handle of file that contains notebook data.
237 212
238 213 Returns both the converted ipynb file and a dict containing the
239 214 resources created along the way via the transformers and Jinja2
240 215 processing.
241 216 """
242 217
243 218 return self.export(nbformat.read(file_stream, 'json'))
244 219
245 220
221 def _init_environment(self):
222 self.env = Environment(
223 loader=FileSystemLoader([
224 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH,
225 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH,
226 ]),
227 extensions=JINJA_EXTENSIONS
228 )
229
230
231 def _register_filters(self, config):
232 self.env.filters['indent'] = indent
233 self.env.filters['markdown'] = markdown
234
235 self.env.filters['ansi2html'] = filters.ansi.ansi2html
236 self.env.filters['filter_data_type'] = filters.datatypefilter.DataTypeFilter(config=config)
237 self.env.filters['get_lines'] = filters.strings.get_lines
238 self.env.filters['highlight'] = filters.pygments.highlight
239 self.env.filters['highlight2html'] = filters.pygments.highlight
240 self.env.filters['highlight2latex'] = filters.pygments.highlight2latex
241 self.env.filters['markdown2latex'] = filters.markdown.markdown2latex
242 self.env.filters['markdown2rst'] = filters.markdown.markdown2rst
243 self.env.filters['pycomment'] = filters.strings.python_comment
244 self.env.filters['rm_ansi'] = filters.ansi.remove_ansi
245 self.env.filters['rm_dollars'] = filters.strings.strip_dollars
246 self.env.filters['rm_fake'] = filters.strings.rm_fake
247 self.env.filters['rm_math_space'] = filters.latex.rm_math_space
248 self.env.filters['wrap'] = filters.strings.wrap
249
250
246 251 def _preprocess(self, nb):
247 252 """ Preprocess the notebook using the transformers specific
248 253 for the current export format.
249 254
250 255 nb: Notebook to preprocess
251 256 """
252 257
253 258 #Dict of 'resources' that can be filled by the preprocessors.
254 259 resources = {}
255 260
256 261 #Run each transformer on the notebook. Carry the output along
257 262 #to each transformer
258 263 for transformer in self.preprocessors:
259 264 nb, resources = transformer(nb, resources)
260 265 return nb, resources
261 266
262
263 def _get_default_options(self, export_format):
264 """ Load the default options for built in formats.
265
266 export_format: Format being exported to.
267 """
268
269 c = get_config()
270
271 #Set default data extraction priorities.
272 c.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text']
273 c.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text']
274 c.ConverterTemplate.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text']
275
276 #For most (or all cases), the template file name matches the format name.
277 c.ConverterTemplate.template_file = export_format
278
279 if export_format == "basichtml" or "fullhtml" or "reveal":
280 c.CSSHtmlHeaderTransformer.enabled=True
281 if export_format == 'reveal'
282 c.NbconvertApp.fileext='reveal.html'
283 else:
284 c.NbconvertApp.fileext='html'
285
286 elif export_format == "latex_sphinx_howto" or export_format == "latex_sphinx_manual":
287
288 #Turn on latex environment
289 c.ConverterTemplate.tex_environement=True
290
291 #Standard latex extension
292 c.NbconvertApp.fileext='tex'
293
294 #Prioritize latex extraction for latex exports.
295 c.GlobalConfigurable.display_data_priority =['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text']
296 c.ExtractFigureTransformer.display_data_priority=['latex', 'svg', 'png', 'jpg', 'jpeg']
297 c.ExtractFigureTransformer.extra_ext_map={'svg':'pdf'}
298 c.ExtractFigureTransformer.enabled=True
299
300 # Enable latex transformers (make markdown2latex work with math $.)
301 c.LatexTransformer.enabled=True
302 c.SphinxTransformer.enabled = True
303
304 elif export_format == 'markdown':
305 c.NbconvertApp.fileext='md'
306 c.ExtractFigureTransformer.enabled=True
307
308 elif export_format == 'python':
309 c.NbconvertApp.fileext='py'
310
311
312 elif export_format == 'rst':
313 c.NbconvertApp.fileext='rst'
314 c.ExtractFigureTransformer.enabled=True
315 return c
316
317
318 #TODO: Comment me.
319 def _rm_fake(strng):
320 return strng.replace('/files/', '')
321
322
323 #TODO: Comment me.
324 def _python_comment(string):
325 return '# '+'\n# '.join(string.split('\n'))
326
327
328 #TODO: Comment me.
329 def _escape_tex(value):
330 newval = value
331 for pattern, replacement in LATEX_SUBS:
332 newval = pattern.sub(replacement, newval)
333 return newval No newline at end of file
@@ -1,295 +1,150 b''
1
2 """Latex exporter for the notebook conversion pipeline.
1 """Latex exporter for the notebook conversion pipeline.
3 2
4 3 This module defines Exporter, a highly configurable converter
5 4 that uses Jinja2 to export notebook files into different format.
6 5
7 6 You can register both pre-transformers that will act on the notebook format
8 7 befor conversion and jinja filter that would then be availlable in the templates
9 8 """
10 9
11 10 #-----------------------------------------------------------------------------
12 11 # Copyright (c) 2013, the IPython Development Team.
13 12 #
14 13 # Distributed under the terms of the Modified BSD License.
15 14 #
16 15 # The full license is in the file COPYING.txt, distributed with this software.
17 16 #-----------------------------------------------------------------------------
18 17
19 18 #-----------------------------------------------------------------------------
20 19 # Imports
21 20 #-----------------------------------------------------------------------------
22 from .utils import highlight2latex #TODO
23
24 from .transformers.latex import LatexTransformer, rm_math_space #TODO: rm_math_space from filters
21 import base.Exporter as Exporter
25 22
26 23 #Try to import the Sphinx exporter. If the user doesn't have Sphinx isntalled
27 24 #on his/her machine, fail silently.
28 25 try:
29 26 from .sphinx_transformer import (SphinxTransformer) #TODO
30 27 except ImportError:
31 28 SphinxTransformer = None
32 29
33 30 #-----------------------------------------------------------------------------
34 31 # Globals and constants
35 32 #-----------------------------------------------------------------------------
36 33
37 34 #Latex Jinja2 constants
38 35 LATEX_TEMPLATE_PATH = "/../templates/tex/"
39 36 LATEX_TEMPLATE_SKELETON_PATH = "/../templates/tex/skeleton/"
40 37 LATEX_TEMPLATE_EXTENSION = ".tplx"
41 38
42 39 #Special Jinja2 syntax that will not conflict when exporting latex.
43 40 LATEX_JINJA_COMMENT_BLOCK = ["((=", "=))"]
44 41 LATEX_JINJA_VARIABLE_BLOCK = ["(((", ")))"]
45 42 LATEX_JINJA_LOGIC_BLOCK = ["((*", "*))"]
46 43
47 #Latex substitutions for escaping latex.
48 LATEX_SUBS = (
49 (re.compile(r'\\'), r'\\textbackslash'),
50 (re.compile(r'([{}_#%&$])'), r'\\\1'),
51 (re.compile(r'~'), r'\~{}'),
52 (re.compile(r'\^'), r'\^{}'),
53 (re.compile(r'"'), r"''"),
54 (re.compile(r'\.\.\.+'), r'\\ldots'),
55 )
56
57 44 #-----------------------------------------------------------------------------
58 45 # Classes and functions
59 46 #-----------------------------------------------------------------------------
60 class LatexExporter(Configurable):
47 class LatexExporter(Exporter):
61 48 """ A Jinja2 base converter templates
62 49
63 50 Preprocess the ipynb files, feed it throug jinja templates,
64 51 and spit an converted files and a data object with other data
65 52 should be mostly configurable
66 53 """
67 54
68 55 #Processors that process the input data prior to the export, set in the
69 56 #constructor for this class.
70 57 preprocessors = []
71 58
72 59 def __init__(self, preprocessors={}, jinja_filters={}, config=None, export_format, **kw):
73 60 """ Init a new converter.
74 61
75 62 config: the Configurable config object to pass around.
76 63
77 64 preprocessors: dict of **availlable** key/value function to run on
78 65 ipynb json data before conversion to extract/inline file.
79 66 See `transformer.py` and `ConfigurableTransformers`
80 67
81 68 set the order in which the transformers should apply
82 69 with the `pre_transformer_order` trait of this class
83 70
84 71 transformers registerd by this key will take precedence on
85 72 default one.
86 73
87 74 jinja_filters: dict of supplementary jinja filter that should be made
88 75 availlable in template. If those are of Configurable Class type,
89 76 they will be instanciated with the config object as argument.
90 77
91 78 user defined filter will overwrite the one availlable by default.
92 79 """
93 80
94 81 #Merge default config options with user specific override options.
95 82 default_config = self._get_default_options()
96 83 if not config == None:
97 84 default_config._merge(config)
98 85 config = default_config
99 86
100 87 #Call the base class constructor
101 88 super(Exporter, self).__init__(config=config, **kw)
102 89
103 90 #Create a Latex environment if the user is exporting latex.
104 91 if self.tex_environement:
105 92 self.ext = LATEX_TEMPLATE_EXTENSION
106 93 self.env = Environment(
107 94 loader=FileSystemLoader([
108 95 os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_PATH,
109 96 os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_SKELETON_PATH,
110 97 ]),
111 98 extensions=JINJA_EXTENSIONS
112 99 )
113 100
114 101 #Set special Jinja2 syntax that will not conflict with latex.
115 102 self.env.block_start_string = LATEX_JINJA_LOGIC_BLOCK[0]
116 103 self.env.block_end_string = LATEX_JINJA_LOGIC_BLOCK[1]
117 104 self.env.variable_start_string = LATEX_JINJA_VARIABLE_BLOCK[0]
118 105 self.env.variable_end_string = LATEX_JINJA_VARIABLE_BLOCK[1]
119 106 self.env.comment_start_string = LATEX_JINJA_COMMENT_BLOCK[0]
120 107 self.env.comment_end_string = LATEX_JINJA_COMMENT_BLOCK[1]
121 108
122 109 else: #Standard environment
123 110 self.ext = TEMPLATE_EXTENSION
124 111 self.env = Environment(
125 112 loader=FileSystemLoader([
126 113 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH,
127 114 os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH,
128 115 ]),
129 116 extensions=JINJA_EXTENSIONS
130 117 )
131 118
132 119 for name in self.pre_transformer_order:
133 120 # get the user-defined transformer first
134 121 transformer = preprocessors.get(name, getattr(trans, name, None))
135 122 if isinstance(transformer, MetaHasTraits):
136 123 transformer = transformer(config=config)
137 124 self.preprocessors.append(transformer)
138 125
139 126 #For compatibility, TODO: remove later.
140 127 self.preprocessors.append(trans.coalesce_streams)
141 128 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
142 129 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
143 130 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
144 131 self.preprocessors.append(LatexTransformer(config=config))
145 132
146 133 #Only load the sphinx transformer if the file reference worked
147 134 #(Sphinx dependencies exist on the user's machine.)
148 135 if SphinxTransformer:
149 136 self.preprocessors.append(SphinxTransformer(config=config))
150 137
151 138 #Add filters to the Jinja2 environment
152 self.env.filters['filter_data_type'] = DataTypeFilter(config=config)
153 self.env.filters['pycomment'] = _python_comment
154 self.env.filters['indent'] = indent
155 self.env.filters['rm_fake'] = _rm_fake
156 self.env.filters['rm_ansi'] = remove_ansi
157 self.env.filters['markdown'] = markdown
158 self.env.filters['ansi2html'] = ansi2html
159 self.env.filters['markdown2latex'] = markdown_utils.markdown2latex
160 self.env.filters['markdown2rst'] = markdown_utils.markdown2rst
161 self.env.filters['get_lines'] = get_lines
162 self.env.filters['wrap'] = strings.wrap
163 self.env.filters['rm_dollars'] = strings.strip_dollars
164 self.env.filters['rm_math_space'] = rm_math_space
165 self.env.filters['highlight2html'] = highlight
166 self.env.filters['highlight2latex'] = highlight2latex
167
168 #Latex specific filters
169 if self.tex_environement:
170 self.env.filters['escape_tex'] = _escape_tex
171 self.env.filters['highlight'] = highlight2latex
172 else:
173 self.env.filters['highlight'] = highlight
174
139 self.env.filters['escape_tex'] = filters.latex.escape_tex
140 self.env.filters['highlight'] = filters.pygments.highlight2latex
141
175 142 #Load user filters. Overwrite existing filters if need be.
176 143 for key, user_filter in jinja_filters.iteritems():
177 144 if isinstance(user_filter, MetaHasTraits):
178 145 self.env.filters[key] = user_filter(config=config)
179 146 else:
180 147 self.env.filters[key] = user_filter
181 148
182 149 #Load the template file.
183 150 self.template = self.env.get_template(self.template_file+self.ext)
184
185
186 def export(self, nb):
187 """Export notebook object
188
189 nb: Notebook object to export.
190
191 Returns both the converted ipynb file and a dict containing the
192 resources created along the way via the transformers and Jinja2
193 processing.
194 """
195
196 nb, resources = self._preprocess(nb)
197 return self.template.render(nb=nb, resources=resources), resources
198
199
200 def from_filename(self, filename):
201 """Read and export a notebook from a filename
202
203 filename: Filename of the notebook file to export.
204
205 Returns both the converted ipynb file and a dict containing the
206 resources created along the way via the transformers and Jinja2
207 processing.
208 """
209 with io.open(filename) as f:
210 return self.export(nbformat.read(f, 'json'))
211
212
213 def from_file(self, file_stream):
214 """Read and export a notebook from a filename
215
216 file_stream: File handle of file that contains notebook data.
217
218 Returns both the converted ipynb file and a dict containing the
219 resources created along the way via the transformers and Jinja2
220 processing.
221 """
222
223 return self.export(nbformat.read(file_stream, 'json'))
224
225
226 def _preprocess(self, nb):
227 """ Preprocess the notebook using the transformers specific
228 for the current export format.
229
230 nb: Notebook to preprocess
231 """
232
233 #Dict of 'resources' that can be filled by the preprocessors.
234 resources = {}
235
236 #Run each transformer on the notebook. Carry the output along
237 #to each transformer
238 for transformer in self.preprocessors:
239 nb, resources = transformer(nb, resources)
240 return nb, resources
241
242
243 def _get_default_options(self, export_format):
244 """ Load the default options for built in formats.
245
246 export_format: Format being exported to.
247 """
248
249 c = get_config()
250
251 #Set default data extraction priorities.
252 c.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text']
253 c.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text']
254 c.ConverterTemplate.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text']
255
256 #For most (or all cases), the template file name matches the format name.
257 c.ConverterTemplate.template_file = export_format
258
259 if export_format == "basichtml" or "fullhtml" or "reveal":
260 c.CSSHtmlHeaderTransformer.enabled=True
261 if export_format == 'reveal'
262 c.NbconvertApp.fileext='reveal.html'
263 else:
264 c.NbconvertApp.fileext='html'
265
266 elif export_format == "latex_sphinx_howto" or export_format == "latex_sphinx_manual":
267
268 #Turn on latex environment
269 c.ConverterTemplate.tex_environement=True
270
271 #Standard latex extension
272 c.NbconvertApp.fileext='tex'
273
274 #Prioritize latex extraction for latex exports.
275 c.GlobalConfigurable.display_data_priority =['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text']
276 c.ExtractFigureTransformer.display_data_priority=['latex', 'svg', 'png', 'jpg', 'jpeg']
277 c.ExtractFigureTransformer.extra_ext_map={'svg':'pdf'}
278 c.ExtractFigureTransformer.enabled=True
279
280 # Enable latex transformers (make markdown2latex work with math $.)
281 c.LatexTransformer.enabled=True
282 c.SphinxTransformer.enabled = True
283
284 elif export_format == 'markdown':
285 c.NbconvertApp.fileext='md'
286 c.ExtractFigureTransformer.enabled=True
287
288 elif export_format == 'python':
289 c.NbconvertApp.fileext='py'
290
291
292 elif export_format == 'rst':
293 c.NbconvertApp.fileext='rst'
294 c.ExtractFigureTransformer.enabled=True
295 return c No newline at end of file
@@ -1,31 +1,31 b''
1 1 """Filter used to select the first prefered output format available.
2 2
3 3 The filter contained in the file allows the converter templates to select
4 4 the output format that is most valuable to the active export format. The
5 5 value of the different formats is set via
6 6 GlobalConfigurable.display_data_priority
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (c) 2013, the IPython Development Team.
10 10 #
11 11 # Distributed under the terms of the Modified BSD License.
12 12 #
13 13 # The full license is in the file COPYING.txt, distributed with this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Classes and functions
18 18 #-----------------------------------------------------------------------------
19 19 class DataTypeFilter(GlobalConfigurable):
20 20 """ Returns the prefered display format """
21 21
22 22 def __init__(self, config=None, **kw):
23 super(FilterDataType, self).__init__(config=config, **kw)
23 super(DataTypeFilter, self).__init__(config=config, **kw)
24 24
25 25 def __call__(self, output):
26 26 """ Return the first available format in the priority """
27 27
28 28 for fmt in self.display_data_priority:
29 29 if fmt in output:
30 30 return [fmt]
31 31 return [] No newline at end of file
@@ -1,72 +1,95 b''
1 1 """Latex transformer.
2 2
3 3 Module that allows latex output notebooks to be conditioned before
4 4 they are converted.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 from __future__ import print_function
17 import re
18
19 #-----------------------------------------------------------------------------
20 # Globals and constants
21 #-----------------------------------------------------------------------------
22
23 #Latex substitutions for escaping latex.
24 LATEX_SUBS = (
25 (re.compile(r'\\'), r'\\textbackslash'),
26 (re.compile(r'([{}_#%&$])'), r'\\\1'),
27 (re.compile(r'~'), r'\~{}'),
28 (re.compile(r'\^'), r'\^{}'),
29 (re.compile(r'"'), r"''"),
30 (re.compile(r'\.\.\.+'), r'\\ldots'),
31 )
18 32
19 33 #-----------------------------------------------------------------------------
20 34 # Functions
21 35 #-----------------------------------------------------------------------------
36
37 #TODO: Comment me.
38 def escape_tex(value):
39 newval = value
40 for pattern, replacement in LATEX_SUBS:
41 newval = pattern.sub(replacement, newval)
42 return newval
43
44
22 45 def rm_math_space(text):
23 46 """
24 47 Remove the space between latex math commands and enclosing $ symbols.
25 48 """
26 49
27 50 # First, scan through the markdown looking for $. If
28 51 # a $ symbol is found, without a preceding \, assume
29 52 # it is the start of a math block. UNLESS that $ is
30 53 # not followed by another within two math_lines.
31 54 math_regions = []
32 55 math_lines = 0
33 56 within_math = False
34 57 math_start_index = 0
35 58 ptext = ''
36 59 last_character = ""
37 60 skip = False
38 61 for index, char in enumerate(text):
39 62
40 63 #Make sure the character isn't preceeded by a backslash
41 64 if (char == "$" and last_character != "\\"):
42 65
43 66 # Close the math region if this is an ending $
44 67 if within_math:
45 68 within_math = False
46 69 skip = True
47 70 ptext = ptext+'$'+text[math_start_index+1:index].strip()+'$'
48 71 math_regions.append([math_start_index, index+1])
49 72 else:
50 73
51 74 # Start a new math region
52 75 within_math = True
53 76 math_start_index = index
54 77 math_lines = 0
55 78
56 79 # If we are in a math region, count the number of lines parsed.
57 80 # Cancel the math region if we find two line breaks!
58 81 elif char == "\n":
59 82 if within_math:
60 83 math_lines += 1
61 84 if math_lines > 1:
62 85 within_math = False
63 86 ptext = ptext+text[math_start_index:index]
64 87
65 88 # Remember the last character so we can easily watch
66 89 # for backslashes
67 90 last_character = char
68 91 if not within_math and not skip:
69 92 ptext = ptext+char
70 93 if skip:
71 94 skip = False
72 95 return ptext
@@ -1,75 +1,77 b''
1 1 """Markdown utilities
2 2
3 3 This file contains a collection of utility functions for dealing with
4 4 markdown.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from __future__ import print_function
18 18
19 19 # Stdlib imports
20 import sys
20 21 import subprocess
21 22
22 23 #-----------------------------------------------------------------------------
23 24 # Functions
24 25 #-----------------------------------------------------------------------------
26
25 27 # Pandoc-dependent code
26 28 def markdown2latex(src):
27 29 """Convert a markdown string to LaTeX via pandoc.
28 30
29 31 This function will raise an error if pandoc is not installed.
30 32
31 33 Any error messages generated by pandoc are printed to stderr.
32 34
33 35 Parameters
34 36 ----------
35 37 src : string
36 38 Input string, assumed to be valid markdown.
37 39
38 40 Returns
39 41 -------
40 42 out : string
41 43 Output as returned by pandoc.
42 44 """
43 45 p = subprocess.Popen('pandoc -f markdown -t latex'.split(),
44 46 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
45 47 out, err = p.communicate(src.encode('utf-8'))
46 48 if err:
47 49 print(err, file=sys.stderr)
48 50 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
49 51 return unicode(out, 'utf-8')
50 52
51 53
52 54 def markdown2rst(src):
53 55 """Convert a markdown string to LaTeX via pandoc.
54 56
55 57 This function will raise an error if pandoc is not installed.
56 58
57 59 Any error messages generated by pandoc are printed to stderr.
58 60
59 61 Parameters
60 62 ----------
61 63 src : string
62 64 Input string, assumed to be valid markdown.
63 65
64 66 Returns
65 67 -------
66 68 out : string
67 69 Output as returned by pandoc.
68 70 """
69 71 p = subprocess.Popen('pandoc -f markdown -t rst'.split(),
70 72 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
71 73 out, err = p.communicate(src.encode('utf-8'))
72 74 if err:
73 75 print(err, file=sys.stderr)
74 76 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
75 77 return unicode(out, 'utf-8') No newline at end of file
@@ -1,35 +1,57 b''
1 """String utilities.
1 """String utilities.
2 2
3 3 Contains a collection of usefull string manipulations functions.
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 # Our own imports
18 18 import textwrap #TODO
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Functions
22 22 #-----------------------------------------------------------------------------
23 23 def wrap(text, width=100):
24 24 """ Intelligently wrap text"""
25 25
26 26 splitt = text.split('\n')
27 27 wrp = map(lambda x:textwrap.wrap(x,width),splitt)
28 28 wrpd = map('\n'.join, wrp)
29 29 return '\n'.join(wrpd)
30 30
31 31
32 32 def strip_dollars(text):
33 33 """Remove all dollar symbols from text"""
34 34
35 return text.strip('$') No newline at end of file
35 return text.strip('$')
36
37
38 #TODO: Comment me.
39 def rm_fake(strng):
40 return strng.replace('/files/', '')
41
42
43 #TODO: Comment me.
44 def python_comment(string):
45 return '# '+'\n# '.join(string.split('\n'))
46
47 def get_lines(src, start=None,end=None):
48 """
49 Split the input text into separate lines and then return the
50 lines that the caller is interested in.
51 """
52
53 # Split the input into lines.
54 lines = src.split("\n")
55
56 # Return the right lines.
57 return "\n".join(lines[start:end]) #re-join
@@ -1,18 +1,19 b''
1
1 from transformers.base import ConfigurableTransformers
2
3
2 4 class ActivatableTransformer(ConfigurableTransformers):
3 5 """A simple ConfigurableTransformers that have an enabled flag
4 6
5 7 Inherit from that if you just want to have a transformer which is
6 8 no-op by default but can be activated in profiles with
7 9
8 10 c.YourTransformerName.enabled = True
9 11 """
10 12
11 13 enabled = Bool(False, config=True)
12 14
13 15 def __call__(self, nb, other):
14 16 if not self.enabled :
15 17 return nb, other
16 18 else :
17 19 return super(ActivatableTransformer, self).__call__(nb, other)
18
@@ -1,120 +1,121 b''
1 1 """
2 2 Module that regroups transformer that woudl be applied to ipynb files
3 3 before going through the templating machinery.
4 4
5 5 It exposes convenient classes to inherit from to access configurability
6 6 as well as decorator to simplify tasks.
7 7 """
8 8
9 9 from __future__ import print_function, absolute_import
10 10
11 11 from IPython.config.configurable import Configurable
12 12 from IPython.utils.traitlets import Unicode, Bool, Dict, List
13 13
14 14 from .config import GlobalConfigurable
15 15
16 16 class ConfigurableTransformers(GlobalConfigurable):
17 17 """ A configurable transformer
18 18
19 19 Inherit from this class if you wish to have configurability for your
20 20 transformer.
21 21
22 22 Any configurable traitlets this class exposed will be configurable in profiles
23 23 using c.SubClassName.atribute=value
24 24
25 25 you can overwrite cell_transform to apply a transformation independently on each cell
26 26 or __call__ if you prefer your own logic. See orresponding docstring for informations.
27 27
28 28
29 29 """
30 30
31 31 def __init__(self, config=None, **kw):
32 32 super(ConfigurableTransformers, self).__init__(config=config, **kw)
33 33
34 34 def __call__(self, nb, other):
35 35 """transformation to apply on each notebook.
36 36
37 37 received a handle to the current notebook as well as a dict of resources
38 38 which structure depends on the transformer.
39 39
40 40 You should return modified nb, other.
41 41
42 42 If you wish to apply on each cell, you might want to overwrite cell_transform method.
43 43 """
44 44 try :
45 45 for worksheet in nb.worksheets :
46 46 for index, cell in enumerate(worksheet.cells):
47 47 worksheet.cells[index], other = self.cell_transform(cell, other, 100*index)
48 48 return nb, other
49 49 except NotImplementedError:
50 50 raise NotImplementedError('should be implemented by subclass')
51 51
52 52 def cell_transform(self, cell, other, index):
53 53 """
54 54 Overwrite if you want to apply a transformation on each cell,
55 55
56 56 receive the current cell, the resource dict and the index of current cell as parameter.
57 57
58 58 You should return modified cell and resource dict.
59 59 """
60
60 61 raise NotImplementedError('should be implemented by subclass')
61 62 return cell, other
62 63
63 64 def cell_preprocessor(function):
64 65 """ wrap a function to be executed on all cells of a notebook
65 66
66 67 wrapped function parameters :
67 68 cell : the cell
68 69 other : external resources
69 70 index : index of the cell
70 71 """
71 72 def wrappedfunc(nb, other):
72 73 for worksheet in nb.worksheets :
73 74 for index, cell in enumerate(worksheet.cells):
74 75 worksheet.cells[index], other = function(cell, other, index)
75 76 return nb, other
76 77 return wrappedfunc
77 78
78 79
79 80 @cell_preprocessor
80 81 def haspyout_transformer(cell, other, count):
81 82 """
82 83 Add a haspyout flag to cell that have it
83 84
84 85 Easier for templating, where you can't know in advance
85 86 wether to write the out prompt
86 87
87 88 """
88 89 cell.type = cell.cell_type
89 90 cell.haspyout = False
90 91 for out in cell.get('outputs', []):
91 92 if out.output_type == 'pyout':
92 93 cell.haspyout = True
93 94 break
94 95 return cell, other
95 96
96 97 @cell_preprocessor
97 98 def coalesce_streams(cell, other, count):
98 99 """merge consecutive sequences of stream output into single stream
99 100
100 101 to prevent extra newlines inserted at flush calls
101 102
102 103 TODO: handle \r deletion
103 104 """
104 105 outputs = cell.get('outputs', [])
105 106 if not outputs:
106 107 return cell, other
107 108 new_outputs = []
108 109 last = outputs[0]
109 110 new_outputs = [last]
110 111 for output in outputs[1:]:
111 112 if (output.output_type == 'stream' and
112 113 last.output_type == 'stream' and
113 114 last.stream == output.stream
114 115 ):
115 116 last.text += output.text
116 117 else:
117 118 new_outputs.append(output)
118 119
119 120 cell.outputs = new_outputs
120 121 return cell, other
1 NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755, file renamed from nbconvert/transformers/reavealhelp.py to nbconvert/transformers/revealhelp.py
1 NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: file renamed from nbconvert1/converters/lexers.py to nbconvert/utils/lexers.py
General Comments 0
You need to be logged in to leave comments. Login now