##// END OF EJS Templates
Merge pull request #3759 from jdfreder/help_fix...
Paul Ivanov -
r11668:335e87d9 merge
parent child Browse files
Show More
@@ -1,576 +1,576 b''
1 1 # encoding: utf-8
2 2 """
3 3 A base class for a configurable application.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * Min RK
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import logging
23 23 import os
24 24 import re
25 25 import sys
26 26 from copy import deepcopy
27 27 from collections import defaultdict
28 28
29 29 from IPython.external.decorator import decorator
30 30
31 31 from IPython.config.configurable import SingletonConfigurable
32 32 from IPython.config.loader import (
33 33 KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound,
34 34 )
35 35
36 36 from IPython.utils.traitlets import (
37 37 Unicode, List, Enum, Dict, Instance, TraitError
38 38 )
39 39 from IPython.utils.importstring import import_item
40 40 from IPython.utils.text import indent, wrap_paragraphs, dedent
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # function for re-wrapping a helpstring
44 44 #-----------------------------------------------------------------------------
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Descriptions for the various sections
48 48 #-----------------------------------------------------------------------------
49 49
50 50 # merge flags&aliases into options
51 51 option_description = """
52 52 Arguments that take values are actually convenience aliases to full
53 53 Configurables, whose aliases are listed on the help line. For more information
54 54 on full configurables, see '--help-all'.
55 55 """.strip() # trim newlines of front and back
56 56
57 57 keyvalue_description = """
58 58 Parameters are set from command-line arguments of the form:
59 59 `--Class.trait=value`.
60 60 This line is evaluated in Python, so simple expressions are allowed, e.g.::
61 61 `--C.a='range(3)'` For setting C.a=[0,1,2].
62 62 """.strip() # trim newlines of front and back
63 63
64 64 # sys.argv can be missing, for example when python is embedded. See the docs
65 65 # for details: http://docs.python.org/2/c-api/intro.html#embedding-python
66 66 if not hasattr(sys, "argv"):
67 67 sys.argv = [""]
68 68
69 69 subcommand_description = """
70 70 Subcommands are launched as `{app} cmd [args]`. For information on using
71 71 subcommand 'cmd', do: `{app} cmd -h`.
72 72 """.strip().format(app=os.path.basename(sys.argv[0]))
73 73 # get running program name
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Application class
77 77 #-----------------------------------------------------------------------------
78 78
79 79 @decorator
80 80 def catch_config_error(method, app, *args, **kwargs):
81 81 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
82 82
83 83 On a TraitError (generally caused by bad config), this will print the trait's
84 84 message, and exit the app.
85 85
86 86 For use on init methods, to prevent invoking excepthook on invalid input.
87 87 """
88 88 try:
89 89 return method(app, *args, **kwargs)
90 90 except (TraitError, ArgumentError) as e:
91 app.print_description()
92 91 app.print_help()
93 app.print_examples()
94 92 app.log.fatal("Bad config encountered during initialization:")
95 93 app.log.fatal(str(e))
96 94 app.log.debug("Config at the time: %s", app.config)
97 95 app.exit(1)
98 96
99 97
100 98 class ApplicationError(Exception):
101 99 pass
102 100
103 101 class LevelFormatter(logging.Formatter):
104 102 """Formatter with additional `highlevel` record
105 103
106 104 This field is empty if log level is less than highlevel_limit,
107 105 otherwise it is formatted with self.highlevel_format.
108 106
109 107 Useful for adding 'WARNING' to warning messages,
110 108 without adding 'INFO' to info, etc.
111 109 """
112 110 highlevel_limit = logging.WARN
113 111 highlevel_format = " %(levelname)s |"
114 112
115 113 def format(self, record):
116 114 if record.levelno >= self.highlevel_limit:
117 115 record.highlevel = self.highlevel_format % record.__dict__
118 116 else:
119 117 record.highlevel = ""
120 118
121 119 return super(LevelFormatter, self).format(record)
122 120
123 121
124 122 class Application(SingletonConfigurable):
125 123 """A singleton application with full configuration support."""
126 124
127 125 # The name of the application, will usually match the name of the command
128 126 # line application
129 127 name = Unicode(u'application')
130 128
131 129 # The description of the application that is printed at the beginning
132 130 # of the help.
133 131 description = Unicode(u'This is an application.')
134 132 # default section descriptions
135 133 option_description = Unicode(option_description)
136 134 keyvalue_description = Unicode(keyvalue_description)
137 135 subcommand_description = Unicode(subcommand_description)
138 136
139 137 # The usage and example string that goes at the end of the help string.
140 138 examples = Unicode()
141 139
142 140 # A sequence of Configurable subclasses whose config=True attributes will
143 141 # be exposed at the command line.
144 142 classes = List([])
145 143
146 144 # The version string of this application.
147 145 version = Unicode(u'0.0')
148 146
149 147 # The log level for the application
150 148 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
151 149 default_value=logging.WARN,
152 150 config=True,
153 151 help="Set the log level by value or name.")
154 152 def _log_level_changed(self, name, old, new):
155 153 """Adjust the log level when log_level is set."""
156 154 if isinstance(new, basestring):
157 155 new = getattr(logging, new)
158 156 self.log_level = new
159 157 self.log.setLevel(new)
160 158
161 159 log_datefmt = Unicode("%Y-%m-%d %H:%M:%S", config=True,
162 160 help="The date format used by logging formatters for %(asctime)s"
163 161 )
164 162 def _log_datefmt_changed(self, name, old, new):
165 163 self._log_format_changed()
166 164
167 165 log_format = Unicode("[%(name)s]%(highlevel)s %(message)s", config=True,
168 166 help="The Logging format template",
169 167 )
170 168 def _log_format_changed(self, name, old, new):
171 169 """Change the log formatter when log_format is set."""
172 170 _log_handler = self.log.handlers[0]
173 171 _log_formatter = LevelFormatter(new, datefmt=self.log_datefmt)
174 172 _log_handler.setFormatter(_log_formatter)
175 173
176 174 log = Instance(logging.Logger)
177 175 def _log_default(self):
178 176 """Start logging for this application.
179 177
180 178 The default is to log to stderr using a StreamHandler, if no default
181 179 handler already exists. The log level starts at logging.WARN, but this
182 180 can be adjusted by setting the ``log_level`` attribute.
183 181 """
184 182 log = logging.getLogger(self.__class__.__name__)
185 183 log.setLevel(self.log_level)
186 184 log.propagate = False
187 185 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
188 186 while _log:
189 187 if _log.handlers:
190 188 return log
191 189 if not _log.propagate:
192 190 break
193 191 else:
194 192 _log = _log.parent
195 193 if sys.executable.endswith('pythonw.exe'):
196 194 # this should really go to a file, but file-logging is only
197 195 # hooked up in parallel applications
198 196 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
199 197 else:
200 198 _log_handler = logging.StreamHandler()
201 199 _log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt)
202 200 _log_handler.setFormatter(_log_formatter)
203 201 log.addHandler(_log_handler)
204 202 return log
205 203
206 204 # the alias map for configurables
207 205 aliases = Dict({'log-level' : 'Application.log_level'})
208 206
209 207 # flags for loading Configurables or store_const style flags
210 208 # flags are loaded from this dict by '--key' flags
211 209 # this must be a dict of two-tuples, the first element being the Config/dict
212 210 # and the second being the help string for the flag
213 211 flags = Dict()
214 212 def _flags_changed(self, name, old, new):
215 213 """ensure flags dict is valid"""
216 214 for key,value in new.iteritems():
217 215 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
218 216 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
219 217 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
220 218
221 219
222 220 # subcommands for launching other applications
223 221 # if this is not empty, this will be a parent Application
224 222 # this must be a dict of two-tuples,
225 223 # the first element being the application class/import string
226 224 # and the second being the help string for the subcommand
227 225 subcommands = Dict()
228 226 # parse_command_line will initialize a subapp, if requested
229 227 subapp = Instance('IPython.config.application.Application', allow_none=True)
230 228
231 229 # extra command-line arguments that don't set config values
232 230 extra_args = List(Unicode)
233 231
234 232
235 233 def __init__(self, **kwargs):
236 234 SingletonConfigurable.__init__(self, **kwargs)
237 235 # Ensure my class is in self.classes, so my attributes appear in command line
238 236 # options and config files.
239 237 if self.__class__ not in self.classes:
240 238 self.classes.insert(0, self.__class__)
241 239
242 240 def _config_changed(self, name, old, new):
243 241 SingletonConfigurable._config_changed(self, name, old, new)
244 242 self.log.debug('Config changed:')
245 243 self.log.debug(repr(new))
246 244
247 245 @catch_config_error
248 246 def initialize(self, argv=None):
249 247 """Do the basic steps to configure me.
250 248
251 249 Override in subclasses.
252 250 """
253 251 self.parse_command_line(argv)
254 252
255 253
256 254 def start(self):
257 255 """Start the app mainloop.
258 256
259 257 Override in subclasses.
260 258 """
261 259 if self.subapp is not None:
262 260 return self.subapp.start()
263 261
264 262 def print_alias_help(self):
265 263 """Print the alias part of the help."""
266 264 if not self.aliases:
267 265 return
268 266
269 267 lines = []
270 268 classdict = {}
271 269 for cls in self.classes:
272 270 # include all parents (up to, but excluding Configurable) in available names
273 271 for c in cls.mro()[:-3]:
274 272 classdict[c.__name__] = c
275 273
276 274 for alias, longname in self.aliases.iteritems():
277 275 classname, traitname = longname.split('.',1)
278 276 cls = classdict[classname]
279 277
280 278 trait = cls.class_traits(config=True)[traitname]
281 279 help = cls.class_get_trait_help(trait).splitlines()
282 280 # reformat first line
283 281 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
284 282 if len(alias) == 1:
285 283 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
286 284 lines.extend(help)
287 285 # lines.append('')
288 286 print os.linesep.join(lines)
289 287
290 288 def print_flag_help(self):
291 289 """Print the flag part of the help."""
292 290 if not self.flags:
293 291 return
294 292
295 293 lines = []
296 294 for m, (cfg,help) in self.flags.iteritems():
297 295 prefix = '--' if len(m) > 1 else '-'
298 296 lines.append(prefix+m)
299 297 lines.append(indent(dedent(help.strip())))
300 298 # lines.append('')
301 299 print os.linesep.join(lines)
302 300
303 301 def print_options(self):
304 302 if not self.flags and not self.aliases:
305 303 return
306 304 lines = ['Options']
307 305 lines.append('-'*len(lines[0]))
308 306 lines.append('')
309 307 for p in wrap_paragraphs(self.option_description):
310 308 lines.append(p)
311 309 lines.append('')
312 310 print os.linesep.join(lines)
313 311 self.print_flag_help()
314 312 self.print_alias_help()
315 313 print
316 314
317 315 def print_subcommands(self):
318 316 """Print the subcommand part of the help."""
319 317 if not self.subcommands:
320 318 return
321 319
322 320 lines = ["Subcommands"]
323 321 lines.append('-'*len(lines[0]))
324 322 lines.append('')
325 323 for p in wrap_paragraphs(self.subcommand_description):
326 324 lines.append(p)
327 325 lines.append('')
328 326 for subc, (cls, help) in self.subcommands.iteritems():
329 327 lines.append(subc)
330 328 if help:
331 329 lines.append(indent(dedent(help.strip())))
332 330 lines.append('')
333 331 print os.linesep.join(lines)
334 332
335 333 def print_help(self, classes=False):
336 334 """Print the help for each Configurable class in self.classes.
337 335
338 336 If classes=False (the default), only flags and aliases are printed.
339 337 """
338 self.print_description()
340 339 self.print_subcommands()
341 340 self.print_options()
342 341
343 342 if classes:
344 343 if self.classes:
345 344 print "Class parameters"
346 345 print "----------------"
347 346 print
348 347 for p in wrap_paragraphs(self.keyvalue_description):
349 348 print p
350 349 print
351 350
352 351 for cls in self.classes:
353 352 cls.class_print_help()
354 353 print
355 354 else:
356 355 print "To see all available configurables, use `--help-all`"
357 356 print
358 357
358 self.print_examples()
359
360
359 361 def print_description(self):
360 362 """Print the application description."""
361 363 for p in wrap_paragraphs(self.description):
362 364 print p
363 365 print
364 366
365 367 def print_examples(self):
366 368 """Print usage and examples.
367 369
368 370 This usage string goes at the end of the command line help string
369 371 and should contain examples of the application's usage.
370 372 """
371 373 if self.examples:
372 374 print "Examples"
373 375 print "--------"
374 376 print
375 377 print indent(dedent(self.examples.strip()))
376 378 print
377 379
378 380 def print_version(self):
379 381 """Print the version string."""
380 382 print self.version
381 383
382 384 def update_config(self, config):
383 385 """Fire the traits events when the config is updated."""
384 386 # Save a copy of the current config.
385 387 newconfig = deepcopy(self.config)
386 388 # Merge the new config into the current one.
387 389 newconfig.merge(config)
388 390 # Save the combined config as self.config, which triggers the traits
389 391 # events.
390 392 self.config = newconfig
391 393
392 394 @catch_config_error
393 395 def initialize_subcommand(self, subc, argv=None):
394 396 """Initialize a subcommand with argv."""
395 397 subapp,help = self.subcommands.get(subc)
396 398
397 399 if isinstance(subapp, basestring):
398 400 subapp = import_item(subapp)
399 401
400 402 # clear existing instances
401 403 self.__class__.clear_instance()
402 404 # instantiate
403 405 self.subapp = subapp.instance(config=self.config)
404 406 # and initialize subapp
405 407 self.subapp.initialize(argv)
406 408
407 409 def flatten_flags(self):
408 410 """flatten flags and aliases, so cl-args override as expected.
409 411
410 412 This prevents issues such as an alias pointing to InteractiveShell,
411 413 but a config file setting the same trait in TerminalInteraciveShell
412 414 getting inappropriate priority over the command-line arg.
413 415
414 416 Only aliases with exactly one descendent in the class list
415 417 will be promoted.
416 418
417 419 """
418 420 # build a tree of classes in our list that inherit from a particular
419 421 # it will be a dict by parent classname of classes in our list
420 422 # that are descendents
421 423 mro_tree = defaultdict(list)
422 424 for cls in self.classes:
423 425 clsname = cls.__name__
424 426 for parent in cls.mro()[1:-3]:
425 427 # exclude cls itself and Configurable,HasTraits,object
426 428 mro_tree[parent.__name__].append(clsname)
427 429 # flatten aliases, which have the form:
428 430 # { 'alias' : 'Class.trait' }
429 431 aliases = {}
430 432 for alias, cls_trait in self.aliases.iteritems():
431 433 cls,trait = cls_trait.split('.',1)
432 434 children = mro_tree[cls]
433 435 if len(children) == 1:
434 436 # exactly one descendent, promote alias
435 437 cls = children[0]
436 438 aliases[alias] = '.'.join([cls,trait])
437 439
438 440 # flatten flags, which are of the form:
439 441 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
440 442 flags = {}
441 443 for key, (flagdict, help) in self.flags.iteritems():
442 444 newflag = {}
443 445 for cls, subdict in flagdict.iteritems():
444 446 children = mro_tree[cls]
445 447 # exactly one descendent, promote flag section
446 448 if len(children) == 1:
447 449 cls = children[0]
448 450 newflag[cls] = subdict
449 451 flags[key] = (newflag, help)
450 452 return flags, aliases
451 453
452 454 @catch_config_error
453 455 def parse_command_line(self, argv=None):
454 456 """Parse the command line arguments."""
455 457 argv = sys.argv[1:] if argv is None else argv
456 458
457 459 if argv and argv[0] == 'help':
458 460 # turn `ipython help notebook` into `ipython notebook -h`
459 461 argv = argv[1:] + ['-h']
460 462
461 463 if self.subcommands and len(argv) > 0:
462 464 # we have subcommands, and one may have been specified
463 465 subc, subargv = argv[0], argv[1:]
464 466 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
465 467 # it's a subcommand, and *not* a flag or class parameter
466 468 return self.initialize_subcommand(subc, subargv)
467 469
468 470 # Arguments after a '--' argument are for the script IPython may be
469 471 # about to run, not IPython iteslf. For arguments parsed here (help and
470 472 # version), we want to only search the arguments up to the first
471 473 # occurrence of '--', which we're calling interpreted_argv.
472 474 try:
473 475 interpreted_argv = argv[:argv.index('--')]
474 476 except ValueError:
475 477 interpreted_argv = argv
476 478
477 479 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
478 self.print_description()
479 480 self.print_help('--help-all' in interpreted_argv)
480 self.print_examples()
481 481 self.exit(0)
482 482
483 483 if '--version' in interpreted_argv or '-V' in interpreted_argv:
484 484 self.print_version()
485 485 self.exit(0)
486 486
487 487 # flatten flags&aliases, so cl-args get appropriate priority:
488 488 flags,aliases = self.flatten_flags()
489 489
490 490 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
491 491 flags=flags)
492 492 config = loader.load_config()
493 493 self.update_config(config)
494 494 # store unparsed args in extra_args
495 495 self.extra_args = loader.extra_args
496 496
497 497 @catch_config_error
498 498 def load_config_file(self, filename, path=None):
499 499 """Load a .py based config file by filename and path."""
500 500 loader = PyFileConfigLoader(filename, path=path)
501 501 try:
502 502 config = loader.load_config()
503 503 except ConfigFileNotFound:
504 504 # problem finding the file, raise
505 505 raise
506 506 except Exception:
507 507 # try to get the full filename, but it will be empty in the
508 508 # unlikely event that the error raised before filefind finished
509 509 filename = loader.full_filename or filename
510 510 # problem while running the file
511 511 self.log.error("Exception while loading config file %s",
512 512 filename, exc_info=True)
513 513 else:
514 514 self.log.debug("Loaded config file: %s", loader.full_filename)
515 515 self.update_config(config)
516 516
517 517 def generate_config_file(self):
518 518 """generate default config file from Configurables"""
519 519 lines = ["# Configuration file for %s."%self.name]
520 520 lines.append('')
521 521 lines.append('c = get_config()')
522 522 lines.append('')
523 523 for cls in self.classes:
524 524 lines.append(cls.class_config_section())
525 525 return '\n'.join(lines)
526 526
527 527 def exit(self, exit_status=0):
528 528 self.log.debug("Exiting application: %s" % self.name)
529 529 sys.exit(exit_status)
530 530
531 531 @classmethod
532 532 def launch_instance(cls, argv=None, **kwargs):
533 533 """Launch a global instance of this Application
534 534
535 535 If a global instance already exists, this reinitializes and starts it
536 536 """
537 537 app = cls.instance(**kwargs)
538 538 app.initialize(argv)
539 539 app.start()
540 540
541 541 #-----------------------------------------------------------------------------
542 542 # utility functions, for convenience
543 543 #-----------------------------------------------------------------------------
544 544
545 545 def boolean_flag(name, configurable, set_help='', unset_help=''):
546 546 """Helper for building basic --trait, --no-trait flags.
547 547
548 548 Parameters
549 549 ----------
550 550
551 551 name : str
552 552 The name of the flag.
553 553 configurable : str
554 554 The 'Class.trait' string of the trait to be set/unset with the flag
555 555 set_help : unicode
556 556 help string for --name flag
557 557 unset_help : unicode
558 558 help string for --no-name flag
559 559
560 560 Returns
561 561 -------
562 562
563 563 cfg : dict
564 564 A dict with two keys: 'name', and 'no-name', for setting and unsetting
565 565 the trait, respectively.
566 566 """
567 567 # default helpstrings
568 568 set_help = set_help or "set %s=True"%configurable
569 569 unset_help = unset_help or "set %s=False"%configurable
570 570
571 571 cls,trait = configurable.split('.')
572 572
573 573 setter = {cls : {trait : True}}
574 574 unsetter = {cls : {trait : False}}
575 575 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
576 576
@@ -1,236 +1,226 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) 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
18 18 # Stdlib imports
19 19 from __future__ import print_function
20 20 import sys
21 21 import os
22 22 import glob
23 23
24 24 # From IPython
25 25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
26 26 from IPython.config import catch_config_error, Configurable
27 27 from IPython.utils.traitlets import (
28 28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 29 )
30 30 from IPython.utils.importstring import import_item
31 31
32 32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 33 from IPython.nbconvert import exporters, transformers, writers
34 34 from .utils.base import NbConvertBase
35 35
36 36 #-----------------------------------------------------------------------------
37 37 #Classes and functions
38 38 #-----------------------------------------------------------------------------
39 39
40 40 nbconvert_aliases = {}
41 41 nbconvert_aliases.update(base_aliases)
42 42 nbconvert_aliases.update({
43 43 'format' : 'NbConvertApp.export_format',
44 44 'notebooks' : 'NbConvertApp.notebooks',
45 45 'writer' : 'NbConvertApp.writer_class',
46 46 })
47 47
48 48 nbconvert_flags = {}
49 49 nbconvert_flags.update(base_flags)
50 50 nbconvert_flags.update({
51 51 'stdout' : (
52 52 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
53 53 "Write notebook output to stdout instead of files."
54 54 )
55 55 })
56 56
57 57
58 58 class NbConvertApp(BaseIPythonApplication):
59 59 """Application used to convert to and from notebook file type (*.ipynb)"""
60 60
61 61 name = 'ipython-nbconvert'
62 62 aliases = nbconvert_aliases
63 63 flags = nbconvert_flags
64 64
65 65 def _classes_default(self):
66 66 classes = [NbConvertBase]
67 67 for pkg in (exporters, transformers, writers):
68 68 for name in dir(pkg):
69 69 cls = getattr(pkg, name)
70 70 if isinstance(cls, type) and issubclass(cls, Configurable):
71 71 classes.append(cls)
72 72 return classes
73 73
74 74 description = Unicode(
75 75 u"""This application is used to convert notebook files (*.ipynb)
76 76 to various other formats.""")
77 77
78 78 examples = Unicode(u"""
79 79 The simplest way to use nbconvert is
80 80
81 81 > ipython nbconvert mynotebook.ipynb
82 82
83 83 which will convert mynotebook.ipynb to the default format (probably HTML).
84 84
85 85 You can specify the export format with `--format`.
86 86 Options include {0}
87 87
88 88 > ipython nbconvert --format latex mynotebook.ipnynb
89 89
90 90 You can also pipe the output to stdout, rather than a file
91 91
92 92 > ipython nbconvert mynotebook.ipynb --stdout
93 93
94 94 Multiple notebooks can be given at the command line in a couple of
95 95 different ways:
96 96
97 97 > ipython nbconvert notebook*.ipynb
98 98 > ipython nbconvert notebook1.ipynb notebook2.ipynb
99 99
100 100 or you can specify the notebooks list in a config file, containing::
101 101
102 102 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
103 103
104 104 > ipython nbconvert --config mycfg.py
105 105 """.format(get_export_names()))
106 106 # Writer specific variables
107 107 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
108 108 help="""Instance of the writer class used to write the
109 109 results of the conversion.""")
110 110 writer_class = DottedObjectName('FilesWriter', config=True,
111 111 help="""Writer class used to write the
112 112 results of the conversion""")
113 113 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
114 114 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
115 115 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
116 116 writer_factory = Type()
117 117
118 118 def _writer_class_changed(self, name, old, new):
119 119 if new in self.writer_aliases:
120 120 new = self.writer_aliases[new]
121 121 self.writer_factory = import_item(new)
122 122
123 123
124 124 # Other configurable variables
125 125 export_format = CaselessStrEnum(get_export_names(),
126 126 default_value="full_html",
127 127 config=True,
128 128 help="""The export format to be used."""
129 129 )
130 130
131 131 notebooks = List([], config=True, help="""List of notebooks to convert.
132 132 Wildcards are supported.
133 133 Filenames passed positionally will be added to the list.
134 134 """)
135 135
136 136 @catch_config_error
137 137 def initialize(self, argv=None):
138 138 super(NbConvertApp, self).initialize(argv)
139 139 self.init_notebooks()
140 140 self.init_writer()
141 141
142 142 def init_notebooks(self):
143 143 """Construct the list of notebooks.
144 144 If notebooks are passed on the command-line,
145 145 they override notebooks specified in config files.
146 146 Glob each notebook to replace notebook patterns with filenames.
147 147 """
148 148
149 149 # Specifying notebooks on the command-line overrides (rather than adds)
150 150 # the notebook list
151 151 if self.extra_args:
152 152 patterns = self.extra_args
153 153 else:
154 154 patterns = self.notebooks
155 155
156 156 # Use glob to replace all the notebook patterns with filenames.
157 157 filenames = []
158 158 for pattern in patterns:
159 159
160 160 # Use glob to find matching filenames. Allow the user to convert
161 161 # notebooks without having to type the extension.
162 162 globbed_files = glob.glob(pattern)
163 163 globbed_files.extend(glob.glob(pattern + '.ipynb'))
164 164
165 165 for filename in globbed_files:
166 166 if not filename in filenames:
167 167 filenames.append(filename)
168 168 self.notebooks = filenames
169 169
170 170 def init_writer(self):
171 171 """
172 172 Initialize the writer (which is stateless)
173 173 """
174 174 self._writer_class_changed(None, self.writer_class, self.writer_class)
175 175 self.writer = self.writer_factory(parent=self)
176 176
177 177 def start(self):
178 178 """
179 179 Ran after initialization completed
180 180 """
181 181 super(NbConvertApp, self).start()
182 182 self.convert_notebooks()
183 183
184 184 def convert_notebooks(self):
185 185 """
186 186 Convert the notebooks in the self.notebook traitlet
187 187 """
188 188 # Export each notebook
189 189 conversion_success = 0
190 190 for notebook_filename in self.notebooks:
191 191
192 192 # Get a unique key for the notebook and set it in the resources object.
193 193 basename = os.path.basename(notebook_filename)
194 194 notebook_name = basename[:basename.rfind('.')]
195 195 resources = {}
196 196 resources['unique_key'] = notebook_name
197 197 resources['output_files_dir'] = '%s_files' % notebook_name
198 198
199 199 # Try to export
200 200 try:
201 201 output, resources = export_by_name(self.export_format,
202 202 notebook_filename,
203 203 resources=resources,
204 204 config=self.config)
205 205 except ExporterNameError as e:
206 206 print("Error: '%s' exporter not found." % self.export_format,
207 207 file=sys.stderr)
208 208 print("Known exporters are:",
209 209 "\n\t" + "\n\t".join(get_export_names()),
210 210 file=sys.stderr)
211 211 sys.exit(-1)
212 # except Exception as e:
213 # print("Error: could not export '%s'" % notebook_filename, file=sys.stderr)
214 # print(e, file=sys.stderr)
215 212 else:
216 213 self.writer.write(output, resources, notebook_name=notebook_name)
217 214 conversion_success += 1
218 215
219 216 # If nothing was converted successfully, help the user.
220 217 if conversion_success == 0:
221
222 # No notebooks were specified, show help.
223 if len(self.notebooks) == 0:
224 self.print_help()
225
226 # Notebooks were specified, but not converted successfully. Show how
227 # to access help.
228 else:
229 print('For help, use "ipython nbconvert --help"')
218 self.print_help()
219 sys.exit(-1)
230 220
231 221
232 222 #-----------------------------------------------------------------------------
233 223 # Main entry point
234 224 #-----------------------------------------------------------------------------
235 225
236 226 launch_new_instance = NbConvertApp.launch_instance
General Comments 0
You need to be logged in to leave comments. Login now