##// END OF EJS Templates
fix some relative path issues
Matthias BUSSONNIER -
Show More
@@ -1,74 +1,74 b''
1 1 #-----------------------------------------------------------------------------
2 2 # Copyright (c) 2012, the IPython Development Team.
3 3 #
4 4 # Distributed under the terms of the Modified BSD License.
5 5 #
6 6 # The full license is in the file COPYING.txt, distributed with this software.
7 7 #-----------------------------------------------------------------------------
8 8
9 9 from __future__ import absolute_import
10 10
11 11 # Stdlib imports
12 12 import re
13 13
14 14 from IPython.utils.text import indent
15 15 from markdown import markdown
16 16 from .utils import remove_ansi
17 17 from .utils import highlight, ansi2html
18 18 from .utils import markdown2latex
19 19 from .utils import highlight2latex
20 20 from .utils import get_lines
21 21
22 from converters.config import GlobalConfigurable
22 from .config import GlobalConfigurable
23 23
24 24 from IPython.config.configurable import Configurable
25 25 from IPython.utils.traitlets import List
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Class declarations
29 29 #-----------------------------------------------------------------------------
30 30
31 31 class ConfigurableFilter(GlobalConfigurable):
32 32 """Configurable Jinja Filter"""
33 33
34 34 def __init__(self, config=None, **kw):
35 35 super(ConfigurableFilter, self).__init__(config=config, **kw)
36 36
37 37 def __call__(self, *args, **kwargs):
38 38 raise NotImplementedError('should be implemented by subclass')
39 39
40 40
41 41 class FilterDataType(ConfigurableFilter):
42 42 """ return the preferd displayed format
43 43 """
44 44
45 45 def __call__(self, output):
46 46 """ return the first availlable format in priority """
47 47 for fmt in self.display_data_priority:
48 48 if fmt in output:
49 49 return [fmt]
50 50 raise Exception("did not found any format I can extract in output, shoudl at lest have one")
51 51
52 52
53 53
54 54 def rm_fake(strng):
55 55 return strng.replace('/files/', '')
56 56
57 57 def python_comment(string):
58 58 return '# '+'\n# '.join(string.split('\n'))
59 59
60 60 LATEX_SUBS = (
61 61 (re.compile(r'\\'), r'\\textbackslash'),
62 62 (re.compile(r'([{}_#%&$])'), r'\\\1'),
63 63 (re.compile(r'~'), r'\~{}'),
64 64 (re.compile(r'\^'), r'\^{}'),
65 65 (re.compile(r'"'), r"''"),
66 66 (re.compile(r'\.\.\.+'), r'\\ldots'),
67 67 )
68 68
69 69 def escape_tex(value):
70 70 newval = value
71 71 for pattern, replacement in LATEX_SUBS:
72 72 newval = pattern.sub(replacement, newval)
73 73 return newval
74 74
@@ -1,127 +1,128 b''
1 1 """
2 2 Module that allows latex output notebooks to be conditioned before
3 3 they are converted.
4 4 """
5 from __future__ import absolute_import
5 6
6 7 # Configurable traitlets
7 8 from IPython.utils.traitlets import Unicode, Bool
8 9
9 10 # Needed to override transformer
10 from converters.transformers import (ActivatableTransformer)
11 from .transformers import (ActivatableTransformer)
11 12
12 13 class LatexTransformer(ActivatableTransformer):
13 14 """
14 15 Converter for latex destined documents.
15 16 """
16 17
17 18 def cell_transform(self, cell, other, index):
18 19 """
19 20 Apply a transformation on each cell,
20 21
21 22 receive the current cell, the resource dict and the index of current cell as parameter.
22 23
23 24 Returns modified cell and resource dict.
24 25 """
25 26 if hasattr(cell, "source") and cell.cell_type == "markdown":
26 27 cell.source = self.remove_math_space(cell.source)
27 28 return cell, other
28 29
29 30 def remove_math_space(self, text):
30 31 """
31 32 Remove the space between latex math commands and enclosing $ symbols.
32 33 """
33 34
34 35 # First, scan through the markdown looking for $. If
35 36 # a $ symbol is found, without a preceding \, assume
36 37 # it is the start of a math block. UNLESS that $ is
37 38 # not followed by another within two math_lines.
38 39 math_regions = []
39 40 math_lines = 0
40 41 within_math = False
41 42 math_start_index = 0
42 43 index = 0
43 44 last_character = ""
44 45 for char in text: #Loop through each character in the text.
45 46
46 47 #Make sure the character isn't preceeded by a backslash
47 48 if (char == "$" and last_character != "\\"):
48 49
49 50 # Close the math region if this is an ending $
50 51 if within_math:
51 52 within_math = False
52 53 math_regions.append([math_start_index, index+1])
53 54 else:
54 55
55 56 # Start a new math region
56 57 within_math = True
57 58 math_start_index = index
58 59 math_lines = 0
59 60
60 61 # If we are in a math region, count the number of lines parsed.
61 62 # Cancel the math region if we find two line breaks!
62 63 elif char == "\n":
63 64 if within_math:
64 65 math_lines += 1
65 66 if math_lines > 1:
66 67 within_math = False
67 68
68 69 # Remember the last character so we can easily watch
69 70 # for backslashes
70 71 last_character = char
71 72
72 73 # Next index
73 74 index += 1
74 75
75 76 # Reset the index and last char
76 77 index = 0
77 78
78 79 # Now that we know what regions of the text are math and
79 80 # what regions aren't, we can separate them into "blocks"
80 81 text_blocks=[]
81 82 math_blocks=[]
82 83 was_math_block = False
83 84 current_block = ""
84 85 for char in text:
85 86
86 87 # Check if this is a math region.
87 88 ismath = False
88 89 for keypair in math_regions:
89 90 if (keypair[0] <= index and index <= keypair[1]):
90 91 ismath = True
91 92
92 93 # If the region type has changed since the last
93 94 # iteration, commit all read characters to that
94 95 # region type and reset the buffer.
95 96 if (ismath and not was_math_block):
96 97 was_math_block = True
97 98 text_blocks.append(current_block)
98 99 current_block=""
99 100 elif ((not ismath) and was_math_block):
100 101 was_math_block = False
101 102 math_blocks.append(current_block)
102 103 current_block=""
103 104
104 105 # Store the character
105 106 current_block += char
106 107
107 108 # Next index
108 109 index += 1
109 110
110 111 # Save whatever remains in the buffer that hasn't yet been saved.
111 112 if was_math_block:
112 113 math_blocks.append(current_block)
113 114 else:
114 115 text_blocks.append(current_block)
115 116
116 117 # Recombine the regions, while processing every math region, removing
117 118 # the spaces between the math and the $ symbols.
118 119 output = ""
119 120 for index in range(0,len(text_blocks) + len(math_blocks)):
120 121 if index % 2 == 0:
121 122 output += text_blocks[index/2]
122 123 else:
123 124 mathblock = math_blocks[(index -1)/2]
124 125 mathblock = mathblock[1:len(mathblock)-2]
125 126 output += "$" + mathblock.strip() + "$"
126 127 return output
127 128
@@ -1,199 +1,200 b''
1 1 """
2 2 Module that allows custom Sphinx parameters to be set on the notebook and
3 3 on the 'other' object passed into Jinja.
4 4 """
5 from __future__ import absolute_import
5 6
6 7 # Used to find Sphinx package location
7 8 import sphinx
8 9 import os.path
9 10
10 11 # Used to determine python version
11 12 import sys
12 13
13 14 # Used to set the default date to today's date
14 15 from datetime import date
15 16
16 17 # Configurable traitlets
17 18 from IPython.utils.traitlets import Unicode, Bool
18 19
19 20 # Needed for Pygments latex definitions.
20 21 from pygments.formatters import LatexFormatter
21 22
22 23 # Needed to override transformer
23 from converters.transformers import (ActivatableTransformer)
24 from .transformers import (ActivatableTransformer)
24 25
25 26 class SphinxTransformer(ActivatableTransformer):
26 27 """
27 28 Sphinx utility transformer.
28 29
29 30 This transformer is used to set variables needed by the latex to build
30 31 Sphinx stylized templates.
31 32 """
32 33
33 34 interactive = Bool(True, config=True, help="""
34 35 Allows you to define whether or not the Sphinx exporter will prompt
35 36 you for input during the conversion process. If this is set to false,
36 37 the author, version, release, date, and chapterstyle traits should
37 38 be set.
38 39 """)
39 40
40 41 author = Unicode("Unknown Author", config=True, help="Author name")
41 42
42 43 version = Unicode("", config=True, help="""Version number
43 44 You can leave this blank if you do not want to render a version number.
44 45 Example: "1.0.0"
45 46 """)
46 47
47 48 release = Unicode("", config=True, help="""Release name
48 49 You can leave this blank if you do not want to render a release name.
49 50 Example: "Rough Draft"
50 51 """)
51 52
52 53 publishdate = Unicode("", config=True, help="""Publish date
53 54 This is the date to render on the document as the publish date.
54 55 Leave this blank to default to todays date.
55 56 Example: "June 12, 1990"
56 57 """)
57 58
58 59 chapterstyle = Unicode("Bjarne", config=True, help="""Sphinx chapter style
59 60 This is the style to use for the chapter headers in the document.
60 61 You may choose one of the following:
61 62 "Bjarne" (default)
62 63 "Lenny"
63 64 "Glenn"
64 65 "Conny"
65 66 "Rejne"
66 67 "Sonny" (used for international documents)
67 68 """)
68 69
69 70
70 71 def __call__(self, nb, other):
71 72 """
72 73 Entry
73 74 Since we are not interested in any additional manipulation on a cell
74 75 by cell basis, we do not call the base implementation.
75 76 """
76 77 if self.enabled:
77 78 return self.transform(nb, other)
78 79 else:
79 80 return nb,other
80 81
81 82 def transform(self, nb, other):
82 83 """
83 84 Sphinx transformation to apply on each notebook.
84 85 """
85 86
86 87 if self.interactive:
87 88
88 89 # Prompt the user for additional meta data that doesn't exist currently
89 90 # but would be usefull for Sphinx.
90 91 nb.metadata["author"] = self._prompt_author()
91 92 nb.metadata["version"] = self._prompt_version()
92 93 nb.metadata["release"] = self._prompt_release()
93 94 nb.metadata["date"] = self._prompt_date()
94 95
95 96 # Prompt the user for the document style.
96 97 other["sphinx_chapterstyle"] = self._prompt_chapter_title_style()
97 98 else:
98 99
99 100 # Try to use the traitlets.
100 101 nb.metadata["author"] = self.author
101 102 nb.metadata["version"] = self.version
102 103 nb.metadata["release"] = self.release
103 104
104 105 if len(self.publishdate.strip()) == 0:
105 106 nb.metadata["date"] = date.today().strftime("%B %-d, %Y")
106 107 else:
107 108 nb.metadata["date"] = self.publishdate
108 109
109 110 other["sphinx_chapterstyle"] = self.chapterstyle
110 111
111 112 # Find and pass in the path to the Sphinx dependencies.
112 113 other["sphinx_texinputs"] = os.path.abspath(sphinx.__file__ + "/../texinputs")
113 114
114 115 # Generate Pygments definitions for Latex
115 116 other["pygment_definitions"] = self._generate_pygments_latex_def()
116 117
117 118 # End
118 119 return nb, other
119 120
120 121 def _generate_pygments_latex_def(self):
121 122 return LatexFormatter().get_style_defs()
122 123
123 124 def _prompt_author(self):
124 125 return self._input("Author name: ")
125 126
126 127 def _prompt_version(self):
127 128 return self._input("Version (ie ""1.0.0""): ")
128 129
129 130 def _prompt_release(self):
130 131 return self._input("Release Name (ie ""Rough draft""): ")
131 132
132 133 def _prompt_date(self):
133 134 default_date = date.today().strftime("%B %-d, %Y")
134 135 user_date = self._input("Date (deafults to \"" + default_date + "\"): ")
135 136 if len(user_date.strip()) == 0:
136 137 user_date = default_date
137 138 return user_date
138 139
139 140 def _prompt_chapter_title_style(self):
140 141
141 142 # Dictionary of available Sphinx styles
142 143 styles = {1: "Bjarne",
143 144 2: "Lenny",
144 145 3: "Glenn",
145 146 4: "Conny",
146 147 5: "Rejne",
147 148 6: "Sonny"}
148 149 default_style = 1
149 150
150 151 # Build the menu that will be displayed to the user with
151 152 # all of the options available.
152 153 style_prompt = ""
153 154 for key, value in styles.iteritems():
154 155 style_prompt += "%d %s" % (key, value)
155 156 if key == default_style:
156 157 style_prompt += " (default)"
157 158 elif value == "Sonny":
158 159 style_prompt += " (for international documents)"
159 160 style_prompt += "\n"
160 161
161 162 # Continue to ask the user for a style until an appropriate
162 163 # one is specified.
163 164 response = -1
164 165 while (0 > response or response > 6):
165 166 try:
166 167 text_response = self._input(style_prompt)
167 168
168 169 # Use default option if no input.
169 170 if len(text_response.strip()) == 0:
170 171 response = 1
171 172 else:
172 173 response = int(text_response)
173 174 except:
174 175 print("Error: Value must be a number between 1 and 6, leave blank for default\n")
175 176 return styles[response]
176 177
177 178 def _input(self, prompt_text):
178 179 """
179 180 Prompt the user for input.
180 181
181 182 The input command will change depending on the version of python
182 183 installed. To maintain support for 2 and earlier, we must use
183 184 raw_input in that case. Else use input.
184 185 """
185 186
186 187 # Try to get the python version. This command is only available in
187 188 # python 2 and later, so it's important that we catch the exception
188 189 # if the command isn't found.
189 190 try:
190 191 majorversion = sys.version_info[0]
191 192 except:
192 193 majorversion = 1
193 194
194 195 # Use the correct function to prompt the user for input depending on
195 196 # what python version the code is running in.
196 197 if majorversion >= 3:
197 198 return input(prompt_text)
198 199 else:
199 200 return raw_input(prompt_text)
@@ -1,239 +1,247 b''
1 1 """Base classes for the notebook conversion pipeline.
2 2
3 3 This module defines ConverterTemplate, a highly configurable converter
4 4 that uses Jinja2 to convert 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
10 from __future__ import absolute_import
11
9 12 #-----------------------------------------------------------------------------
10 13 # Copyright (c) 2013, the IPython Development Team.
11 14 #
12 15 # Distributed under the terms of the Modified BSD License.
13 16 #
14 17 # The full license is in the file COPYING.txt, distributed with this software.
15 18 #-----------------------------------------------------------------------------
16 19
17 20 #-----------------------------------------------------------------------------
18 21 # Imports
19 22 #-----------------------------------------------------------------------------
20 23
21 from __future__ import print_function, absolute_import
24 from __future__ import print_function
25 from __future__ import absolute_import
22 26
23 27 # Stdlib imports
24 28 import io
29 import os
25 30
26 31 # IPython imports
27 32 from IPython.utils.traitlets import MetaHasTraits
28 33 from IPython.utils.traitlets import (Unicode, List, Bool)
29 34 from IPython.config.configurable import Configurable
30 35 from IPython.nbformat import current as nbformat
31 36
32 37
33 38 # other libs/dependencies
34 39 from jinja2 import Environment, FileSystemLoader
35 40
36 41
37 42 # local import (pre-transformers)
38 import converters.transformers as trans
39 from converters.sphinx_transformer import (SphinxTransformer)
40 from converters.latex_transformer import (LatexTransformer)
43 from . import transformers as trans
44 from .sphinx_transformer import (SphinxTransformer)
45 from .latex_transformer import (LatexTransformer)
41 46
42 47 # some jinja filters
43 from converters.jinja_filters import (python_comment, indent,
48 from .jinja_filters import (python_comment, indent,
44 49 rm_fake, remove_ansi, markdown, highlight, highlight2latex,
45 50 ansi2html, markdown2latex, get_lines, escape_tex, FilterDataType)
46 51
47 from converters.utils import markdown2rst
52 from .utils import markdown2rst
48 53
49 54 import textwrap
50 55
51 56 def wrap(text, width=100):
52 57 """ try to detect and wrap paragraph"""
53 58 splitt = text.split('\n')
54 59 wrp = map(lambda x:textwrap.wrap(x,width),splitt)
55 60 wrpd = map('\n'.join, wrp)
56 61 return '\n'.join(wrpd)
57 62
58 63
59 64
60 65 # define differents environemnt with different
61 66 # delimiters not to conflict with languages inside
62 67
63 68 env = Environment(
64 69 loader=FileSystemLoader([
65 './templates/',
66 './templates/skeleton/',
70 os.path.dirname(os.path.realpath(__file__))+'/../templates/',
71 os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/',
67 72 ]),
68 73 extensions=['jinja2.ext.loopcontrols']
69 74 )
70 75
76 print(os.path.dirname(os.path.realpath(__file__))+'/../templates/')
77
78
71 79 texenv = Environment(
72 80 loader=FileSystemLoader([
73 './templates/tex/',
74 './templates/skeleton/tex/',
81 os.path.dirname(os.path.realpath(__file__))+'/../templates/tex/',
82 os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/tex/',
75 83 ]),
76 84 extensions=['jinja2.ext.loopcontrols']
77 85 )
78 86
79 87
80 88 texenv.block_start_string = '((*'
81 89 texenv.block_end_string = '*))'
82 90
83 91 texenv.variable_start_string = '((('
84 92 texenv.variable_end_string = ')))'
85 93
86 94 texenv.comment_start_string = '((='
87 95 texenv.comment_end_string = '=))'
88 96
89 97 texenv.filters['escape_tex'] = escape_tex
90 98
91 99 #-----------------------------------------------------------------------------
92 100 # Class declarations
93 101 #-----------------------------------------------------------------------------
94 102 class ConversionException(Exception):
95 103 pass
96 104
97 105 class ConverterTemplate(Configurable):
98 106 """ A Jinja2 base converter templates
99 107
100 108 Preprocess the ipynb files, feed it throug jinja templates,
101 109 and spit an converted files and a data object with other data
102 110
103 111 should be mostly configurable
104 112 """
105 113
106 114 pre_transformer_order = List(['haspyout_transformer'],
107 115 config=True,
108 116 help= """
109 117 An ordered list of pre transformer to apply to the ipynb
110 118 file before running through templates
111 119 """
112 120 )
113 121
114 122 tex_environement = Bool(False,
115 123 config=True,
116 124 help=""" is this a tex environment or not """)
117 125
118 126 template_file = Unicode('',
119 127 config=True,
120 128 help=""" Name of the template file to use """ )
121 129 #-------------------------------------------------------------------------
122 130 # Instance-level attributes that are set in the constructor for this
123 131 # class.
124 132 #-------------------------------------------------------------------------
125 133
126 134
127 135 preprocessors = []
128 136
129 137 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
130 138 """ Init a new converter.
131 139
132 140 config: the Configurable config object to pass around.
133 141
134 142 preprocessors: dict of **availlable** key/value function to run on
135 143 ipynb json data before conversion to extract/inline file.
136 144 See `transformer.py` and `ConfigurableTransformers`
137 145
138 146 set the order in which the transformers should apply
139 147 with the `pre_transformer_order` trait of this class
140 148
141 149 transformers registerd by this key will take precedence on
142 150 default one.
143 151
144 152
145 153 jinja_filters: dict of supplementary jinja filter that should be made
146 154 availlable in template. If those are of Configurable Class type,
147 155 they will be instanciated with the config object as argument.
148 156
149 157 user defined filter will overwrite the one availlable by default.
150 158 """
151 159 super(ConverterTemplate, self).__init__(config=config, **kw)
152 160
153 161 # variable parameters depending on the pype of jinja environement
154 162 self.env = texenv if self.tex_environement else env
155 163 self.ext = '.tplx' if self.tex_environement else '.tpl'
156 164
157 165 for name in self.pre_transformer_order:
158 166 # get the user-defined transformer first
159 167 transformer = preprocessors.get(name, getattr(trans, name, None))
160 168 if isinstance(transformer, MetaHasTraits):
161 169 transformer = transformer(config=config)
162 170 self.preprocessors.append(transformer)
163 171
164 172 ## for compat, remove later
165 173 self.preprocessors.append(trans.coalesce_streams)
166 174 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
167 175 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
168 176 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
169 177 self.preprocessors.append(SphinxTransformer(config=config))
170 178 self.preprocessors.append(LatexTransformer(config=config))
171 179
172 180 ##
173 181 self.env.filters['filter_data_type'] = FilterDataType(config=config)
174 182 self.env.filters['pycomment'] = python_comment
175 183 self.env.filters['indent'] = indent
176 184 self.env.filters['rm_fake'] = rm_fake
177 185 self.env.filters['rm_ansi'] = remove_ansi
178 186 self.env.filters['markdown'] = markdown
179 187 self.env.filters['highlight'] = highlight
180 188 self.env.filters['highlight2latex'] = highlight2latex
181 189 self.env.filters['ansi2html'] = ansi2html
182 190 self.env.filters['markdown2latex'] = markdown2latex
183 191 self.env.filters['markdown2rst'] = markdown2rst
184 192 self.env.filters['get_lines'] = get_lines
185 193 self.env.filters['wrap'] = wrap
186 194
187 195 ## user filter will overwrite
188 196 for key, filtr in jinja_filters.iteritems():
189 197 if isinstance(filtr, MetaHasTraits):
190 198 self.env.filters[key] = filtr(config=config)
191 199 else :
192 200 self.env.filters[key] = filtr
193 201
194 202 self.template = self.env.get_template(self.template_file+self.ext)
195 203
196 204
197 205 def process(self, nb):
198 206 """
199 207 preprocess the notebook json for easier use with the templates.
200 208 will call all the `preprocessor`s in order before returning it.
201 209 """
202 210
203 211 # dict of 'resources' that could be made by the preprocessors
204 212 # like key/value data to extract files from ipynb like in latex conversion
205 213 resources = {}
206 214
207 215 for preprocessor in self.preprocessors:
208 216 nb, resources = preprocessor(nb, resources)
209 217
210 218 return nb, resources
211 219
212 220 def convert(self, nb):
213 221 """ convert the ipynb file
214 222
215 223 return both the converted ipynb file and a dict containing potential
216 224 other resources
217 225 """
218 226 nb, resources = self.process(nb)
219 227 return self.template.render(nb=nb, resources=resources), resources
220 228
221 229
222 230 def from_filename(self, filename):
223 231 """read and convert a notebook from a file name"""
224 232 with io.open(filename) as f:
225 233 return self.convert(nbformat.read(f, 'json'))
226 234
227 235 def from_file(self, filelike):
228 236 """read and convert a notebook from a filelike object
229 237
230 238 filelike object will just be "read" and should be json format..
231 239 """
232 240 return self.convert(nbformat.read(filelike, 'json'))
233 241
234 242 def from_json(self, json):
235 243 """ not implemented
236 244
237 245 Should convert from a json object
238 246 """
239 247 raise NotImplementedError('not implemented (yet?)')
@@ -1,269 +1,269 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 from __future__ import print_function
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 from converters.config import GlobalConfigurable
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, 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 raise NotImplementedError('should be implemented by subclass')
61 61 return cell, other
62 62
63 63
64 64 class ActivatableTransformer(ConfigurableTransformers):
65 65 """A simple ConfigurableTransformers that have an enabled flag
66 66
67 67 Inherit from that if you just want to have a transformer which is
68 68 no-op by default but can be activated in profiles with
69 69
70 70 c.YourTransformerName.enabled = True
71 71 """
72 72
73 73 enabled = Bool(False, config=True)
74 74
75 75 def __call__(self, nb, other):
76 76 if not self.enabled :
77 77 return nb, other
78 78 else :
79 79 return super(ActivatableTransformer, self).__call__(nb, other)
80 80
81 81
82 82 def cell_preprocessor(function):
83 83 """ wrap a function to be executed on all cells of a notebook
84 84
85 85 wrapped function parameters :
86 86 cell : the cell
87 87 other : external resources
88 88 index : index of the cell
89 89 """
90 90 def wrappedfunc(nb, other):
91 91 for worksheet in nb.worksheets :
92 92 for index, cell in enumerate(worksheet.cells):
93 93 worksheet.cells[index], other = function(cell, other, index)
94 94 return nb, other
95 95 return wrappedfunc
96 96
97 97
98 98 @cell_preprocessor
99 99 def haspyout_transformer(cell, other, count):
100 100 """
101 101 Add a haspyout flag to cell that have it
102 102
103 103 Easier for templating, where you can't know in advance
104 104 wether to write the out prompt
105 105
106 106 """
107 107 cell.type = cell.cell_type
108 108 cell.haspyout = False
109 109 for out in cell.get('outputs', []):
110 110 if out.output_type == 'pyout':
111 111 cell.haspyout = True
112 112 break
113 113 return cell, other
114 114
115 115 @cell_preprocessor
116 116 def coalesce_streams(cell, other, count):
117 117 """merge consecutive sequences of stream output into single stream
118 118
119 119 to prevent extra newlines inserted at flush calls
120 120
121 121 TODO: handle \r deletion
122 122 """
123 123 outputs = cell.get('outputs', [])
124 124 if not outputs:
125 125 return cell, other
126 126 new_outputs = []
127 127 last = outputs[0]
128 128 new_outputs = [last]
129 129 for output in outputs[1:]:
130 130 if (output.output_type == 'stream' and
131 131 last.output_type == 'stream' and
132 132 last.stream == output.stream
133 133 ):
134 134 last.text += output.text
135 135 else:
136 136 new_outputs.append(output)
137 137
138 138 cell.outputs = new_outputs
139 139 return cell, other
140 140
141 141 class ExtractFigureTransformer(ActivatableTransformer):
142 142
143 143
144 144 extra_ext_map = Dict({},
145 145 config=True,
146 146 help="""extra map to override extension based on type.
147 147 Usefull for latex where svg will be converted to pdf before inclusion
148 148 """
149 149 )
150 150
151 151 key_format_map = Dict({},
152 152 config=True,
153 153 )
154 154
155 155 figname_format_map = Dict({},
156 156 config=True,
157 157 )
158 158
159 159
160 160 #to do change this to .format {} syntax
161 161 default_key_tpl = Unicode('_fig_{count:02d}.{ext}', config=True)
162 162
163 163 def _get_ext(self, ext):
164 164 if ext in self.extra_ext_map :
165 165 return self.extra_ext_map[ext]
166 166 return ext
167 167
168 168 def _new_figure(self, data, fmt, count):
169 169 """Create a new figure file in the given format.
170 170
171 171 """
172 172 tplf = self.figname_format_map.get(fmt, self.default_key_tpl)
173 173 tplk = self.key_format_map.get(fmt, self.default_key_tpl)
174 174
175 175 # option to pass the hash as data ?
176 176 figname = tplf.format(count=count, ext=self._get_ext(fmt))
177 177 key = tplk.format(count=count, ext=self._get_ext(fmt))
178 178
179 179 # Binary files are base64-encoded, SVG is already XML
180 180 if fmt in ('png', 'jpg', 'pdf'):
181 181 data = data.decode('base64')
182 182
183 183 return figname, key, data
184 184
185 185
186 186 def cell_transform(self, cell, other, count):
187 187 if other.get('figures', None) is None :
188 188 other['figures'] = {}
189 189 for out in cell.get('outputs', []):
190 190 for out_type in self.display_data_priority:
191 191 if out.hasattr(out_type):
192 192 figname, key, data = self._new_figure(out[out_type], out_type, count)
193 193 out['key_'+out_type] = figname
194 194 other['figures'][key] = data
195 195 count = count+1
196 196 return cell, other
197 197
198 198
199 199 class RevealHelpTransformer(ConfigurableTransformers):
200 200
201 201 def __call__(self, nb, other):
202 202 for worksheet in nb.worksheets :
203 203 for i, cell in enumerate(worksheet.cells):
204 204 cell.metadata.slide_type = cell.metadata.get('slideshow', {}).get('slide_type', None)
205 205 if cell.metadata.slide_type is None:
206 206 cell.metadata.slide_type = '-'
207 207 if cell.metadata.slide_type in ['slide']:
208 208 worksheet.cells[i - 1].metadata.slide_helper = 'slide_end'
209 209 if cell.metadata.slide_type in ['subslide']:
210 210 worksheet.cells[i - 1].metadata.slide_helper = 'subslide_end'
211 211 return nb, other
212 212
213 213
214 214 class CSSHtmlHeaderTransformer(ActivatableTransformer):
215 215
216 216 def __call__(self, nb, resources):
217 217 """Fetch and add css to the resource dict
218 218
219 219 Fetch css from IPython adn Pygment to add at the beginning
220 220 of the html files.
221 221
222 222 Add this css in resources in the "inlining.css" key
223 223 """
224 224 resources['inlining'] = {}
225 225 resources['inlining']['css'] = self.header
226 226 return nb, resources
227 227
228 228 header = []
229 229
230 230 def __init__(self, config=None, **kw):
231 231 super(CSSHtmlHeaderTransformer, self).__init__(config=config, **kw)
232 232 if self.enabled :
233 233 self.regen_header()
234 234
235 235 def regen_header(self):
236 236 ## lazy load asa this might not be use in many transformers
237 237 import os
238 238 from IPython.utils import path
239 239 import io
240 240 from pygments.formatters import HtmlFormatter
241 241 header = []
242 242 static = os.path.join(path.get_ipython_package_dir(),
243 243 'frontend', 'html', 'notebook', 'static',
244 244 )
245 245 here = os.path.split(os.path.realpath(__file__))[0]
246 246 css = os.path.join(static, 'css')
247 247 for sheet in [
248 248 # do we need jquery and prettify?
249 249 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
250 250 # 'jquery-ui.min.css'),
251 251 # os.path.join(static, 'prettify', 'prettify.css'),
252 252 os.path.join(css, 'boilerplate.css'),
253 253 os.path.join(css, 'fbm.css'),
254 254 os.path.join(css, 'notebook.css'),
255 255 os.path.join(css, 'renderedhtml.css'),
256 256 os.path.join(css, 'style.min.css'),
257 257 ]:
258 258 try:
259 259 with io.open(sheet, encoding='utf-8') as f:
260 260 s = f.read()
261 261 header.append(s)
262 262 except IOError:
263 263 # new version of ipython with style.min.css, pass
264 264 pass
265 265
266 266 pygments_css = HtmlFormatter().get_style_defs('.highlight')
267 267 header.append(pygments_css)
268 268 self.header = header
269 269
@@ -1,132 +1,132 b''
1 1 {%- extends 'display_priority.tpl' -%}
2 2
3 3
4 4
5 5 {% block codecell %}
6 6 <div class="cell border-box-sizing code_cell vbox">
7 7 {{ super() }}</div>
8 8 {%- endblock codecell %}
9 9
10 10 {% block input_group -%}
11 11 <div class="input hbox">
12 12 {{super()}}
13 13 </div>
14 14 {% endblock input_group %}
15 15
16 16 {% block output_group -%}
17 17 <div class="vbox output_wrapper">
18 18 <div class="output vbox">
19 <div class="hbox output_area">
19 <div class="vbox output_area">
20 20 {{ super() }}
21 21 </div>
22 22 </div>
23 23 </div>
24 24 {% endblock output_group %}
25 25
26 26
27 27 {% block in_prompt -%}
28 28 <div class="prompt input_prompt">In&nbsp;[{{cell.prompt_number}}]:</div>
29 29 {%- endblock in_prompt %}
30 30
31 31 {% block output_prompt -%}
32 32 <div class="prompt output_prompt">
33 33 {%- if cell.haspyout -%}
34 34 Out[{{cell.prompt_number}}]:
35 35 {%- endif -%}
36 36 </div>
37 37 {% endblock output_prompt %}
38 38
39 39 {% block input %}
40 40 <div class="input_area box-flex1">
41 41 {{cell.input | highlight }}
42 42 </div>
43 43 {%- endblock input %}
44 44
45 45
46 46 {% block markdowncell scoped %}
47 47 <div class="text_cell_render border-box-sizing rendered_html">
48 48 {{ cell.source | markdown| rm_fake}}
49 49 </div>
50 50 {%- endblock markdowncell %}
51 51
52 52 {% block headingcell scoped %}
53 53 <div class="text_cell_render border-box-sizing rendered_html">
54 54 <h{{cell.level}}>
55 55 {{cell.source}}
56 56 </h{{cell.level}}>
57 57 </div>
58 58 {% endblock headingcell %}
59 59
60 60 {% block rawcell scoped %}
61 61 {{ cell.source }}
62 62 {% endblock rawcell %}
63 63
64 64 {% block unknowncell scoped %}
65 65 unknown type {{cell.type}}
66 66 {% endblock unknowncell %}
67 67
68 68
69 69 {% block pyout -%}
70 70 <div class="box-flex1 output_subarea output_pyout">
71 71 {% block data_priority scoped %}{{ super()}}{% endblock %}
72 72 </div>
73 73 {%- endblock pyout %}
74 74
75 75 {% block stream_stdout -%}
76 76 <div class="box-flex1 output_subarea output_stream output_stdout">
77 77 <pre>{{output.text |ansi2html}}</pre>
78 78 </div>
79 79 {%- endblock stream_stdout %}
80 80
81 81 {% block stream_stderr -%}
82 82 <div class="box-flex1 output_subarea output_stream output_stderr">
83 83 <pre>{{output.text |ansi2html}}</pre>
84 84 </div>
85 85 {%- endblock stream_stderr %}
86 86
87 87 {% block data_svg -%}
88 88 {{output.svg}}
89 89 {%- endblock data_svg %}
90 90
91 91
92 92 {% block data_html -%}
93 93 <div class="output_html rendered_html">
94 94 {{output.html}}
95 95 </div>
96 96 {%- endblock data_html %}
97 97
98 98 {% block data_png %}
99 99 <img src="data:image/png;base64,{{output.png}}"></img>
100 100 {%- endblock data_png %}
101 101
102 102
103 103 {% block data_jpg %}
104 104 <img src="data:image/jpeg;base64,{{output.jpeg}}"></img>
105 105 {%- endblock data_jpg %}
106 106
107 107
108 108 {% block data_latex %}
109 109 {{output.latex}}
110 110 {%- endblock data_latex %}
111 111
112 112 {% block pyerr -%}
113 113 <div class="box-flex1 output_subarea output_pyerr">
114 114 <pre>{{super()}}</pre>
115 115 </div>
116 116 {%- endblock pyerr %}
117 117
118 118 {%- block traceback_line %}
119 119 {{line| ansi2html}}
120 120 {%- endblock traceback_line %}
121 121
122 122
123 123 {%- block data_text %}
124 124 <pre>{{output.text | ansi2html}}</pre>
125 125 {%- endblock -%}
126 126
127 127
128 128 {%- block display_data scoped -%}
129 129 <div class="box-flex1 output_subarea output_display_data">
130 130 {{super()}}
131 131 </div>
132 {%- endblock display_data -%} No newline at end of file
132 {%- endblock display_data -%}
General Comments 0
You need to be logged in to leave comments. Login now