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