##// END OF EJS Templates
Inline user custom CSS if it's defined.
Jonathan Frederic -
Show More
@@ -1,311 +1,312 b''
1 1 #!/usr/bin/env python
2 2 """NbConvert is a utility for conversion of .ipynb files.
3 3
4 4 Command-line interface for the NbConvert conversion utility.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 from __future__ import print_function
11 11
12 12 import logging
13 13 import sys
14 14 import os
15 15 import glob
16 16
17 17 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
18 18 from IPython.core.profiledir import ProfileDir
19 19 from IPython.config import catch_config_error, Configurable
20 20 from IPython.utils.traitlets import (
21 21 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
22 22 )
23 23 from IPython.utils.importstring import import_item
24 24
25 25 from .exporters.export import get_export_names, exporter_map
26 26 from IPython.nbconvert import exporters, preprocessors, writers, postprocessors
27 27 from .utils.base import NbConvertBase
28 28 from .utils.exceptions import ConversionException
29 29
30 30 #-----------------------------------------------------------------------------
31 31 #Classes and functions
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class DottedOrNone(DottedObjectName):
35 35 """
36 36 A string holding a valid dotted object name in Python, such as A.b3._c
37 37 Also allows for None type."""
38 38
39 39 default_value = u''
40 40
41 41 def validate(self, obj, value):
42 42 if value is not None and len(value) > 0:
43 43 return super(DottedOrNone, self).validate(obj, value)
44 44 else:
45 45 return value
46 46
47 47 nbconvert_aliases = {}
48 48 nbconvert_aliases.update(base_aliases)
49 49 nbconvert_aliases.update({
50 50 'to' : 'NbConvertApp.export_format',
51 51 'template' : 'TemplateExporter.template_file',
52 52 'writer' : 'NbConvertApp.writer_class',
53 53 'post': 'NbConvertApp.postprocessor_class',
54 54 'output': 'NbConvertApp.output_base',
55 55 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
56 56 })
57 57
58 58 nbconvert_flags = {}
59 59 nbconvert_flags.update(base_flags)
60 60 nbconvert_flags.update({
61 61 'stdout' : (
62 62 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
63 63 "Write notebook output to stdout instead of files."
64 64 )
65 65 })
66 66
67 67
68 68 class NbConvertApp(BaseIPythonApplication):
69 69 """Application used to convert from notebook file type (``*.ipynb``)"""
70 70
71 71 name = 'ipython-nbconvert'
72 72 aliases = nbconvert_aliases
73 73 flags = nbconvert_flags
74 74
75 75 def _log_level_default(self):
76 76 return logging.INFO
77 77
78 78 def _classes_default(self):
79 79 classes = [NbConvertBase, ProfileDir]
80 80 for pkg in (exporters, preprocessors, writers, postprocessors):
81 81 for name in dir(pkg):
82 82 cls = getattr(pkg, name)
83 83 if isinstance(cls, type) and issubclass(cls, Configurable):
84 84 classes.append(cls)
85 85
86 86 return classes
87 87
88 88 description = Unicode(
89 89 u"""This application is used to convert notebook files (*.ipynb)
90 90 to various other formats.
91 91
92 92 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
93 93
94 94 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
95 95 can only be use when converting one notebook at a time.
96 96 ''')
97 97
98 98 examples = Unicode(u"""
99 99 The simplest way to use nbconvert is
100 100
101 101 > ipython nbconvert mynotebook.ipynb
102 102
103 103 which will convert mynotebook.ipynb to the default format (probably HTML).
104 104
105 105 You can specify the export format with `--to`.
106 106 Options include {0}
107 107
108 108 > ipython nbconvert --to latex mynotebook.ipynb
109 109
110 110 Both HTML and LaTeX support multiple output templates. LaTeX includes
111 111 'base', 'article' and 'report'. HTML includes 'basic' and 'full'. You
112 112 can specify the flavor of the format used.
113 113
114 114 > ipython nbconvert --to html --template basic mynotebook.ipynb
115 115
116 116 You can also pipe the output to stdout, rather than a file
117 117
118 118 > ipython nbconvert mynotebook.ipynb --stdout
119 119
120 120 PDF is generated via latex
121 121
122 122 > ipython nbconvert mynotebook.ipynb --to pdf
123 123
124 124 You can get (and serve) a Reveal.js-powered slideshow
125 125
126 126 > ipython nbconvert myslides.ipynb --to slides --post serve
127 127
128 128 Multiple notebooks can be given at the command line in a couple of
129 129 different ways:
130 130
131 131 > ipython nbconvert notebook*.ipynb
132 132 > ipython nbconvert notebook1.ipynb notebook2.ipynb
133 133
134 134 or you can specify the notebooks list in a config file, containing::
135 135
136 136 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
137 137
138 138 > ipython nbconvert --config mycfg.py
139 139 """.format(get_export_names()))
140 140
141 141 # Writer specific variables
142 142 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
143 143 help="""Instance of the writer class used to write the
144 144 results of the conversion.""")
145 145 writer_class = DottedObjectName('FilesWriter', config=True,
146 146 help="""Writer class used to write the
147 147 results of the conversion""")
148 148 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
149 149 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
150 150 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
151 151 writer_factory = Type()
152 152
153 153 def _writer_class_changed(self, name, old, new):
154 154 if new.lower() in self.writer_aliases:
155 155 new = self.writer_aliases[new.lower()]
156 156 self.writer_factory = import_item(new)
157 157
158 158 # Post-processor specific variables
159 159 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
160 160 help="""Instance of the PostProcessor class used to write the
161 161 results of the conversion.""")
162 162
163 163 postprocessor_class = DottedOrNone(config=True,
164 164 help="""PostProcessor class used to write the
165 165 results of the conversion""")
166 166 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
167 167 postprocessor_factory = Type()
168 168
169 169 def _postprocessor_class_changed(self, name, old, new):
170 170 if new.lower() in self.postprocessor_aliases:
171 171 new = self.postprocessor_aliases[new.lower()]
172 172 if new:
173 173 self.postprocessor_factory = import_item(new)
174 174
175 175
176 176 # Other configurable variables
177 177 export_format = CaselessStrEnum(get_export_names(),
178 178 default_value="html",
179 179 config=True,
180 180 help="""The export format to be used."""
181 181 )
182 182
183 183 notebooks = List([], config=True, help="""List of notebooks to convert.
184 184 Wildcards are supported.
185 185 Filenames passed positionally will be added to the list.
186 186 """)
187 187
188 188 @catch_config_error
189 189 def initialize(self, argv=None):
190 190 self.init_syspath()
191 191 super(NbConvertApp, self).initialize(argv)
192 192 self.init_notebooks()
193 193 self.init_writer()
194 194 self.init_postprocessor()
195 195
196 196
197 197
198 198 def init_syspath(self):
199 199 """
200 200 Add the cwd to the sys.path ($PYTHONPATH)
201 201 """
202 202 sys.path.insert(0, os.getcwd())
203 203
204 204
205 205 def init_notebooks(self):
206 206 """Construct the list of notebooks.
207 207 If notebooks are passed on the command-line,
208 208 they override notebooks specified in config files.
209 209 Glob each notebook to replace notebook patterns with filenames.
210 210 """
211 211
212 212 # Specifying notebooks on the command-line overrides (rather than adds)
213 213 # the notebook list
214 214 if self.extra_args:
215 215 patterns = self.extra_args
216 216 else:
217 217 patterns = self.notebooks
218 218
219 219 # Use glob to replace all the notebook patterns with filenames.
220 220 filenames = []
221 221 for pattern in patterns:
222 222
223 223 # Use glob to find matching filenames. Allow the user to convert
224 224 # notebooks without having to type the extension.
225 225 globbed_files = glob.glob(pattern)
226 226 globbed_files.extend(glob.glob(pattern + '.ipynb'))
227 227 if not globbed_files:
228 228 self.log.warn("pattern %r matched no files", pattern)
229 229
230 230 for filename in globbed_files:
231 231 if not filename in filenames:
232 232 filenames.append(filename)
233 233 self.notebooks = filenames
234 234
235 235 def init_writer(self):
236 236 """
237 237 Initialize the writer (which is stateless)
238 238 """
239 239 self._writer_class_changed(None, self.writer_class, self.writer_class)
240 240 self.writer = self.writer_factory(parent=self)
241 241
242 242 def init_postprocessor(self):
243 243 """
244 244 Initialize the postprocessor (which is stateless)
245 245 """
246 246 self._postprocessor_class_changed(None, self.postprocessor_class,
247 247 self.postprocessor_class)
248 248 if self.postprocessor_factory:
249 249 self.postprocessor = self.postprocessor_factory(parent=self)
250 250
251 251 def start(self):
252 252 """
253 253 Ran after initialization completed
254 254 """
255 255 super(NbConvertApp, self).start()
256 256 self.convert_notebooks()
257 257
258 258 def convert_notebooks(self):
259 259 """
260 260 Convert the notebooks in the self.notebook traitlet
261 261 """
262 262 # Export each notebook
263 263 conversion_success = 0
264 264
265 265 if self.output_base != '' and len(self.notebooks) > 1:
266 266 self.log.error(
267 267 """UsageError: --output flag or `NbConvertApp.output_base` config option
268 268 cannot be used when converting multiple notebooks.
269 269 """)
270 270 self.exit(1)
271 271
272 272 exporter = exporter_map[self.export_format](config=self.config)
273 273
274 274 for notebook_filename in self.notebooks:
275 275 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
276 276
277 277 # Get a unique key for the notebook and set it in the resources object.
278 278 basename = os.path.basename(notebook_filename)
279 279 notebook_name = basename[:basename.rfind('.')]
280 280 if self.output_base:
281 281 notebook_name = self.output_base
282 282 resources = {}
283 resources['profile_dir'] = self.profile_dir.location
283 284 resources['unique_key'] = notebook_name
284 285 resources['output_files_dir'] = '%s_files' % notebook_name
285 286 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
286 287
287 288 # Try to export
288 289 try:
289 290 output, resources = exporter.from_filename(notebook_filename, resources=resources)
290 291 except ConversionException as e:
291 292 self.log.error("Error while converting '%s'", notebook_filename,
292 293 exc_info=True)
293 294 self.exit(1)
294 295 else:
295 296 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
296 297
297 298 #Post-process if post processor has been defined.
298 299 if hasattr(self, 'postprocessor') and self.postprocessor:
299 300 self.postprocessor(write_resultes)
300 301 conversion_success += 1
301 302
302 303 # If nothing was converted successfully, help the user.
303 304 if conversion_success == 0:
304 305 self.print_help()
305 306 sys.exit(-1)
306 307
307 308 #-----------------------------------------------------------------------------
308 309 # Main entry point
309 310 #-----------------------------------------------------------------------------
310 311
311 312 launch_new_instance = NbConvertApp.launch_instance
@@ -1,106 +1,74 b''
1 1 """Module that pre-processes the notebook for export to HTML.
2 2 """
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
5 #
3 # Copyright (c) IPython Development Team.
6 4 # Distributed under the terms of the Modified BSD License.
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 5 import os
16 6 import io
17 7
18 8 from IPython.utils import path
19
20 from .base import Preprocessor
21
22 9 from IPython.utils.traitlets import Unicode
23
24 #-----------------------------------------------------------------------------
25 # Classes and functions
26 #-----------------------------------------------------------------------------
10 from .base import Preprocessor
27 11
28 12 class CSSHTMLHeaderPreprocessor(Preprocessor):
29 13 """
30 14 Preprocessor used to pre-process notebook for HTML output. Adds IPython notebook
31 15 front-end CSS and Pygments CSS to HTML output.
32 16 """
33
34 header = []
35
36 17 highlight_class = Unicode('.highlight', config=True,
37 18 help="CSS highlight class identifier")
38 19
39 def __init__(self, config=None, **kw):
40 """
41 Public constructor
42
43 Parameters
44 ----------
45 config : Config
46 Configuration file structure
47 **kw : misc
48 Additional arguments
49 """
50
51 super(CSSHTMLHeaderPreprocessor, self).__init__(config=config, **kw)
52
53 if self.enabled :
54 self._regen_header()
55
56
57 20 def preprocess(self, nb, resources):
58 21 """Fetch and add CSS to the resource dictionary
59 22
60 23 Fetch CSS from IPython and Pygments to add at the beginning
61 24 of the html files. Add this css in resources in the
62 25 "inlining.css" key
63 26
64 27 Parameters
65 28 ----------
66 29 nb : NotebookNode
67 30 Notebook being converted
68 31 resources : dictionary
69 32 Additional resources used in the conversion process. Allows
70 33 preprocessors to pass variables into the Jinja engine.
71 34 """
72
73 35 resources['inlining'] = {}
74 resources['inlining']['css'] = self.header
75
36 resources['inlining']['css'] = self._generate_header(resources)
76 37 return nb, resources
77 38
78
79 def _regen_header(self):
39 def _generate_header(self, resources):
80 40 """
81 41 Fills self.header with lines of CSS extracted from IPython
82 42 and Pygments.
83 43 """
84 44 from pygments.formatters import HtmlFormatter
85
86 #Clear existing header.
87 45 header = []
88 46
89 #Construct path to IPy CSS
47 # Construct path to IPy CSS
90 48 from IPython.html import DEFAULT_STATIC_FILES_PATH
91 49 sheet_filename = os.path.join(DEFAULT_STATIC_FILES_PATH,
92 50 'style', 'style.min.css')
93 51
94 #Load style CSS file.
95 with io.open(sheet_filename, encoding='utf-8') as file:
96 file_text = file.read()
97 header.append(file_text)
98
99 #Add pygments CSS
52 # Load style CSS file.
53 with io.open(sheet_filename, encoding='utf-8') as f:
54 header.append(f.read())
55
56 # Load the user's custom CSS and IPython's default custom CSS. If they
57 # differ, assume the user has made modifications to his/her custom CSS
58 # and that we should inline it in the nbconvert output.
59 profile_dir = resources['profile_dir']
60 custom_css_filename = os.path.join(profile_dir, 'static', 'custom', 'custom.css')
61 if os.path.isfile(custom_css_filename):
62 with io.open(custom_css_filename, encoding='utf-8') as f:
63 custom_css = f.read()
64 with io.open(os.path.join(DEFAULT_STATIC_FILES_PATH, 'custom', 'custom.css'), encoding='utf-8') as f:
65 default_css = f.read()
66 if custom_css != default_css:
67 header.append(custom_css)
68
69 # Add pygments CSS
100 70 formatter = HtmlFormatter()
101 71 pygments_css = formatter.get_style_defs(self.highlight_class)
102 72 header.append(pygments_css)
103
104 #Set header
105 self.header = header
73 return header
106 74
General Comments 0
You need to be logged in to leave comments. Login now