##// END OF EJS Templates
Merge pull request #5272 from minrk/highlight-language-metadata...
Min RK -
r15681:9cea9c07 merge
parent child Browse files
Show More
@@ -1,275 +1,278 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 = None
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.HighlightMagicsPreprocessor'],
74 74 config=True,
75 75 help="""List of preprocessors available by default, by name, namespace,
76 76 instance, or type.""")
77 77
78 78
79 79 def __init__(self, config=None, **kw):
80 80 """
81 81 Public constructor
82 82
83 83 Parameters
84 84 ----------
85 85 config : config
86 86 User configuration instance.
87 87 """
88 88 with_default_config = self.default_config
89 89 if config:
90 90 with_default_config.merge(config)
91 91
92 92 super(Exporter, self).__init__(config=with_default_config, **kw)
93 93
94 94 self._init_preprocessors()
95 95
96 96
97 97 @property
98 98 def default_config(self):
99 99 return Config()
100 100
101 101 @nbformat.docstring_nbformat_mod
102 102 def from_notebook_node(self, nb, resources=None, **kw):
103 103 """
104 104 Convert a notebook from a notebook node instance.
105 105
106 106 Parameters
107 107 ----------
108 108 nb : :class:`~{nbformat_mod}.nbbase.NotebookNode`
109 109 Notebook node
110 110 resources : dict
111 111 Additional resources that can be accessed read/write by
112 112 preprocessors and filters.
113 113 **kw
114 114 Ignored (?)
115 115 """
116 116 nb_copy = copy.deepcopy(nb)
117 117 resources = self._init_resources(resources)
118
119 if 'language' in nb['metadata']:
120 resources['language'] = nb['metadata']['language'].lower()
118 121
119 122 # Preprocess
120 123 nb_copy, resources = self._preprocess(nb_copy, resources)
121 124
122 125 return nb_copy, resources
123 126
124 127
125 128 def from_filename(self, filename, resources=None, **kw):
126 129 """
127 130 Convert a notebook from a notebook file.
128 131
129 132 Parameters
130 133 ----------
131 134 filename : str
132 135 Full filename of the notebook file to open and convert.
133 136 """
134 137
135 138 # Pull the metadata from the filesystem.
136 139 if resources is None:
137 140 resources = ResourcesDict()
138 141 if not 'metadata' in resources or resources['metadata'] == '':
139 142 resources['metadata'] = ResourcesDict()
140 143 basename = os.path.basename(filename)
141 144 notebook_name = basename[:basename.rfind('.')]
142 145 resources['metadata']['name'] = notebook_name
143 146
144 147 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
145 148 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
146 149
147 150 with io.open(filename, encoding='utf-8') as f:
148 151 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
149 152
150 153
151 154 def from_file(self, file_stream, resources=None, **kw):
152 155 """
153 156 Convert a notebook from a notebook file.
154 157
155 158 Parameters
156 159 ----------
157 160 file_stream : file-like object
158 161 Notebook file-like object to convert.
159 162 """
160 163 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
161 164
162 165
163 166 def register_preprocessor(self, preprocessor, enabled=False):
164 167 """
165 168 Register a preprocessor.
166 169 Preprocessors are classes that act upon the notebook before it is
167 170 passed into the Jinja templating engine. preprocessors are also
168 171 capable of passing additional information to the Jinja
169 172 templating engine.
170 173
171 174 Parameters
172 175 ----------
173 176 preprocessor : preprocessor
174 177 """
175 178 if preprocessor is None:
176 179 raise TypeError('preprocessor')
177 180 isclass = isinstance(preprocessor, type)
178 181 constructed = not isclass
179 182
180 183 # Handle preprocessor's registration based on it's type
181 184 if constructed and isinstance(preprocessor, py3compat.string_types):
182 185 # Preprocessor is a string, import the namespace and recursively call
183 186 # this register_preprocessor method
184 187 preprocessor_cls = import_item(preprocessor)
185 188 return self.register_preprocessor(preprocessor_cls, enabled)
186 189
187 190 if constructed and hasattr(preprocessor, '__call__'):
188 191 # Preprocessor is a function, no need to construct it.
189 192 # Register and return the preprocessor.
190 193 if enabled:
191 194 preprocessor.enabled = True
192 195 self._preprocessors.append(preprocessor)
193 196 return preprocessor
194 197
195 198 elif isclass and isinstance(preprocessor, MetaHasTraits):
196 199 # Preprocessor is configurable. Make sure to pass in new default for
197 200 # the enabled flag if one was specified.
198 201 self.register_preprocessor(preprocessor(parent=self), enabled)
199 202
200 203 elif isclass:
201 204 # Preprocessor is not configurable, construct it
202 205 self.register_preprocessor(preprocessor(), enabled)
203 206
204 207 else:
205 208 # Preprocessor is an instance of something without a __call__
206 209 # attribute.
207 210 raise TypeError('preprocessor')
208 211
209 212
210 213 def _init_preprocessors(self):
211 214 """
212 215 Register all of the preprocessors needed for this exporter, disabled
213 216 unless specified explicitly.
214 217 """
215 218 if self._preprocessors is None:
216 219 self._preprocessors = []
217 220
218 221 #Load default preprocessors (not necessarly enabled by default).
219 222 if self.default_preprocessors:
220 223 for preprocessor in self.default_preprocessors:
221 224 self.register_preprocessor(preprocessor)
222 225
223 226 #Load user preprocessors. Enable by default.
224 227 if self.preprocessors:
225 228 for preprocessor in self.preprocessors:
226 229 self.register_preprocessor(preprocessor, enabled=True)
227 230
228 231
229 232 def _init_resources(self, resources):
230 233
231 234 #Make sure the resources dict is of ResourcesDict type.
232 235 if resources is None:
233 236 resources = ResourcesDict()
234 237 if not isinstance(resources, ResourcesDict):
235 238 new_resources = ResourcesDict()
236 239 new_resources.update(resources)
237 240 resources = new_resources
238 241
239 242 #Make sure the metadata extension exists in resources
240 243 if 'metadata' in resources:
241 244 if not isinstance(resources['metadata'], ResourcesDict):
242 245 resources['metadata'] = ResourcesDict(resources['metadata'])
243 246 else:
244 247 resources['metadata'] = ResourcesDict()
245 248 if not resources['metadata']['name']:
246 249 resources['metadata']['name'] = 'Notebook'
247 250
248 251 #Set the output extension
249 252 resources['output_extension'] = self.file_extension
250 253 return resources
251 254
252 255
253 256 def _preprocess(self, nb, resources):
254 257 """
255 258 Preprocess the notebook before passing it into the Jinja engine.
256 259 To preprocess the notebook is to apply all of the
257 260
258 261 Parameters
259 262 ----------
260 263 nb : notebook node
261 264 notebook that is being exported.
262 265 resources : a dict of additional resources that
263 266 can be accessed read/write by preprocessors
264 267 """
265 268
266 269 # Do a copy.deepcopy first,
267 270 # we are never safe enough with what the preprocessors could do.
268 271 nbc = copy.deepcopy(nb)
269 272 resc = copy.deepcopy(resources)
270 273
271 274 #Run each preprocessor on the notebook. Carry the output along
272 275 #to each preprocessor
273 276 for preprocessor in self._preprocessors:
274 277 nbc, resc = preprocessor(nbc, resc)
275 278 return nbc, resc
@@ -1,189 +1,189 b''
1 1 {%- extends 'display_priority.tpl' -%}
2 2
3 3
4 4 {% block codecell %}
5 5 <div class="cell border-box-sizing code_cell">
6 6 {{ super() }}
7 7 </div>
8 8 {%- endblock codecell %}
9 9
10 10 {% block input_group -%}
11 11 <div class="input">
12 12 {{ super() }}
13 13 </div>
14 14 {% endblock input_group %}
15 15
16 16 {% block output_group %}
17 17 <div class="output_wrapper">
18 18 <div class="output">
19 19 {{ super() }}
20 20 </div>
21 21 </div>
22 22 {% endblock output_group %}
23 23
24 24 {% block in_prompt -%}
25 25 <div class="prompt input_prompt">
26 26 In&nbsp;[{{ cell.prompt_number }}]:
27 27 </div>
28 28 {%- endblock in_prompt %}
29 29
30 30 {% block empty_in_prompt -%}
31 31 <div class="prompt input_prompt">
32 32 </div>
33 33 {%- endblock empty_in_prompt %}
34 34
35 35 {#
36 36 output_prompt doesn't do anything in HTML,
37 37 because there is a prompt div in each output area (see output block)
38 38 #}
39 39 {% block output_prompt %}
40 40 {% endblock output_prompt %}
41 41
42 42 {% block input %}
43 43 <div class="input_area box-flex1">
44 {{ cell.input | highlight2html(metadata=cell.metadata) }}
44 {{ cell.input | highlight2html(language=resources.get('language'), metadata=cell.metadata) }}
45 45 </div>
46 46 {%- endblock input %}
47 47
48 48 {% block output %}
49 49 <div class="output_area">
50 50 {%- if output.output_type == 'pyout' -%}
51 51 <div class="prompt output_prompt">
52 52 Out[{{ cell.prompt_number }}]:
53 53 {%- else -%}
54 54 <div class="prompt">
55 55 {%- endif -%}
56 56 </div>
57 57 {{ super() }}
58 58 </div>
59 59 {% endblock output %}
60 60
61 61 {% block markdowncell scoped %}
62 62 <div class="cell border-box-sizing text_cell">
63 63 <div class="input">
64 64 {{ self.empty_in_prompt() }}
65 65 <div class="inner_cell">
66 66 <div class="text_cell_render border-box-sizing rendered_html">
67 67 {{ cell.source | markdown2html | strip_files_prefix }}
68 68 </div>
69 69 </div>
70 70 </div>
71 71 </div>
72 72 {%- endblock markdowncell %}
73 73
74 74 {% block headingcell scoped %}
75 75 <div class="cell border-box-sizing text_cell">
76 76 <div class="input">
77 77 {{ self.empty_in_prompt() }}
78 78 <div class="inner_cell">
79 79 <div class="text_cell_render border-box-sizing rendered_html">
80 80 {{ ("#" * cell.level + cell.source) | replace('\n', ' ') | markdown2html | strip_files_prefix | add_anchor }}
81 81 </div>
82 82 </div>
83 83 </div>
84 84 </div>
85 85 {% endblock headingcell %}
86 86
87 87 {% block unknowncell scoped %}
88 88 unknown type {{ cell.type }}
89 89 {% endblock unknowncell %}
90 90
91 91 {% block pyout -%}
92 92 <div class="box-flex1 output_subarea output_pyout">
93 93 {% block data_priority scoped %}
94 94 {{ super() }}
95 95 {% endblock %}
96 96 </div>
97 97 {%- endblock pyout %}
98 98
99 99 {% block stream_stdout -%}
100 100 <div class="box-flex1 output_subarea output_stream output_stdout">
101 101 <pre>
102 102 {{ output.text | ansi2html }}
103 103 </pre>
104 104 </div>
105 105 {%- endblock stream_stdout %}
106 106
107 107 {% block stream_stderr -%}
108 108 <div class="box-flex1 output_subarea output_stream output_stderr">
109 109 <pre>
110 110 {{ output.text | ansi2html }}
111 111 </pre>
112 112 </div>
113 113 {%- endblock stream_stderr %}
114 114
115 115 {% block data_svg -%}
116 116 {%- if output.svg_filename %}
117 117 <img src="{{output.svg_filename | posix_path}}"
118 118 {%- else %}
119 119 {{ output.svg }}
120 120 {%- endif %}
121 121 {%- endblock data_svg %}
122 122
123 123 {% block data_html -%}
124 124 <div class="output_html rendered_html">
125 125 {{ output.html }}
126 126 </div>
127 127 {%- endblock data_html %}
128 128
129 129 {% block data_png %}
130 130 {%- if output.png_filename %}
131 131 <img src="{{output.png_filename | posix_path}}"
132 132 {%- else %}
133 133 <img src="data:image/png;base64,{{ output.png }}"
134 134 {%- endif %}
135 135 {%- if 'metadata' in output and 'width' in output.metadata.get('png', {}) %}
136 136 width={{output.metadata['png']['width']}}
137 137 {%- endif %}
138 138 {%- if 'metadata' in output and 'height' in output.metadata.get('png', {}) %}
139 139 height={{output.metadata['png']['height']}}
140 140 {%- endif %}
141 141 >
142 142 {%- endblock data_png %}
143 143
144 144 {% block data_jpg %}
145 145 {%- if output.jpeg_filename %}
146 146 <img src="{{output.jpeg_filename | posix_path}}"
147 147 {%- else %}
148 148 <img src="data:image/jpeg;base64,{{ output.jpeg }}"
149 149 {%- endif %}
150 150 {%- if 'metadata' in output and 'width' in output.metadata.get('jpeg', {}) %}
151 151 width={{output.metadata['jpeg']['width']}}
152 152 {%- endif %}
153 153 {%- if 'metadata' in output and 'height' in output.metadata.get('jpeg', {}) %}
154 154 height={{output.metadata['jpeg']['height']}}
155 155 {%- endif %}
156 156 >
157 157 {%- endblock data_jpg %}
158 158
159 159 {% block data_latex %}
160 160 {{ output.latex }}
161 161 {%- endblock data_latex %}
162 162
163 163 {% block pyerr -%}
164 164 <div class="box-flex1 output_subarea output_pyerr">
165 165 <pre>{{ super() }}</pre>
166 166 </div>
167 167 {%- endblock pyerr %}
168 168
169 169 {%- block traceback_line %}
170 170 {{ line | ansi2html }}
171 171 {%- endblock traceback_line %}
172 172
173 173 {%- block data_text %}
174 174 <pre>
175 175 {{ output.text | ansi2html }}
176 176 </pre>
177 177 {%- endblock -%}
178 178
179 179 {%- block data_javascript %}
180 180 <script type="text/javascript">
181 181 {{ output.javascript }}
182 182 </script>
183 183 {%- endblock -%}
184 184
185 185 {%- block display_data scoped -%}
186 186 <div class="box-flex1 output_subarea output_display_data">
187 187 {{ super() }}
188 188 </div>
189 189 {%- endblock display_data -%}
@@ -1,21 +1,21 b''
1 1 ((= Python input/output style =))
2 2
3 3 ((*- extends 'base.tplx' -*))
4 4
5 5 % Custom definitions
6 6 ((* block definitions *))
7 7 ((( super() )))
8 8
9 9 % Pygments definitions
10 10 ((( resources.latex.pygments_definitions )))
11 11 ((* endblock definitions *))
12 12
13 13 %===============================================================================
14 14 % Input
15 15 %===============================================================================
16 16
17 17 ((* block input scoped *))
18 18 \begin{Verbatim}[commandchars=\\\{\}]
19 ((( cell.input | highlight2latex(strip_verbatim=True) | add_prompts )))
19 ((( cell.input | highlight2latex(language=resources.get('language'), strip_verbatim=True) | add_prompts )))
20 20 \end{Verbatim}
21 21 ((* endblock input *))
General Comments 0
You need to be logged in to leave comments. Login now