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