##// END OF EJS Templates
Fix docstring of _log_default.
Antony Lee -
Show More
@@ -1,528 +1,528 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 subcommand_description = """
64 subcommand_description = """
65 Subcommands are launched as `{app} cmd [args]`. For information on using
65 Subcommands are launched as `{app} cmd [args]`. For information on using
66 subcommand 'cmd', do: `{app} cmd -h`.
66 subcommand 'cmd', do: `{app} cmd -h`.
67 """.strip().format(app=os.path.basename(sys.argv[0]))
67 """.strip().format(app=os.path.basename(sys.argv[0]))
68 # get running program name
68 # get running program name
69
69
70 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
71 # Application class
71 # Application class
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73
73
74 @decorator
74 @decorator
75 def catch_config_error(method, app, *args, **kwargs):
75 def catch_config_error(method, app, *args, **kwargs):
76 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
76 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
77
77
78 On a TraitError (generally caused by bad config), this will print the trait's
78 On a TraitError (generally caused by bad config), this will print the trait's
79 message, and exit the app.
79 message, and exit the app.
80
80
81 For use on init methods, to prevent invoking excepthook on invalid input.
81 For use on init methods, to prevent invoking excepthook on invalid input.
82 """
82 """
83 try:
83 try:
84 return method(app, *args, **kwargs)
84 return method(app, *args, **kwargs)
85 except (TraitError, ArgumentError) as e:
85 except (TraitError, ArgumentError) as e:
86 app.print_description()
86 app.print_description()
87 app.print_help()
87 app.print_help()
88 app.print_examples()
88 app.print_examples()
89 app.log.fatal("Bad config encountered during initialization:")
89 app.log.fatal("Bad config encountered during initialization:")
90 app.log.fatal(str(e))
90 app.log.fatal(str(e))
91 app.log.debug("Config at the time: %s", app.config)
91 app.log.debug("Config at the time: %s", app.config)
92 app.exit(1)
92 app.exit(1)
93
93
94
94
95 class ApplicationError(Exception):
95 class ApplicationError(Exception):
96 pass
96 pass
97
97
98
98
99 class Application(SingletonConfigurable):
99 class Application(SingletonConfigurable):
100 """A singleton application with full configuration support."""
100 """A singleton application with full configuration support."""
101
101
102 # The name of the application, will usually match the name of the command
102 # The name of the application, will usually match the name of the command
103 # line application
103 # line application
104 name = Unicode(u'application')
104 name = Unicode(u'application')
105
105
106 # The description of the application that is printed at the beginning
106 # The description of the application that is printed at the beginning
107 # of the help.
107 # of the help.
108 description = Unicode(u'This is an application.')
108 description = Unicode(u'This is an application.')
109 # default section descriptions
109 # default section descriptions
110 option_description = Unicode(option_description)
110 option_description = Unicode(option_description)
111 keyvalue_description = Unicode(keyvalue_description)
111 keyvalue_description = Unicode(keyvalue_description)
112 subcommand_description = Unicode(subcommand_description)
112 subcommand_description = Unicode(subcommand_description)
113
113
114 # The usage and example string that goes at the end of the help string.
114 # The usage and example string that goes at the end of the help string.
115 examples = Unicode()
115 examples = Unicode()
116
116
117 # A sequence of Configurable subclasses whose config=True attributes will
117 # A sequence of Configurable subclasses whose config=True attributes will
118 # be exposed at the command line.
118 # be exposed at the command line.
119 classes = List([])
119 classes = List([])
120
120
121 # The version string of this application.
121 # The version string of this application.
122 version = Unicode(u'0.0')
122 version = Unicode(u'0.0')
123
123
124 # The log level for the application
124 # The log level for the application
125 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
125 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
126 default_value=logging.WARN,
126 default_value=logging.WARN,
127 config=True,
127 config=True,
128 help="Set the log level by value or name.")
128 help="Set the log level by value or name.")
129 def _log_level_changed(self, name, old, new):
129 def _log_level_changed(self, name, old, new):
130 """Adjust the log level when log_level is set."""
130 """Adjust the log level when log_level is set."""
131 if isinstance(new, basestring):
131 if isinstance(new, basestring):
132 new = getattr(logging, new)
132 new = getattr(logging, new)
133 self.log_level = new
133 self.log_level = new
134 self.log.setLevel(new)
134 self.log.setLevel(new)
135
135
136 log_format = Unicode("[%(name)s] %(message)s", config=True,
136 log_format = Unicode("[%(name)s] %(message)s", config=True,
137 help="The Logging format template",
137 help="The Logging format template",
138 )
138 )
139 log = Instance(logging.Logger)
139 log = Instance(logging.Logger)
140 def _log_default(self):
140 def _log_default(self):
141 """Start logging for this application.
141 """Start logging for this application.
142
142
143 The default is to log to stdout using a StreamHandler, if no default
143 The default is to log to stderr using a StreamHandler, if no default
144 handler already exists. The log level starts at logging.WARN, but this
144 handler already exists. The log level starts at logging.WARN, but this
145 can be adjusted by setting the ``log_level`` attribute.
145 can be adjusted by setting the ``log_level`` attribute.
146 """
146 """
147 log = logging.getLogger(self.__class__.__name__)
147 log = logging.getLogger(self.__class__.__name__)
148 log.setLevel(self.log_level)
148 log.setLevel(self.log_level)
149 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
149 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
150 while _log:
150 while _log:
151 if _log.handlers:
151 if _log.handlers:
152 return log
152 return log
153 if not _log.propagate:
153 if not _log.propagate:
154 break
154 break
155 else:
155 else:
156 _log = _log.parent
156 _log = _log.parent
157 if sys.executable.endswith('pythonw.exe'):
157 if sys.executable.endswith('pythonw.exe'):
158 # this should really go to a file, but file-logging is only
158 # this should really go to a file, but file-logging is only
159 # hooked up in parallel applications
159 # hooked up in parallel applications
160 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
160 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
161 else:
161 else:
162 _log_handler = logging.StreamHandler()
162 _log_handler = logging.StreamHandler()
163 _log_formatter = logging.Formatter(self.log_format)
163 _log_formatter = logging.Formatter(self.log_format)
164 _log_handler.setFormatter(_log_formatter)
164 _log_handler.setFormatter(_log_formatter)
165 log.addHandler(_log_handler)
165 log.addHandler(_log_handler)
166 return log
166 return log
167
167
168 # the alias map for configurables
168 # the alias map for configurables
169 aliases = Dict({'log-level' : 'Application.log_level'})
169 aliases = Dict({'log-level' : 'Application.log_level'})
170
170
171 # flags for loading Configurables or store_const style flags
171 # flags for loading Configurables or store_const style flags
172 # flags are loaded from this dict by '--key' flags
172 # flags are loaded from this dict by '--key' flags
173 # this must be a dict of two-tuples, the first element being the Config/dict
173 # this must be a dict of two-tuples, the first element being the Config/dict
174 # and the second being the help string for the flag
174 # and the second being the help string for the flag
175 flags = Dict()
175 flags = Dict()
176 def _flags_changed(self, name, old, new):
176 def _flags_changed(self, name, old, new):
177 """ensure flags dict is valid"""
177 """ensure flags dict is valid"""
178 for key,value in new.iteritems():
178 for key,value in new.iteritems():
179 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
179 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
180 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
180 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
181 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
181 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
182
182
183
183
184 # subcommands for launching other applications
184 # subcommands for launching other applications
185 # if this is not empty, this will be a parent Application
185 # if this is not empty, this will be a parent Application
186 # this must be a dict of two-tuples,
186 # this must be a dict of two-tuples,
187 # the first element being the application class/import string
187 # the first element being the application class/import string
188 # and the second being the help string for the subcommand
188 # and the second being the help string for the subcommand
189 subcommands = Dict()
189 subcommands = Dict()
190 # parse_command_line will initialize a subapp, if requested
190 # parse_command_line will initialize a subapp, if requested
191 subapp = Instance('IPython.config.application.Application', allow_none=True)
191 subapp = Instance('IPython.config.application.Application', allow_none=True)
192
192
193 # extra command-line arguments that don't set config values
193 # extra command-line arguments that don't set config values
194 extra_args = List(Unicode)
194 extra_args = List(Unicode)
195
195
196
196
197 def __init__(self, **kwargs):
197 def __init__(self, **kwargs):
198 SingletonConfigurable.__init__(self, **kwargs)
198 SingletonConfigurable.__init__(self, **kwargs)
199 # Ensure my class is in self.classes, so my attributes appear in command line
199 # Ensure my class is in self.classes, so my attributes appear in command line
200 # options and config files.
200 # options and config files.
201 if self.__class__ not in self.classes:
201 if self.__class__ not in self.classes:
202 self.classes.insert(0, self.__class__)
202 self.classes.insert(0, self.__class__)
203
203
204 def _config_changed(self, name, old, new):
204 def _config_changed(self, name, old, new):
205 SingletonConfigurable._config_changed(self, name, old, new)
205 SingletonConfigurable._config_changed(self, name, old, new)
206 self.log.debug('Config changed:')
206 self.log.debug('Config changed:')
207 self.log.debug(repr(new))
207 self.log.debug(repr(new))
208
208
209 @catch_config_error
209 @catch_config_error
210 def initialize(self, argv=None):
210 def initialize(self, argv=None):
211 """Do the basic steps to configure me.
211 """Do the basic steps to configure me.
212
212
213 Override in subclasses.
213 Override in subclasses.
214 """
214 """
215 self.parse_command_line(argv)
215 self.parse_command_line(argv)
216
216
217
217
218 def start(self):
218 def start(self):
219 """Start the app mainloop.
219 """Start the app mainloop.
220
220
221 Override in subclasses.
221 Override in subclasses.
222 """
222 """
223 if self.subapp is not None:
223 if self.subapp is not None:
224 return self.subapp.start()
224 return self.subapp.start()
225
225
226 def print_alias_help(self):
226 def print_alias_help(self):
227 """Print the alias part of the help."""
227 """Print the alias part of the help."""
228 if not self.aliases:
228 if not self.aliases:
229 return
229 return
230
230
231 lines = []
231 lines = []
232 classdict = {}
232 classdict = {}
233 for cls in self.classes:
233 for cls in self.classes:
234 # include all parents (up to, but excluding Configurable) in available names
234 # include all parents (up to, but excluding Configurable) in available names
235 for c in cls.mro()[:-3]:
235 for c in cls.mro()[:-3]:
236 classdict[c.__name__] = c
236 classdict[c.__name__] = c
237
237
238 for alias, longname in self.aliases.iteritems():
238 for alias, longname in self.aliases.iteritems():
239 classname, traitname = longname.split('.',1)
239 classname, traitname = longname.split('.',1)
240 cls = classdict[classname]
240 cls = classdict[classname]
241
241
242 trait = cls.class_traits(config=True)[traitname]
242 trait = cls.class_traits(config=True)[traitname]
243 help = cls.class_get_trait_help(trait).splitlines()
243 help = cls.class_get_trait_help(trait).splitlines()
244 # reformat first line
244 # reformat first line
245 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
245 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
246 if len(alias) == 1:
246 if len(alias) == 1:
247 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
247 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
248 lines.extend(help)
248 lines.extend(help)
249 # lines.append('')
249 # lines.append('')
250 print os.linesep.join(lines)
250 print os.linesep.join(lines)
251
251
252 def print_flag_help(self):
252 def print_flag_help(self):
253 """Print the flag part of the help."""
253 """Print the flag part of the help."""
254 if not self.flags:
254 if not self.flags:
255 return
255 return
256
256
257 lines = []
257 lines = []
258 for m, (cfg,help) in self.flags.iteritems():
258 for m, (cfg,help) in self.flags.iteritems():
259 prefix = '--' if len(m) > 1 else '-'
259 prefix = '--' if len(m) > 1 else '-'
260 lines.append(prefix+m)
260 lines.append(prefix+m)
261 lines.append(indent(dedent(help.strip())))
261 lines.append(indent(dedent(help.strip())))
262 # lines.append('')
262 # lines.append('')
263 print os.linesep.join(lines)
263 print os.linesep.join(lines)
264
264
265 def print_options(self):
265 def print_options(self):
266 if not self.flags and not self.aliases:
266 if not self.flags and not self.aliases:
267 return
267 return
268 lines = ['Options']
268 lines = ['Options']
269 lines.append('-'*len(lines[0]))
269 lines.append('-'*len(lines[0]))
270 lines.append('')
270 lines.append('')
271 for p in wrap_paragraphs(self.option_description):
271 for p in wrap_paragraphs(self.option_description):
272 lines.append(p)
272 lines.append(p)
273 lines.append('')
273 lines.append('')
274 print os.linesep.join(lines)
274 print os.linesep.join(lines)
275 self.print_flag_help()
275 self.print_flag_help()
276 self.print_alias_help()
276 self.print_alias_help()
277 print
277 print
278
278
279 def print_subcommands(self):
279 def print_subcommands(self):
280 """Print the subcommand part of the help."""
280 """Print the subcommand part of the help."""
281 if not self.subcommands:
281 if not self.subcommands:
282 return
282 return
283
283
284 lines = ["Subcommands"]
284 lines = ["Subcommands"]
285 lines.append('-'*len(lines[0]))
285 lines.append('-'*len(lines[0]))
286 lines.append('')
286 lines.append('')
287 for p in wrap_paragraphs(self.subcommand_description):
287 for p in wrap_paragraphs(self.subcommand_description):
288 lines.append(p)
288 lines.append(p)
289 lines.append('')
289 lines.append('')
290 for subc, (cls, help) in self.subcommands.iteritems():
290 for subc, (cls, help) in self.subcommands.iteritems():
291 lines.append(subc)
291 lines.append(subc)
292 if help:
292 if help:
293 lines.append(indent(dedent(help.strip())))
293 lines.append(indent(dedent(help.strip())))
294 lines.append('')
294 lines.append('')
295 print os.linesep.join(lines)
295 print os.linesep.join(lines)
296
296
297 def print_help(self, classes=False):
297 def print_help(self, classes=False):
298 """Print the help for each Configurable class in self.classes.
298 """Print the help for each Configurable class in self.classes.
299
299
300 If classes=False (the default), only flags and aliases are printed.
300 If classes=False (the default), only flags and aliases are printed.
301 """
301 """
302 self.print_subcommands()
302 self.print_subcommands()
303 self.print_options()
303 self.print_options()
304
304
305 if classes:
305 if classes:
306 if self.classes:
306 if self.classes:
307 print "Class parameters"
307 print "Class parameters"
308 print "----------------"
308 print "----------------"
309 print
309 print
310 for p in wrap_paragraphs(self.keyvalue_description):
310 for p in wrap_paragraphs(self.keyvalue_description):
311 print p
311 print p
312 print
312 print
313
313
314 for cls in self.classes:
314 for cls in self.classes:
315 cls.class_print_help()
315 cls.class_print_help()
316 print
316 print
317 else:
317 else:
318 print "To see all available configurables, use `--help-all`"
318 print "To see all available configurables, use `--help-all`"
319 print
319 print
320
320
321 def print_description(self):
321 def print_description(self):
322 """Print the application description."""
322 """Print the application description."""
323 for p in wrap_paragraphs(self.description):
323 for p in wrap_paragraphs(self.description):
324 print p
324 print p
325 print
325 print
326
326
327 def print_examples(self):
327 def print_examples(self):
328 """Print usage and examples.
328 """Print usage and examples.
329
329
330 This usage string goes at the end of the command line help string
330 This usage string goes at the end of the command line help string
331 and should contain examples of the application's usage.
331 and should contain examples of the application's usage.
332 """
332 """
333 if self.examples:
333 if self.examples:
334 print "Examples"
334 print "Examples"
335 print "--------"
335 print "--------"
336 print
336 print
337 print indent(dedent(self.examples.strip()))
337 print indent(dedent(self.examples.strip()))
338 print
338 print
339
339
340 def print_version(self):
340 def print_version(self):
341 """Print the version string."""
341 """Print the version string."""
342 print self.version
342 print self.version
343
343
344 def update_config(self, config):
344 def update_config(self, config):
345 """Fire the traits events when the config is updated."""
345 """Fire the traits events when the config is updated."""
346 # Save a copy of the current config.
346 # Save a copy of the current config.
347 newconfig = deepcopy(self.config)
347 newconfig = deepcopy(self.config)
348 # Merge the new config into the current one.
348 # Merge the new config into the current one.
349 newconfig._merge(config)
349 newconfig._merge(config)
350 # Save the combined config as self.config, which triggers the traits
350 # Save the combined config as self.config, which triggers the traits
351 # events.
351 # events.
352 self.config = newconfig
352 self.config = newconfig
353
353
354 @catch_config_error
354 @catch_config_error
355 def initialize_subcommand(self, subc, argv=None):
355 def initialize_subcommand(self, subc, argv=None):
356 """Initialize a subcommand with argv."""
356 """Initialize a subcommand with argv."""
357 subapp,help = self.subcommands.get(subc)
357 subapp,help = self.subcommands.get(subc)
358
358
359 if isinstance(subapp, basestring):
359 if isinstance(subapp, basestring):
360 subapp = import_item(subapp)
360 subapp = import_item(subapp)
361
361
362 # clear existing instances
362 # clear existing instances
363 self.__class__.clear_instance()
363 self.__class__.clear_instance()
364 # instantiate
364 # instantiate
365 self.subapp = subapp.instance()
365 self.subapp = subapp.instance()
366 # and initialize subapp
366 # and initialize subapp
367 self.subapp.initialize(argv)
367 self.subapp.initialize(argv)
368
368
369 def flatten_flags(self):
369 def flatten_flags(self):
370 """flatten flags and aliases, so cl-args override as expected.
370 """flatten flags and aliases, so cl-args override as expected.
371
371
372 This prevents issues such as an alias pointing to InteractiveShell,
372 This prevents issues such as an alias pointing to InteractiveShell,
373 but a config file setting the same trait in TerminalInteraciveShell
373 but a config file setting the same trait in TerminalInteraciveShell
374 getting inappropriate priority over the command-line arg.
374 getting inappropriate priority over the command-line arg.
375
375
376 Only aliases with exactly one descendent in the class list
376 Only aliases with exactly one descendent in the class list
377 will be promoted.
377 will be promoted.
378
378
379 """
379 """
380 # build a tree of classes in our list that inherit from a particular
380 # build a tree of classes in our list that inherit from a particular
381 # it will be a dict by parent classname of classes in our list
381 # it will be a dict by parent classname of classes in our list
382 # that are descendents
382 # that are descendents
383 mro_tree = defaultdict(list)
383 mro_tree = defaultdict(list)
384 for cls in self.classes:
384 for cls in self.classes:
385 clsname = cls.__name__
385 clsname = cls.__name__
386 for parent in cls.mro()[1:-3]:
386 for parent in cls.mro()[1:-3]:
387 # exclude cls itself and Configurable,HasTraits,object
387 # exclude cls itself and Configurable,HasTraits,object
388 mro_tree[parent.__name__].append(clsname)
388 mro_tree[parent.__name__].append(clsname)
389 # flatten aliases, which have the form:
389 # flatten aliases, which have the form:
390 # { 'alias' : 'Class.trait' }
390 # { 'alias' : 'Class.trait' }
391 aliases = {}
391 aliases = {}
392 for alias, cls_trait in self.aliases.iteritems():
392 for alias, cls_trait in self.aliases.iteritems():
393 cls,trait = cls_trait.split('.',1)
393 cls,trait = cls_trait.split('.',1)
394 children = mro_tree[cls]
394 children = mro_tree[cls]
395 if len(children) == 1:
395 if len(children) == 1:
396 # exactly one descendent, promote alias
396 # exactly one descendent, promote alias
397 cls = children[0]
397 cls = children[0]
398 aliases[alias] = '.'.join([cls,trait])
398 aliases[alias] = '.'.join([cls,trait])
399
399
400 # flatten flags, which are of the form:
400 # flatten flags, which are of the form:
401 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
401 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
402 flags = {}
402 flags = {}
403 for key, (flagdict, help) in self.flags.iteritems():
403 for key, (flagdict, help) in self.flags.iteritems():
404 newflag = {}
404 newflag = {}
405 for cls, subdict in flagdict.iteritems():
405 for cls, subdict in flagdict.iteritems():
406 children = mro_tree[cls]
406 children = mro_tree[cls]
407 # exactly one descendent, promote flag section
407 # exactly one descendent, promote flag section
408 if len(children) == 1:
408 if len(children) == 1:
409 cls = children[0]
409 cls = children[0]
410 newflag[cls] = subdict
410 newflag[cls] = subdict
411 flags[key] = (newflag, help)
411 flags[key] = (newflag, help)
412 return flags, aliases
412 return flags, aliases
413
413
414 @catch_config_error
414 @catch_config_error
415 def parse_command_line(self, argv=None):
415 def parse_command_line(self, argv=None):
416 """Parse the command line arguments."""
416 """Parse the command line arguments."""
417 argv = sys.argv[1:] if argv is None else argv
417 argv = sys.argv[1:] if argv is None else argv
418
418
419 if argv and argv[0] == 'help':
419 if argv and argv[0] == 'help':
420 # turn `ipython help notebook` into `ipython notebook -h`
420 # turn `ipython help notebook` into `ipython notebook -h`
421 argv = argv[1:] + ['-h']
421 argv = argv[1:] + ['-h']
422
422
423 if self.subcommands and len(argv) > 0:
423 if self.subcommands and len(argv) > 0:
424 # we have subcommands, and one may have been specified
424 # we have subcommands, and one may have been specified
425 subc, subargv = argv[0], argv[1:]
425 subc, subargv = argv[0], argv[1:]
426 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
426 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
427 # it's a subcommand, and *not* a flag or class parameter
427 # it's a subcommand, and *not* a flag or class parameter
428 return self.initialize_subcommand(subc, subargv)
428 return self.initialize_subcommand(subc, subargv)
429
429
430 # Arguments after a '--' argument are for the script IPython may be
430 # Arguments after a '--' argument are for the script IPython may be
431 # about to run, not IPython iteslf. For arguments parsed here (help and
431 # about to run, not IPython iteslf. For arguments parsed here (help and
432 # version), we want to only search the arguments up to the first
432 # version), we want to only search the arguments up to the first
433 # occurrence of '--', which we're calling interpreted_argv.
433 # occurrence of '--', which we're calling interpreted_argv.
434 try:
434 try:
435 interpreted_argv = argv[:argv.index('--')]
435 interpreted_argv = argv[:argv.index('--')]
436 except ValueError:
436 except ValueError:
437 interpreted_argv = argv
437 interpreted_argv = argv
438
438
439 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
439 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
440 self.print_description()
440 self.print_description()
441 self.print_help('--help-all' in interpreted_argv)
441 self.print_help('--help-all' in interpreted_argv)
442 self.print_examples()
442 self.print_examples()
443 self.exit(0)
443 self.exit(0)
444
444
445 if '--version' in interpreted_argv or '-V' in interpreted_argv:
445 if '--version' in interpreted_argv or '-V' in interpreted_argv:
446 self.print_version()
446 self.print_version()
447 self.exit(0)
447 self.exit(0)
448
448
449 # flatten flags&aliases, so cl-args get appropriate priority:
449 # flatten flags&aliases, so cl-args get appropriate priority:
450 flags,aliases = self.flatten_flags()
450 flags,aliases = self.flatten_flags()
451
451
452 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
452 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
453 flags=flags)
453 flags=flags)
454 config = loader.load_config()
454 config = loader.load_config()
455 self.update_config(config)
455 self.update_config(config)
456 # store unparsed args in extra_args
456 # store unparsed args in extra_args
457 self.extra_args = loader.extra_args
457 self.extra_args = loader.extra_args
458
458
459 @catch_config_error
459 @catch_config_error
460 def load_config_file(self, filename, path=None):
460 def load_config_file(self, filename, path=None):
461 """Load a .py based config file by filename and path."""
461 """Load a .py based config file by filename and path."""
462 loader = PyFileConfigLoader(filename, path=path)
462 loader = PyFileConfigLoader(filename, path=path)
463 try:
463 try:
464 config = loader.load_config()
464 config = loader.load_config()
465 except ConfigFileNotFound:
465 except ConfigFileNotFound:
466 # problem finding the file, raise
466 # problem finding the file, raise
467 raise
467 raise
468 except Exception:
468 except Exception:
469 # try to get the full filename, but it will be empty in the
469 # try to get the full filename, but it will be empty in the
470 # unlikely event that the error raised before filefind finished
470 # unlikely event that the error raised before filefind finished
471 filename = loader.full_filename or filename
471 filename = loader.full_filename or filename
472 # problem while running the file
472 # problem while running the file
473 self.log.error("Exception while loading config file %s",
473 self.log.error("Exception while loading config file %s",
474 filename, exc_info=True)
474 filename, exc_info=True)
475 else:
475 else:
476 self.log.debug("Loaded config file: %s", loader.full_filename)
476 self.log.debug("Loaded config file: %s", loader.full_filename)
477 self.update_config(config)
477 self.update_config(config)
478
478
479 def generate_config_file(self):
479 def generate_config_file(self):
480 """generate default config file from Configurables"""
480 """generate default config file from Configurables"""
481 lines = ["# Configuration file for %s."%self.name]
481 lines = ["# Configuration file for %s."%self.name]
482 lines.append('')
482 lines.append('')
483 lines.append('c = get_config()')
483 lines.append('c = get_config()')
484 lines.append('')
484 lines.append('')
485 for cls in self.classes:
485 for cls in self.classes:
486 lines.append(cls.class_config_section())
486 lines.append(cls.class_config_section())
487 return '\n'.join(lines)
487 return '\n'.join(lines)
488
488
489 def exit(self, exit_status=0):
489 def exit(self, exit_status=0):
490 self.log.debug("Exiting application: %s" % self.name)
490 self.log.debug("Exiting application: %s" % self.name)
491 sys.exit(exit_status)
491 sys.exit(exit_status)
492
492
493 #-----------------------------------------------------------------------------
493 #-----------------------------------------------------------------------------
494 # utility functions, for convenience
494 # utility functions, for convenience
495 #-----------------------------------------------------------------------------
495 #-----------------------------------------------------------------------------
496
496
497 def boolean_flag(name, configurable, set_help='', unset_help=''):
497 def boolean_flag(name, configurable, set_help='', unset_help=''):
498 """Helper for building basic --trait, --no-trait flags.
498 """Helper for building basic --trait, --no-trait flags.
499
499
500 Parameters
500 Parameters
501 ----------
501 ----------
502
502
503 name : str
503 name : str
504 The name of the flag.
504 The name of the flag.
505 configurable : str
505 configurable : str
506 The 'Class.trait' string of the trait to be set/unset with the flag
506 The 'Class.trait' string of the trait to be set/unset with the flag
507 set_help : unicode
507 set_help : unicode
508 help string for --name flag
508 help string for --name flag
509 unset_help : unicode
509 unset_help : unicode
510 help string for --no-name flag
510 help string for --no-name flag
511
511
512 Returns
512 Returns
513 -------
513 -------
514
514
515 cfg : dict
515 cfg : dict
516 A dict with two keys: 'name', and 'no-name', for setting and unsetting
516 A dict with two keys: 'name', and 'no-name', for setting and unsetting
517 the trait, respectively.
517 the trait, respectively.
518 """
518 """
519 # default helpstrings
519 # default helpstrings
520 set_help = set_help or "set %s=True"%configurable
520 set_help = set_help or "set %s=True"%configurable
521 unset_help = unset_help or "set %s=False"%configurable
521 unset_help = unset_help or "set %s=False"%configurable
522
522
523 cls,trait = configurable.split('.')
523 cls,trait = configurable.split('.')
524
524
525 setter = {cls : {trait : True}}
525 setter = {cls : {trait : True}}
526 unsetter = {cls : {trait : False}}
526 unsetter = {cls : {trait : False}}
527 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
527 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
528
528
General Comments 0
You need to be logged in to leave comments. Login now