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