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