##// END OF EJS Templates
disable string macros
MinRK -
Show More
@@ -1,506 +1,502 b''
1 """A simple configuration system.
1 """A simple configuration system.
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * Brian Granger
5 * Brian Granger
6 * Fernando Perez
6 * Fernando Perez
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2009 The IPython Development Team
10 # Copyright (C) 2008-2009 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import __builtin__
20 import __builtin__
21 import re
21 import re
22 import sys
22 import sys
23
23
24 from IPython.external import argparse
24 from IPython.external import argparse
25 from IPython.utils.path import filefind
25 from IPython.utils.path import filefind
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Exceptions
28 # Exceptions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31
31
32 class ConfigError(Exception):
32 class ConfigError(Exception):
33 pass
33 pass
34
34
35
35
36 class ConfigLoaderError(ConfigError):
36 class ConfigLoaderError(ConfigError):
37 pass
37 pass
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Argparse fix
40 # Argparse fix
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 # Unfortunately argparse by default prints help messages to stderr instead of
43 # Unfortunately argparse by default prints help messages to stderr instead of
44 # stdout. This makes it annoying to capture long help screens at the command
44 # stdout. This makes it annoying to capture long help screens at the command
45 # line, since one must know how to pipe stderr, which many users don't know how
45 # line, since one must know how to pipe stderr, which many users don't know how
46 # to do. So we override the print_help method with one that defaults to
46 # to do. So we override the print_help method with one that defaults to
47 # stdout and use our class instead.
47 # stdout and use our class instead.
48
48
49 class ArgumentParser(argparse.ArgumentParser):
49 class ArgumentParser(argparse.ArgumentParser):
50 """Simple argparse subclass that prints help to stdout by default."""
50 """Simple argparse subclass that prints help to stdout by default."""
51
51
52 def print_help(self, file=None):
52 def print_help(self, file=None):
53 if file is None:
53 if file is None:
54 file = sys.stdout
54 file = sys.stdout
55 return super(ArgumentParser, self).print_help(file)
55 return super(ArgumentParser, self).print_help(file)
56
56
57 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
57 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Config class for holding config information
60 # Config class for holding config information
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63
63
64 class Config(dict):
64 class Config(dict):
65 """An attribute based dict that can do smart merges."""
65 """An attribute based dict that can do smart merges."""
66
66
67 def __init__(self, *args, **kwds):
67 def __init__(self, *args, **kwds):
68 dict.__init__(self, *args, **kwds)
68 dict.__init__(self, *args, **kwds)
69 # This sets self.__dict__ = self, but it has to be done this way
69 # This sets self.__dict__ = self, but it has to be done this way
70 # because we are also overriding __setattr__.
70 # because we are also overriding __setattr__.
71 dict.__setattr__(self, '__dict__', self)
71 dict.__setattr__(self, '__dict__', self)
72
72
73 def _merge(self, other):
73 def _merge(self, other):
74 to_update = {}
74 to_update = {}
75 for k, v in other.iteritems():
75 for k, v in other.iteritems():
76 if not self.has_key(k):
76 if not self.has_key(k):
77 to_update[k] = v
77 to_update[k] = v
78 else: # I have this key
78 else: # I have this key
79 if isinstance(v, Config):
79 if isinstance(v, Config):
80 # Recursively merge common sub Configs
80 # Recursively merge common sub Configs
81 self[k]._merge(v)
81 self[k]._merge(v)
82 else:
82 else:
83 # Plain updates for non-Configs
83 # Plain updates for non-Configs
84 to_update[k] = v
84 to_update[k] = v
85
85
86 self.update(to_update)
86 self.update(to_update)
87
87
88 def _is_section_key(self, key):
88 def _is_section_key(self, key):
89 if key[0].upper()==key[0] and not key.startswith('_'):
89 if key[0].upper()==key[0] and not key.startswith('_'):
90 return True
90 return True
91 else:
91 else:
92 return False
92 return False
93
93
94 def __contains__(self, key):
94 def __contains__(self, key):
95 if self._is_section_key(key):
95 if self._is_section_key(key):
96 return True
96 return True
97 else:
97 else:
98 return super(Config, self).__contains__(key)
98 return super(Config, self).__contains__(key)
99 # .has_key is deprecated for dictionaries.
99 # .has_key is deprecated for dictionaries.
100 has_key = __contains__
100 has_key = __contains__
101
101
102 def _has_section(self, key):
102 def _has_section(self, key):
103 if self._is_section_key(key):
103 if self._is_section_key(key):
104 if super(Config, self).__contains__(key):
104 if super(Config, self).__contains__(key):
105 return True
105 return True
106 return False
106 return False
107
107
108 def copy(self):
108 def copy(self):
109 return type(self)(dict.copy(self))
109 return type(self)(dict.copy(self))
110
110
111 def __copy__(self):
111 def __copy__(self):
112 return self.copy()
112 return self.copy()
113
113
114 def __deepcopy__(self, memo):
114 def __deepcopy__(self, memo):
115 import copy
115 import copy
116 return type(self)(copy.deepcopy(self.items()))
116 return type(self)(copy.deepcopy(self.items()))
117
117
118 def __getitem__(self, key):
118 def __getitem__(self, key):
119 # We cannot use directly self._is_section_key, because it triggers
119 # We cannot use directly self._is_section_key, because it triggers
120 # infinite recursion on top of PyPy. Instead, we manually fish the
120 # infinite recursion on top of PyPy. Instead, we manually fish the
121 # bound method.
121 # bound method.
122 is_section_key = self.__class__._is_section_key.__get__(self)
122 is_section_key = self.__class__._is_section_key.__get__(self)
123
123
124 # Because we use this for an exec namespace, we need to delegate
124 # Because we use this for an exec namespace, we need to delegate
125 # the lookup of names in __builtin__ to itself. This means
125 # the lookup of names in __builtin__ to itself. This means
126 # that you can't have section or attribute names that are
126 # that you can't have section or attribute names that are
127 # builtins.
127 # builtins.
128 try:
128 try:
129 return getattr(__builtin__, key)
129 return getattr(__builtin__, key)
130 except AttributeError:
130 except AttributeError:
131 pass
131 pass
132 if is_section_key(key):
132 if is_section_key(key):
133 try:
133 try:
134 return dict.__getitem__(self, key)
134 return dict.__getitem__(self, key)
135 except KeyError:
135 except KeyError:
136 c = Config()
136 c = Config()
137 dict.__setitem__(self, key, c)
137 dict.__setitem__(self, key, c)
138 return c
138 return c
139 else:
139 else:
140 return dict.__getitem__(self, key)
140 return dict.__getitem__(self, key)
141
141
142 def __setitem__(self, key, value):
142 def __setitem__(self, key, value):
143 # Don't allow names in __builtin__ to be modified.
143 # Don't allow names in __builtin__ to be modified.
144 if hasattr(__builtin__, key):
144 if hasattr(__builtin__, key):
145 raise ConfigError('Config variable names cannot have the same name '
145 raise ConfigError('Config variable names cannot have the same name '
146 'as a Python builtin: %s' % key)
146 'as a Python builtin: %s' % key)
147 if self._is_section_key(key):
147 if self._is_section_key(key):
148 if not isinstance(value, Config):
148 if not isinstance(value, Config):
149 raise ValueError('values whose keys begin with an uppercase '
149 raise ValueError('values whose keys begin with an uppercase '
150 'char must be Config instances: %r, %r' % (key, value))
150 'char must be Config instances: %r, %r' % (key, value))
151 else:
151 else:
152 dict.__setitem__(self, key, value)
152 dict.__setitem__(self, key, value)
153
153
154 def __getattr__(self, key):
154 def __getattr__(self, key):
155 try:
155 try:
156 return self.__getitem__(key)
156 return self.__getitem__(key)
157 except KeyError, e:
157 except KeyError, e:
158 raise AttributeError(e)
158 raise AttributeError(e)
159
159
160 def __setattr__(self, key, value):
160 def __setattr__(self, key, value):
161 try:
161 try:
162 self.__setitem__(key, value)
162 self.__setitem__(key, value)
163 except KeyError, e:
163 except KeyError, e:
164 raise AttributeError(e)
164 raise AttributeError(e)
165
165
166 def __delattr__(self, key):
166 def __delattr__(self, key):
167 try:
167 try:
168 dict.__delitem__(self, key)
168 dict.__delitem__(self, key)
169 except KeyError, e:
169 except KeyError, e:
170 raise AttributeError(e)
170 raise AttributeError(e)
171
171
172
172
173 #-----------------------------------------------------------------------------
173 #-----------------------------------------------------------------------------
174 # Config loading classes
174 # Config loading classes
175 #-----------------------------------------------------------------------------
175 #-----------------------------------------------------------------------------
176
176
177
177
178 class ConfigLoader(object):
178 class ConfigLoader(object):
179 """A object for loading configurations from just about anywhere.
179 """A object for loading configurations from just about anywhere.
180
180
181 The resulting configuration is packaged as a :class:`Struct`.
181 The resulting configuration is packaged as a :class:`Struct`.
182
182
183 Notes
183 Notes
184 -----
184 -----
185 A :class:`ConfigLoader` does one thing: load a config from a source
185 A :class:`ConfigLoader` does one thing: load a config from a source
186 (file, command line arguments) and returns the data as a :class:`Struct`.
186 (file, command line arguments) and returns the data as a :class:`Struct`.
187 There are lots of things that :class:`ConfigLoader` does not do. It does
187 There are lots of things that :class:`ConfigLoader` does not do. It does
188 not implement complex logic for finding config files. It does not handle
188 not implement complex logic for finding config files. It does not handle
189 default values or merge multiple configs. These things need to be
189 default values or merge multiple configs. These things need to be
190 handled elsewhere.
190 handled elsewhere.
191 """
191 """
192
192
193 def __init__(self):
193 def __init__(self):
194 """A base class for config loaders.
194 """A base class for config loaders.
195
195
196 Examples
196 Examples
197 --------
197 --------
198
198
199 >>> cl = ConfigLoader()
199 >>> cl = ConfigLoader()
200 >>> config = cl.load_config()
200 >>> config = cl.load_config()
201 >>> config
201 >>> config
202 {}
202 {}
203 """
203 """
204 self.clear()
204 self.clear()
205
205
206 def clear(self):
206 def clear(self):
207 self.config = Config()
207 self.config = Config()
208
208
209 def load_config(self):
209 def load_config(self):
210 """Load a config from somewhere, return a :class:`Config` instance.
210 """Load a config from somewhere, return a :class:`Config` instance.
211
211
212 Usually, this will cause self.config to be set and then returned.
212 Usually, this will cause self.config to be set and then returned.
213 However, in most cases, :meth:`ConfigLoader.clear` should be called
213 However, in most cases, :meth:`ConfigLoader.clear` should be called
214 to erase any previous state.
214 to erase any previous state.
215 """
215 """
216 self.clear()
216 self.clear()
217 return self.config
217 return self.config
218
218
219
219
220 class FileConfigLoader(ConfigLoader):
220 class FileConfigLoader(ConfigLoader):
221 """A base class for file based configurations.
221 """A base class for file based configurations.
222
222
223 As we add more file based config loaders, the common logic should go
223 As we add more file based config loaders, the common logic should go
224 here.
224 here.
225 """
225 """
226 pass
226 pass
227
227
228
228
229 class PyFileConfigLoader(FileConfigLoader):
229 class PyFileConfigLoader(FileConfigLoader):
230 """A config loader for pure python files.
230 """A config loader for pure python files.
231
231
232 This calls execfile on a plain python file and looks for attributes
232 This calls execfile on a plain python file and looks for attributes
233 that are all caps. These attribute are added to the config Struct.
233 that are all caps. These attribute are added to the config Struct.
234 """
234 """
235
235
236 def __init__(self, filename, path=None):
236 def __init__(self, filename, path=None):
237 """Build a config loader for a filename and path.
237 """Build a config loader for a filename and path.
238
238
239 Parameters
239 Parameters
240 ----------
240 ----------
241 filename : str
241 filename : str
242 The file name of the config file.
242 The file name of the config file.
243 path : str, list, tuple
243 path : str, list, tuple
244 The path to search for the config file on, or a sequence of
244 The path to search for the config file on, or a sequence of
245 paths to try in order.
245 paths to try in order.
246 """
246 """
247 super(PyFileConfigLoader, self).__init__()
247 super(PyFileConfigLoader, self).__init__()
248 self.filename = filename
248 self.filename = filename
249 self.path = path
249 self.path = path
250 self.full_filename = ''
250 self.full_filename = ''
251 self.data = None
251 self.data = None
252
252
253 def load_config(self):
253 def load_config(self):
254 """Load the config from a file and return it as a Struct."""
254 """Load the config from a file and return it as a Struct."""
255 self.clear()
255 self.clear()
256 self._find_file()
256 self._find_file()
257 self._read_file_as_dict()
257 self._read_file_as_dict()
258 self._convert_to_config()
258 self._convert_to_config()
259 return self.config
259 return self.config
260
260
261 def _find_file(self):
261 def _find_file(self):
262 """Try to find the file by searching the paths."""
262 """Try to find the file by searching the paths."""
263 self.full_filename = filefind(self.filename, self.path)
263 self.full_filename = filefind(self.filename, self.path)
264
264
265 def _read_file_as_dict(self):
265 def _read_file_as_dict(self):
266 """Load the config file into self.config, with recursive loading."""
266 """Load the config file into self.config, with recursive loading."""
267 # This closure is made available in the namespace that is used
267 # This closure is made available in the namespace that is used
268 # to exec the config file. This allows users to call
268 # to exec the config file. This allows users to call
269 # load_subconfig('myconfig.py') to load config files recursively.
269 # load_subconfig('myconfig.py') to load config files recursively.
270 # It needs to be a closure because it has references to self.path
270 # It needs to be a closure because it has references to self.path
271 # and self.config. The sub-config is loaded with the same path
271 # and self.config. The sub-config is loaded with the same path
272 # as the parent, but it uses an empty config which is then merged
272 # as the parent, but it uses an empty config which is then merged
273 # with the parents.
273 # with the parents.
274 def load_subconfig(fname):
274 def load_subconfig(fname):
275 loader = PyFileConfigLoader(fname, self.path)
275 loader = PyFileConfigLoader(fname, self.path)
276 try:
276 try:
277 sub_config = loader.load_config()
277 sub_config = loader.load_config()
278 except IOError:
278 except IOError:
279 # Pass silently if the sub config is not there. This happens
279 # Pass silently if the sub config is not there. This happens
280 # when a user us using a profile, but not the default config.
280 # when a user us using a profile, but not the default config.
281 pass
281 pass
282 else:
282 else:
283 self.config._merge(sub_config)
283 self.config._merge(sub_config)
284
284
285 # Again, this needs to be a closure and should be used in config
285 # Again, this needs to be a closure and should be used in config
286 # files to get the config being loaded.
286 # files to get the config being loaded.
287 def get_config():
287 def get_config():
288 return self.config
288 return self.config
289
289
290 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
290 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
291 fs_encoding = sys.getfilesystemencoding() or 'ascii'
291 fs_encoding = sys.getfilesystemencoding() or 'ascii'
292 conf_filename = self.full_filename.encode(fs_encoding)
292 conf_filename = self.full_filename.encode(fs_encoding)
293 execfile(conf_filename, namespace)
293 execfile(conf_filename, namespace)
294
294
295 def _convert_to_config(self):
295 def _convert_to_config(self):
296 if self.data is None:
296 if self.data is None:
297 ConfigLoaderError('self.data does not exist')
297 ConfigLoaderError('self.data does not exist')
298
298
299
299
300 class CommandLineConfigLoader(ConfigLoader):
300 class CommandLineConfigLoader(ConfigLoader):
301 """A config loader for command line arguments.
301 """A config loader for command line arguments.
302
302
303 As we add more command line based loaders, the common logic should go
303 As we add more command line based loaders, the common logic should go
304 here.
304 here.
305 """
305 """
306
306
307 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.+')
307 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.+')
308 macro_pattern = re.compile(r'\-\-\w+(\-\w)*')
308 macro_pattern = re.compile(r'\-\-\w+(\-\w)*')
309
309
310 class KeyValueConfigLoader(CommandLineConfigLoader):
310 class KeyValueConfigLoader(CommandLineConfigLoader):
311 """A config loader that loads key value pairs from the command line.
311 """A config loader that loads key value pairs from the command line.
312
312
313 This allows command line options to be gives in the following form::
313 This allows command line options to be gives in the following form::
314
314
315 ipython Global.profile="foo" InteractiveShell.autocall=False
315 ipython Global.profile="foo" InteractiveShell.autocall=False
316 """
316 """
317
317
318 def __init__(self, argv=None, shortnames=None, macros=None):
318 def __init__(self, argv=None, shortnames=None, macros=None):
319 """Create a key value pair config loader.
319 """Create a key value pair config loader.
320
320
321 Parameters
321 Parameters
322 ----------
322 ----------
323 argv : list
323 argv : list
324 A list that has the form of sys.argv[1:] which has unicode
324 A list that has the form of sys.argv[1:] which has unicode
325 elements of the form u"key=value". If this is None (default),
325 elements of the form u"key=value". If this is None (default),
326 then sys.argv[1:] will be used.
326 then sys.argv[1:] will be used.
327 shortnames : dict
327 shortnames : dict
328 A dict of aliases for configurable traits.
328 A dict of aliases for configurable traits.
329 Keys are the short aliases, Values are the resolved trait.
329 Keys are the short aliases, Values are the resolved trait.
330 Of the form: `{'shortname' : 'Configurable.trait'}`
330 Of the form: `{'shortname' : 'Configurable.trait'}`
331 macros : dict
331 macros : dict
332 A dict of macros, keyed by str name. Vaues can be Config objects,
332 A dict of macros, keyed by str name. Vaues can be Config objects,
333 dicts, or "key=value" strings. If Config or dict, when the macro
333 dicts, or "key=value" strings. If Config or dict, when the macro
334 is triggered, The macro is loaded as `self.config.update(m)`.
334 is triggered, The macro is loaded as `self.config.update(m)`.
335
335
336 Returns
336 Returns
337 -------
337 -------
338 config : Config
338 config : Config
339 The resulting Config object.
339 The resulting Config object.
340
340
341 Examples
341 Examples
342 --------
342 --------
343
343
344 >>> from IPython.config.loader import KeyValueConfigLoader
344 >>> from IPython.config.loader import KeyValueConfigLoader
345 >>> cl = KeyValueConfigLoader()
345 >>> cl = KeyValueConfigLoader()
346 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
346 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
347 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
347 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
348 """
348 """
349 if argv is None:
349 if argv is None:
350 argv = sys.argv[1:]
350 argv = sys.argv[1:]
351 self.argv = argv
351 self.argv = argv
352 self.shortnames = shortnames or {}
352 self.shortnames = shortnames or {}
353 self.macros = macros or {}
353 self.macros = macros or {}
354
354
355 def load_config(self, argv=None, shortnames=None, macros=None):
355 def load_config(self, argv=None, shortnames=None, macros=None):
356 """Parse the configuration and generate the Config object.
356 """Parse the configuration and generate the Config object.
357
357
358 Parameters
358 Parameters
359 ----------
359 ----------
360 argv : list, optional
360 argv : list, optional
361 A list that has the form of sys.argv[1:] which has unicode
361 A list that has the form of sys.argv[1:] which has unicode
362 elements of the form u"key=value". If this is None (default),
362 elements of the form u"key=value". If this is None (default),
363 then self.argv will be used.
363 then self.argv will be used.
364 shortnames : dict
364 shortnames : dict
365 A dict of aliases for configurable traits.
365 A dict of aliases for configurable traits.
366 Keys are the short aliases, Values are the resolved trait.
366 Keys are the short aliases, Values are the resolved trait.
367 Of the form: `{'shortname' : 'Configurable.trait'}`
367 Of the form: `{'shortname' : 'Configurable.trait'}`
368 macros : dict
368 macros : dict
369 A dict of macros, keyed by str name. Vaues can be Config objects,
369 A dict of macros, keyed by str name. Vaues can be Config objects,
370 dicts, or "key=value" strings. If Config or dict, when the macro
370 dicts, or "key=value" strings. If Config or dict, when the macro
371 is triggered, The macro is loaded as `self.config.update(m)`.
371 is triggered, The macro is loaded as `self.config.update(m)`.
372 """
372 """
373 from IPython.config.configurable import Configurable
373 from IPython.config.configurable import Configurable
374
374
375 self.clear()
375 self.clear()
376 if argv is None:
376 if argv is None:
377 argv = self.argv
377 argv = self.argv
378 if shortnames is None:
378 if shortnames is None:
379 shortnames = self.shortnames
379 shortnames = self.shortnames
380 if macros is None:
380 if macros is None:
381 macros = self.macros
381 macros = self.macros
382
382
383 for item in argv:
383 for item in argv:
384 if kv_pattern.match(item):
384 if kv_pattern.match(item):
385 lhs,rhs = item.split('=',1)
385 lhs,rhs = item.split('=',1)
386 # Substitute longnames for shortnames.
386 # Substitute longnames for shortnames.
387 if lhs in shortnames:
387 if lhs in shortnames:
388 lhs = shortnames[lhs]
388 lhs = shortnames[lhs]
389 exec_str = 'self.config.' + lhs + '=' + rhs
389 exec_str = 'self.config.' + lhs + '=' + rhs
390 try:
390 try:
391 # Try to see if regular Python syntax will work. This
391 # Try to see if regular Python syntax will work. This
392 # won't handle strings as the quote marks are removed
392 # won't handle strings as the quote marks are removed
393 # by the system shell.
393 # by the system shell.
394 exec exec_str in locals(), globals()
394 exec exec_str in locals(), globals()
395 except (NameError, SyntaxError):
395 except (NameError, SyntaxError):
396 # This case happens if the rhs is a string but without
396 # This case happens if the rhs is a string but without
397 # the quote marks. We add the quote marks and see if
397 # the quote marks. We add the quote marks and see if
398 # it succeeds. If it still fails, we let it raise.
398 # it succeeds. If it still fails, we let it raise.
399 exec_str = 'self.config.' + lhs + '="' + rhs + '"'
399 exec_str = 'self.config.' + lhs + '="' + rhs + '"'
400 exec exec_str in locals(), globals()
400 exec exec_str in locals(), globals()
401 elif macro_pattern.match(item):
401 elif macro_pattern.match(item):
402 # trim leading '--'
402 # trim leading '--'
403 m = item[2:]
403 m = item[2:]
404 macro = macros.get(m, None)
404 macro = macros.get(m, None)
405 if macro is None:
405 if macro is None:
406 raise ValueError("Unrecognized argument: %r"%item)
406 raise ValueError("Unrecognized argument: %r"%item)
407 macro = macros[m]
407 macro = macros[m]
408 if isinstance(macro, basestring):
408 if isinstance(macro, (dict, Configurable)):
409 # macro is simply a 'Class.trait=value' string
410 exec_str = 'self.config.' + macro
411 exec exec_str in locals(), globals()
412 elif isinstance(macro, (dict, Configurable)):
413 # update self.config with Config:
409 # update self.config with Config:
414 self.config.update(macros[m])
410 self.config.update(macros[m])
415 else:
411 else:
416 raise ValueError("Invalid macro: %r"%macro)
412 raise ValueError("Invalid macro: %r"%macro)
417 else:
413 else:
418 raise ValueError("Invalid argument: %r"%item)
414 raise ValueError("Invalid argument: %r"%item)
419 return self.config
415 return self.config
420
416
421 class ArgParseConfigLoader(CommandLineConfigLoader):
417 class ArgParseConfigLoader(CommandLineConfigLoader):
422 """A loader that uses the argparse module to load from the command line."""
418 """A loader that uses the argparse module to load from the command line."""
423
419
424 def __init__(self, argv=None, *parser_args, **parser_kw):
420 def __init__(self, argv=None, *parser_args, **parser_kw):
425 """Create a config loader for use with argparse.
421 """Create a config loader for use with argparse.
426
422
427 Parameters
423 Parameters
428 ----------
424 ----------
429
425
430 argv : optional, list
426 argv : optional, list
431 If given, used to read command-line arguments from, otherwise
427 If given, used to read command-line arguments from, otherwise
432 sys.argv[1:] is used.
428 sys.argv[1:] is used.
433
429
434 parser_args : tuple
430 parser_args : tuple
435 A tuple of positional arguments that will be passed to the
431 A tuple of positional arguments that will be passed to the
436 constructor of :class:`argparse.ArgumentParser`.
432 constructor of :class:`argparse.ArgumentParser`.
437
433
438 parser_kw : dict
434 parser_kw : dict
439 A tuple of keyword arguments that will be passed to the
435 A tuple of keyword arguments that will be passed to the
440 constructor of :class:`argparse.ArgumentParser`.
436 constructor of :class:`argparse.ArgumentParser`.
441
437
442 Returns
438 Returns
443 -------
439 -------
444 config : Config
440 config : Config
445 The resulting Config object.
441 The resulting Config object.
446 """
442 """
447 super(CommandLineConfigLoader, self).__init__()
443 super(CommandLineConfigLoader, self).__init__()
448 if argv == None:
444 if argv == None:
449 argv = sys.argv[1:]
445 argv = sys.argv[1:]
450 self.argv = argv
446 self.argv = argv
451 self.parser_args = parser_args
447 self.parser_args = parser_args
452 self.version = parser_kw.pop("version", None)
448 self.version = parser_kw.pop("version", None)
453 kwargs = dict(argument_default=argparse.SUPPRESS)
449 kwargs = dict(argument_default=argparse.SUPPRESS)
454 kwargs.update(parser_kw)
450 kwargs.update(parser_kw)
455 self.parser_kw = kwargs
451 self.parser_kw = kwargs
456
452
457 def load_config(self, argv=None):
453 def load_config(self, argv=None):
458 """Parse command line arguments and return as a Config object.
454 """Parse command line arguments and return as a Config object.
459
455
460 Parameters
456 Parameters
461 ----------
457 ----------
462
458
463 args : optional, list
459 args : optional, list
464 If given, a list with the structure of sys.argv[1:] to parse
460 If given, a list with the structure of sys.argv[1:] to parse
465 arguments from. If not given, the instance's self.argv attribute
461 arguments from. If not given, the instance's self.argv attribute
466 (given at construction time) is used."""
462 (given at construction time) is used."""
467 self.clear()
463 self.clear()
468 if argv is None:
464 if argv is None:
469 argv = self.argv
465 argv = self.argv
470 self._create_parser()
466 self._create_parser()
471 self._parse_args(argv)
467 self._parse_args(argv)
472 self._convert_to_config()
468 self._convert_to_config()
473 return self.config
469 return self.config
474
470
475 def get_extra_args(self):
471 def get_extra_args(self):
476 if hasattr(self, 'extra_args'):
472 if hasattr(self, 'extra_args'):
477 return self.extra_args
473 return self.extra_args
478 else:
474 else:
479 return []
475 return []
480
476
481 def _create_parser(self):
477 def _create_parser(self):
482 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
478 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
483 self._add_arguments()
479 self._add_arguments()
484
480
485 def _add_arguments(self):
481 def _add_arguments(self):
486 raise NotImplementedError("subclasses must implement _add_arguments")
482 raise NotImplementedError("subclasses must implement _add_arguments")
487
483
488 def _parse_args(self, args):
484 def _parse_args(self, args):
489 """self.parser->self.parsed_data"""
485 """self.parser->self.parsed_data"""
490 # decode sys.argv to support unicode command-line options
486 # decode sys.argv to support unicode command-line options
491 uargs = []
487 uargs = []
492 for a in args:
488 for a in args:
493 if isinstance(a, str):
489 if isinstance(a, str):
494 # don't decode if we already got unicode
490 # don't decode if we already got unicode
495 a = a.decode(sys.stdin.encoding or
491 a = a.decode(sys.stdin.encoding or
496 sys.getdefaultencoding())
492 sys.getdefaultencoding())
497 uargs.append(a)
493 uargs.append(a)
498 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
494 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
499
495
500 def _convert_to_config(self):
496 def _convert_to_config(self):
501 """self.parsed_data->self.config"""
497 """self.parsed_data->self.config"""
502 for k, v in vars(self.parsed_data).iteritems():
498 for k, v in vars(self.parsed_data).iteritems():
503 exec_str = 'self.config.' + k + '= v'
499 exec_str = 'self.config.' + k + '= v'
504 exec exec_str in locals(), globals()
500 exec exec_str in locals(), globals()
505
501
506
502
@@ -1,108 +1,108 b''
1 """
1 """
2 Tests for IPython.config.application.Application
2 Tests for IPython.config.application.Application
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2011 The IPython Development Team
10 # Copyright (C) 2008-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from unittest import TestCase
20 from unittest import TestCase
21
21
22 from IPython.config.configurable import Configurable
22 from IPython.config.configurable import Configurable
23
23
24 from IPython.config.application import (
24 from IPython.config.application import (
25 Application
25 Application
26 )
26 )
27
27
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Bool, Unicode, Int, Float, List
29 Bool, Unicode, Int, Float, List
30 )
30 )
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Code
33 # Code
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class Foo(Configurable):
36 class Foo(Configurable):
37
37
38 i = Int(0, config=True, help="The integer i.")
38 i = Int(0, config=True, help="The integer i.")
39 j = Int(1, config=True, help="The integer j.")
39 j = Int(1, config=True, help="The integer j.")
40 name = Unicode(u'Brian', config=True, help="First name.")
40 name = Unicode(u'Brian', config=True, help="First name.")
41
41
42
42
43 class Bar(Configurable):
43 class Bar(Configurable):
44
44
45 enabled = Bool(True, config=True, help="Enable bar.")
45 enabled = Bool(True, config=True, help="Enable bar.")
46
46
47
47
48 class MyApp(Application):
48 class MyApp(Application):
49
49
50 app_name = Unicode(u'myapp')
50 app_name = Unicode(u'myapp')
51 running = Bool(False, config=True, shortname="running",
51 running = Bool(False, config=True, shortname="running",
52 help="Is the app running?")
52 help="Is the app running?")
53 classes = List([Bar, Foo])
53 classes = List([Bar, Foo])
54 config_file = Unicode(u'', config=True, shortname="config_file",
54 config_file = Unicode(u'', config=True, shortname="config_file",
55 help="Load this config file")
55 help="Load this config file")
56
56
57 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name',
57 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name',
58 enabled='Bar.enabled', log_level='MyApp.log_level')
58 enabled='Bar.enabled', log_level='MyApp.log_level')
59
59
60 macros = dict(enable='Bar.enabled=True', disable='Bar.enabled=False')
60 macros = dict(enable={'Bar': {'enabled' : True}}, disable={'Bar': {'enabled' : False}})
61
61
62 macro_help = dict(
62 macro_help = dict(
63 enable="""Enable bar""",
63 enable="""Enable bar""",
64 disable="""Disable bar"""
64 disable="""Disable bar"""
65 )
65 )
66 def init_foo(self):
66 def init_foo(self):
67 self.foo = Foo(config=self.config)
67 self.foo = Foo(config=self.config)
68
68
69 def init_bar(self):
69 def init_bar(self):
70 self.bar = Bar(config=self.config)
70 self.bar = Bar(config=self.config)
71
71
72
72
73 class TestApplication(TestCase):
73 class TestApplication(TestCase):
74
74
75 def test_basic(self):
75 def test_basic(self):
76 app = MyApp()
76 app = MyApp()
77 self.assertEquals(app.app_name, u'myapp')
77 self.assertEquals(app.app_name, u'myapp')
78 self.assertEquals(app.running, False)
78 self.assertEquals(app.running, False)
79 self.assertEquals(app.classes, [MyApp,Bar,Foo])
79 self.assertEquals(app.classes, [MyApp,Bar,Foo])
80 self.assertEquals(app.config_file, u'')
80 self.assertEquals(app.config_file, u'')
81
81
82 def test_config(self):
82 def test_config(self):
83 app = MyApp()
83 app = MyApp()
84 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
84 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
85 config = app.config
85 config = app.config
86 self.assertEquals(config.Foo.i, 10)
86 self.assertEquals(config.Foo.i, 10)
87 self.assertEquals(config.Foo.j, 10)
87 self.assertEquals(config.Foo.j, 10)
88 self.assertEquals(config.Bar.enabled, False)
88 self.assertEquals(config.Bar.enabled, False)
89 self.assertEquals(config.MyApp.log_level,0)
89 self.assertEquals(config.MyApp.log_level,0)
90
90
91 def test_config_propagation(self):
91 def test_config_propagation(self):
92 app = MyApp()
92 app = MyApp()
93 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
93 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
94 app.init_foo()
94 app.init_foo()
95 app.init_bar()
95 app.init_bar()
96 self.assertEquals(app.foo.i, 10)
96 self.assertEquals(app.foo.i, 10)
97 self.assertEquals(app.foo.j, 10)
97 self.assertEquals(app.foo.j, 10)
98 self.assertEquals(app.bar.enabled, False)
98 self.assertEquals(app.bar.enabled, False)
99
99
100 def test_macro(self):
100 def test_macro(self):
101 app = MyApp()
101 app = MyApp()
102 app.parse_command_line(["--disable"])
102 app.parse_command_line(["--disable"])
103 app.init_bar()
103 app.init_bar()
104 self.assertEquals(app.bar.enabled, False)
104 self.assertEquals(app.bar.enabled, False)
105 app.parse_command_line(["--enable"])
105 app.parse_command_line(["--enable"])
106 app.init_bar()
106 app.init_bar()
107 self.assertEquals(app.bar.enabled, True)
107 self.assertEquals(app.bar.enabled, True)
108
108
@@ -1,97 +1,98 b''
1 """A simple example of how to use IPython.config.application.Application.
1 """A simple example of how to use IPython.config.application.Application.
2
2
3 This should serve as a simple example that shows how the IPython config
3 This should serve as a simple example that shows how the IPython config
4 system works. The main classes are:
4 system works. The main classes are:
5
5
6 * IPython.config.configurable.Configurable
6 * IPython.config.configurable.Configurable
7 * IPython.config.configurable.SingletonConfigurable
7 * IPython.config.configurable.SingletonConfigurable
8 * IPython.config.loader.Config
8 * IPython.config.loader.Config
9 * IPython.config.application.Application
9 * IPython.config.application.Application
10
10
11 To see the command line option help, run this program from the command line::
11 To see the command line option help, run this program from the command line::
12
12
13 $ python appconfig.py -h
13 $ python appconfig.py -h
14
14
15 To make one of your classes configurable (from the command line and config
15 To make one of your classes configurable (from the command line and config
16 files) inherit from Configurable and declare class attributes as traits (see
16 files) inherit from Configurable and declare class attributes as traits (see
17 classes Foo and Bar below). To make the traits configurable, you will need
17 classes Foo and Bar below). To make the traits configurable, you will need
18 to set the following options:
18 to set the following options:
19
19
20 * ``config``: set to ``True`` to make the attribute configurable.
20 * ``config``: set to ``True`` to make the attribute configurable.
21 * ``shortname``: by default, configurable attributes are set using the syntax
21 * ``shortname``: by default, configurable attributes are set using the syntax
22 "Classname.attributename". At the command line, this is a bit verbose, so
22 "Classname.attributename". At the command line, this is a bit verbose, so
23 we allow "shortnames" to be declared. Setting a shortname is optional, but
23 we allow "shortnames" to be declared. Setting a shortname is optional, but
24 when you do this, you can set the option at the command line using the
24 when you do this, you can set the option at the command line using the
25 syntax: "shortname=value".
25 syntax: "shortname=value".
26 * ``help``: set the help string to display a help message when the ``-h``
26 * ``help``: set the help string to display a help message when the ``-h``
27 option is given at the command line. The help string should be valid ReST.
27 option is given at the command line. The help string should be valid ReST.
28
28
29 When the config attribute of an Application is updated, it will fire all of
29 When the config attribute of an Application is updated, it will fire all of
30 the trait's events for all of the config=True attributes.
30 the trait's events for all of the config=True attributes.
31 """
31 """
32
32
33 import sys
33 import sys
34
34
35 from IPython.config.configurable import Configurable
35 from IPython.config.configurable import Configurable
36 from IPython.config.application import Application
36 from IPython.config.application import Application
37 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
38 Bool, Unicode, Int, Float, List
38 Bool, Unicode, Int, Float, List
39 )
39 )
40
40
41
41
42 class Foo(Configurable):
42 class Foo(Configurable):
43 """A class that has configurable, typed attributes.
43 """A class that has configurable, typed attributes.
44
44
45 """
45 """
46
46
47 i = Int(0, config=True, shortname='i', help="The integer i.")
47 i = Int(0, config=True, help="The integer i.")
48 j = Int(1, config=True, shortname='j', help="The integer j.")
48 j = Int(1, config=True, help="The integer j.")
49 name = Unicode(u'Brian', config=True, shortname='name', help="First name.")
49 name = Unicode(u'Brian', config=True, help="First name.")
50
50
51
51
52 class Bar(Configurable):
52 class Bar(Configurable):
53
53
54 enabled = Bool(True, config=True, shortname="enabled", help="Enable bar.")
54 enabled = Bool(True, config=True, help="Enable bar.")
55
55
56
56
57 class MyApp(Application):
57 class MyApp(Application):
58
58
59 app_name = Unicode(u'myapp')
59 app_name = Unicode(u'myapp')
60 running = Bool(False, config=True, shortname="running",
60 running = Bool(False, config=True,
61 help="Is the app running?")
61 help="Is the app running?")
62 classes = List([Bar, Foo])
62 classes = List([Bar, Foo])
63 config_file = Unicode(u'', config=True, shortname="config_file",
63 config_file = Unicode(u'', config=True,
64 help="Load this config file")
64 help="Load this config file")
65
65
66 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name',
66 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name', running='MyApp.running',
67 enabled='Bar.enabled')
67 enabled='Bar.enabled')
68
68
69 macros = dict(enable='Bar.enabled=True', disable='Bar.enabled=False')
69 macros = dict(enable={'Bar': {'enabled' : True}}, disable={'Bar': {'enabled' : False}})
70 macro_help = dict(
70 macro_help = dict(
71 enable="""Set Bar.enabled to True""",
71 enable="""Set Bar.enabled to True""",
72 disable="""Set Bar.enabled to False"""
72 disable="""Set Bar.enabled to False"""
73 )
73 )
74
74
75 def init_foo(self):
75 def init_foo(self):
76 # Pass config to other classes for them to inherit the config.
76 # Pass config to other classes for them to inherit the config.
77 self.foo = Foo(config=self.config)
77 self.foo = Foo(config=self.config)
78
78
79 def init_bar(self):
79 def init_bar(self):
80 # Pass config to other classes for them to inherit the config.
80 # Pass config to other classes for them to inherit the config.
81 self.bar = Bar(config=self.config)
81 self.bar = Bar(config=self.config)
82
82
83
83
84
84
85 def main():
85 def main():
86 app = MyApp()
86 app = MyApp()
87 app.parse_command_line()
87 app.parse_command_line()
88 if app.config_file:
88 if app.config_file:
89 app.load_config_file(app.config_file)
89 app.load_config_file(app.config_file)
90 app.init_foo()
90 app.init_foo()
91 app.init_bar()
91 app.init_bar()
92 print "app.config:"
92 print "app.config:"
93 print app.config
93 print app.config
94 print app.bar.enabled
94
95
95
96
96 if __name__ == "__main__":
97 if __name__ == "__main__":
97 main()
98 main()
General Comments 0
You need to be logged in to leave comments. Login now