##// END OF EJS Templates
add `--help-all` flag, and don't print all configurables by default
MinRK -
Show More
@@ -1,286 +1,293 b''
1 1 # encoding: utf-8
2 2 """
3 3 A base class for a configurable application.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2008-2011 The IPython Development Team
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 21 from copy import deepcopy
22 22 import logging
23 23 import sys
24 24
25 25 from IPython.config.configurable import SingletonConfigurable
26 26 from IPython.config.loader import (
27 27 KeyValueConfigLoader, PyFileConfigLoader, Config
28 28 )
29 29
30 30 from IPython.utils.traitlets import (
31 31 Unicode, List, Int, Enum, Dict
32 32 )
33 33 from IPython.utils.text import indent
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Descriptions for the various sections
37 37 #-----------------------------------------------------------------------------
38 38
39 39 flag_description = """
40 40 Flags are command-line arguments passed as '--<flag>'.
41 41 These take no parameters, unlike regular key-value arguments.
42 42 They are typically used for setting boolean flags, or enabling
43 43 modes that involve setting multiple options together.
44 44 """.strip() # trim newlines of front and back
45 45
46 46 alias_description = """
47 47 These are commonly set parameters, given abbreviated aliases for convenience.
48 48 They are set in the same `name=value` way as class parameters, where
49 49 <name> is replaced by the real parameter for which it is an alias.
50 50 """.strip() # trim newlines of front and back
51 51
52 52 keyvalue_description = """
53 53 Parameters are set from command-line arguments of the form:
54 54 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
55 55 This line is evaluated in Python, so simple expressions are allowed, e.g.
56 56 `C.a='range(3)'` For setting C.a=[0,1,2]
57 57 """.strip() # trim newlines of front and back
58 58
59 59 #-----------------------------------------------------------------------------
60 60 # Application class
61 61 #-----------------------------------------------------------------------------
62 62
63 63
64 64 class ApplicationError(Exception):
65 65 pass
66 66
67 67
68 68 class Application(SingletonConfigurable):
69 69 """A singleton application with full configuration support."""
70 70
71 71 # The name of the application, will usually match the name of the command
72 72 # line application
73 73 name = Unicode(u'application')
74 74
75 75 # The description of the application that is printed at the beginning
76 76 # of the help.
77 77 description = Unicode(u'This is an application.')
78 78 # default section descriptions
79 79 flag_description = Unicode(flag_description)
80 80 alias_description = Unicode(alias_description)
81 81 keyvalue_description = Unicode(keyvalue_description)
82 82
83 83
84 84 # A sequence of Configurable subclasses whose config=True attributes will
85 85 # be exposed at the command line.
86 86 classes = List([])
87 87
88 88 # The version string of this application.
89 89 version = Unicode(u'0.0')
90 90
91 91 # The log level for the application
92 92 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
93 93 config=True,
94 94 help="Set the log level.")
95 95
96 96 # the alias map for configurables
97 97 aliases = Dict(dict(log_level='Application.log_level'))
98 98
99 99 # flags for loading Configurables or store_const style flags
100 100 # flags are loaded from this dict by '--key' flags
101 101 # this must be a dict of two-tuples, the first element being the Config/dict
102 102 # and the second being the help string for the flag
103 103 flags = Dict()
104 104
105 105
106 106 def __init__(self, **kwargs):
107 107 SingletonConfigurable.__init__(self, **kwargs)
108 108 # Add my class to self.classes so my attributes appear in command line
109 109 # options.
110 110 self.classes.insert(0, self.__class__)
111 111
112 112 # ensure self.flags dict is valid
113 113 for key,value in self.flags.iteritems():
114 114 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
115 115 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
116 116 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
117 117 self.init_logging()
118 118
119 119 def _config_changed(self, name, old, new):
120 120 SingletonConfigurable._config_changed(self, name, old, new)
121 121 self.log.debug('Config changed:')
122 122 self.log.debug(repr(new))
123 123
124 124 def init_logging(self):
125 125 """Start logging for this application.
126 126
127 127 The default is to log to stdout using a StreaHandler. The log level
128 128 starts at loggin.WARN, but this can be adjusted by setting the
129 129 ``log_level`` attribute.
130 130 """
131 131 self.log = logging.getLogger(self.__class__.__name__)
132 132 self.log.setLevel(self.log_level)
133 133 self._log_handler = logging.StreamHandler()
134 134 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
135 135 self._log_handler.setFormatter(self._log_formatter)
136 136 self.log.addHandler(self._log_handler)
137 137
138 138 def _log_level_changed(self, name, old, new):
139 139 """Adjust the log level when log_level is set."""
140 140 self.log.setLevel(new)
141 141
142 142 def print_alias_help(self):
143 143 """print the alias part of the help"""
144 144 if not self.aliases:
145 145 return
146 146
147 147 lines = ['Aliases']
148 148 lines.append('_'*len(lines[0]))
149 149 lines.append(self.alias_description)
150 150 lines.append('')
151 151
152 152 classdict = {}
153 153 for c in self.classes:
154 154 classdict[c.__name__] = c
155 155
156 156 for alias, longname in self.aliases.iteritems():
157 157 classname, traitname = longname.split('.',1)
158 158 cls = classdict[classname]
159 159
160 160 trait = cls.class_traits(config=True)[traitname]
161 161 help = cls.class_get_trait_help(trait)
162 162 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
163 163 lines.append(help)
164 164 # header = "%s (%s) : %s"%(alias, longname, trait.__class__.__name__)
165 165 # lines.append(header)
166 166 # help = cls.class_get_trait_help(trait)
167 167 # if help:
168 168 # lines.append(indent(help, flatten=True))
169 169 lines.append('')
170 170 print '\n'.join(lines)
171 171
172 172 def print_flag_help(self):
173 173 """print the flag part of the help"""
174 174 if not self.flags:
175 175 return
176 176
177 177 lines = ['Flags']
178 178 lines.append('_'*len(lines[0]))
179 179 lines.append(self.flag_description)
180 180 lines.append('')
181 181
182 182 for m, (cfg,help) in self.flags.iteritems():
183 183 lines.append('--'+m)
184 184 lines.append(indent(help, flatten=True))
185 185 lines.append('')
186 186 print '\n'.join(lines)
187 187
188 def print_help(self):
189 """Print the help for each Configurable class in self.classes."""
188 def print_help(self, classes=False):
189 """Print the help for each Configurable class in self.classes.
190
191 If classes=False (the default), only flags and aliases are printed
192 """
190 193 self.print_flag_help()
191 194 self.print_alias_help()
192 195
193 if self.classes:
194 print "Class parameters"
195 print "----------------"
196 print self.keyvalue_description
197 print
196 if classes:
197 if self.classes:
198 print "Class parameters"
199 print "----------------"
200 print self.keyvalue_description
201 print
198 202
199 for cls in self.classes:
200 cls.class_print_help()
203 for cls in self.classes:
204 cls.class_print_help()
205 print
206 else:
207 print "To see all available configurables, use `--help-all`"
201 208 print
202 209
203 210 def print_description(self):
204 211 """Print the application description."""
205 212 print self.description
206 213 print
207 214
208 215 def print_version(self):
209 216 """Print the version string."""
210 217 print self.version
211 218
212 219 def update_config(self, config):
213 220 """Fire the traits events when the config is updated."""
214 221 # Save a copy of the current config.
215 222 newconfig = deepcopy(self.config)
216 223 # Merge the new config into the current one.
217 224 newconfig._merge(config)
218 225 # Save the combined config as self.config, which triggers the traits
219 226 # events.
220 227 self.config = newconfig
221 228
222 229 def parse_command_line(self, argv=None):
223 230 """Parse the command line arguments."""
224 231 argv = sys.argv[1:] if argv is None else argv
225 232
226 if '-h' in argv or '--help' in argv:
233 if '-h' in argv or '--help' in argv or '--help-all' in argv:
227 234 self.print_description()
228 self.print_help()
235 self.print_help('--help-all' in argv)
229 236 self.exit(0)
230 237
231 238 if '--version' in argv:
232 239 self.print_version()
233 240 self.exit(0)
234 241
235 242 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
236 243 flags=self.flags)
237 244 config = loader.load_config()
238 245 self.update_config(config)
239 246
240 247 def load_config_file(self, filename, path=None):
241 248 """Load a .py based config file by filename and path."""
242 249 loader = PyFileConfigLoader(filename, path=path)
243 250 config = loader.load_config()
244 251 self.update_config(config)
245 252
246 253 def exit(self, exit_status=0):
247 254 self.log.debug("Exiting application: %s" % self.name)
248 255 sys.exit(exit_status)
249 256
250 257 #-----------------------------------------------------------------------------
251 258 # utility functions, for convenience
252 259 #-----------------------------------------------------------------------------
253 260
254 261 def boolean_flag(name, configurable, set_help='', unset_help=''):
255 262 """helper for building basic --trait, --no-trait flags
256 263
257 264 Parameters
258 265 ----------
259 266
260 267 name : str
261 268 The name of the flag.
262 269 configurable : str
263 270 The 'Class.trait' string of the trait to be set/unset with the flag
264 271 set_help : unicode
265 272 help string for --name flag
266 273 unset_help : unicode
267 274 help string for --no-name flag
268 275
269 276 Returns
270 277 -------
271 278
272 279 cfg : dict
273 280 A dict with two keys: 'name', and 'no-name', for setting and unsetting
274 281 the trait, respectively.
275 282 """
276 283 # default helpstrings
277 284 set_help = set_help or "set %s=True"%configurable
278 285 unset_help = unset_help or "set %s=False"%configurable
279 286
280 287 cls,trait = configurable.split('.')
281 288
282 289 setter = Config()
283 290 setter[cls][trait] = True
284 291 unsetter = Config()
285 292 unsetter[cls][trait] = False
286 293 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
General Comments 0
You need to be logged in to leave comments. Login now