##// END OF EJS Templates
Updates to config/application.
Brian Granger -
Show More
@@ -1,230 +1,241 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
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 class ApplicationError(Exception):
65 pass
66
67
64 68 class Application(SingletonConfigurable):
65 69 """A singleton application with full configuration support."""
66 70
67 71 # The name of the application, will usually match the name of the command
68 72 # line application
69 73 app_name = Unicode(u'application')
70 74
71 75 # The description of the application that is printed at the beginning
72 76 # of the help.
73 77 description = Unicode(u'This is an application.')
74 78 # default section descriptions
75 79 flag_description = Unicode(flag_description)
76 80 alias_description = Unicode(alias_description)
77 81 keyvalue_description = Unicode(keyvalue_description)
78 82
79 83
80 84 # A sequence of Configurable subclasses whose config=True attributes will
81 85 # be exposed at the command line.
82 86 classes = List([])
83 87
84 88 # The version string of this application.
85 89 version = Unicode(u'0.0')
86 90
87 91 # The log level for the application
88 92 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
89 93 config=True,
90 94 help="Set the log level (0,10,20,30,40,50).")
91 95
92 96 # the alias map for configurables
93 97 aliases = Dict(dict(log_level='Application.log_level'))
94 98
95 99 # flags for loading Configurables or store_const style flags
96 100 # flags are loaded from this dict by '--key' flags
97 101 # this must be a dict of two-tuples, the first element being the Config/dict
98 102 # and the second being the help string for the flag
99 103 flags = Dict()
100 104
101 105
102 106 def __init__(self, **kwargs):
103 107 SingletonConfigurable.__init__(self, **kwargs)
104 108 # Add my class to self.classes so my attributes appear in command line
105 109 # options.
106 110 self.classes.insert(0, self.__class__)
107 111
108 112 # ensure self.flags dict is valid
109 113 for key,value in self.flags.iteritems():
110 114 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
111 115 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
112 116 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
113 117 self.init_logging()
114
118
119 def _config_changed(self, name, old, new):
120 SingletonConfigurable._config_changed(self, name, old, new)
121 self.log.debug('Config changed:')
122 self.log.debug(repr(new))
123
115 124 def init_logging(self):
116 125 """Start logging for this application.
117 126
118 127 The default is to log to stdout using a StreaHandler. The log level
119 128 starts at loggin.WARN, but this can be adjusted by setting the
120 129 ``log_level`` attribute.
121 130 """
122 131 self.log = logging.getLogger(self.__class__.__name__)
123 132 self.log.setLevel(self.log_level)
124 133 self._log_handler = logging.StreamHandler()
125 134 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
126 135 self._log_handler.setFormatter(self._log_formatter)
127 136 self.log.addHandler(self._log_handler)
128 137
129 138 def _log_level_changed(self, name, old, new):
130 139 """Adjust the log level when log_level is set."""
131 140 self.log.setLevel(new)
132 141
133 142 def print_alias_help(self):
134 143 """print the alias part of the help"""
135 144 if not self.aliases:
136 145 return
137 146
138 147 print "Aliases"
139 148 print "-------"
140 149 print self.alias_description
141 150 print
142 151
143 152 classdict = {}
144 153 for c in self.classes:
145 154 classdict[c.__name__] = c
146 155
147 156 for alias, longname in self.aliases.iteritems():
148 157 classname, traitname = longname.split('.',1)
149 158 cls = classdict[classname]
150 159
151 160 trait = cls.class_traits(config=True)[traitname]
152 161 help = trait.get_metadata('help')
153 162 print alias, "(%s)"%longname, ':', trait.__class__.__name__
154 163 if help:
155 164 print indent(help)
156 165 print
157 166
158 167 def print_flag_help(self):
159 168 """print the flag part of the help"""
160 169 if not self.flags:
161 170 return
162 171
163 172 print "Flags"
164 173 print "-----"
165 174 print self.flag_description
166 175 print
167 176
168 177 for m, (cfg,help) in self.flags.iteritems():
169 178 print '--'+m
170 179 print indent(help)
171 180 print
172 181
173 182 def print_help(self):
174 183 """Print the help for each Configurable class in self.classes."""
175 184 self.print_flag_help()
176 185 self.print_alias_help()
177 186 if self.classes:
178 187 print "Class parameters"
179 188 print "----------------"
180 189 print self.keyvalue_description
181 190 print
182 191
183 192 for cls in self.classes:
184 193 cls.class_print_help()
185 194 print
186 195
187 196 def print_description(self):
188 197 """Print the application description."""
189 198 print self.description
190 199 print
191 200
192 201 def print_version(self):
193 202 """Print the version string."""
194 203 print self.version
195 204
196 205 def update_config(self, config):
197 206 """Fire the traits events when the config is updated."""
198 207 # Save a copy of the current config.
199 208 newconfig = deepcopy(self.config)
200 209 # Merge the new config into the current one.
201 210 newconfig._merge(config)
202 211 # Save the combined config as self.config, which triggers the traits
203 212 # events.
204 213 self.config = config
205 214
206 215 def parse_command_line(self, argv=None):
207 216 """Parse the command line arguments."""
208 217 argv = sys.argv[1:] if argv is None else argv
209 218
210 219 if '-h' in argv or '--help' in argv:
211 220 self.print_description()
212 221 self.print_help()
213 sys.exit(1)
222 self.exit(1)
214 223
215 224 if '--version' in argv:
216 225 self.print_version()
217 sys.exit(1)
226 self.exit(1)
218 227
219 228 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
220 229 flags=self.flags)
221 230 config = loader.load_config()
222 231 self.update_config(config)
223 232
224 233 def load_config_file(self, filename, path=None):
225 234 """Load a .py based config file by filename and path."""
226 # TODO: this raises IOError if filename does not exist.
227 235 loader = PyFileConfigLoader(filename, path=path)
228 236 config = loader.load_config()
229 237 self.update_config(config)
230 238
239 def exit(self, exit_status=0):
240 self.log.debug("Exiting application: %s" % self.name)
241 sys.exit(exit_status)
General Comments 0
You need to be logged in to leave comments. Login now