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