##// END OF EJS Templates
Merge branch 'pypy-compat' of https://github.com/takluyver/ipython into takluyver-pypy-compat
Thomas Kluyver -
r3508:38550298 merge
parent child Browse files
Show More
@@ -1,384 +1,389 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # coding: utf-8
2 # coding: utf-8
3 """A simple configuration system.
3 """A simple configuration system.
4
4
5 Authors
5 Authors
6 -------
6 -------
7 * Brian Granger
7 * Brian Granger
8 * Fernando Perez
8 * Fernando Perez
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2009 The IPython Development Team
12 # Copyright (C) 2008-2009 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import __builtin__
22 import __builtin__
23 import os
23 import os
24 import sys
24 import sys
25
25
26 from IPython.external import argparse
26 from IPython.external import argparse
27 from IPython.utils.path import filefind
27 from IPython.utils.path import filefind
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Exceptions
30 # Exceptions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33
33
34 class ConfigError(Exception):
34 class ConfigError(Exception):
35 pass
35 pass
36
36
37
37
38 class ConfigLoaderError(ConfigError):
38 class ConfigLoaderError(ConfigError):
39 pass
39 pass
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Argparse fix
42 # Argparse fix
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 # Unfortunately argparse by default prints help messages to stderr instead of
45 # Unfortunately argparse by default prints help messages to stderr instead of
46 # stdout. This makes it annoying to capture long help screens at the command
46 # stdout. This makes it annoying to capture long help screens at the command
47 # line, since one must know how to pipe stderr, which many users don't know how
47 # line, since one must know how to pipe stderr, which many users don't know how
48 # to do. So we override the print_help method with one that defaults to
48 # to do. So we override the print_help method with one that defaults to
49 # stdout and use our class instead.
49 # stdout and use our class instead.
50
50
51 class ArgumentParser(argparse.ArgumentParser):
51 class ArgumentParser(argparse.ArgumentParser):
52 """Simple argparse subclass that prints help to stdout by default."""
52 """Simple argparse subclass that prints help to stdout by default."""
53
53
54 def print_help(self, file=None):
54 def print_help(self, file=None):
55 if file is None:
55 if file is None:
56 file = sys.stdout
56 file = sys.stdout
57 return super(ArgumentParser, self).print_help(file)
57 return super(ArgumentParser, self).print_help(file)
58
58
59 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
59 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Config class for holding config information
62 # Config class for holding config information
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65
65
66 class Config(dict):
66 class Config(dict):
67 """An attribute based dict that can do smart merges."""
67 """An attribute based dict that can do smart merges."""
68
68
69 def __init__(self, *args, **kwds):
69 def __init__(self, *args, **kwds):
70 dict.__init__(self, *args, **kwds)
70 dict.__init__(self, *args, **kwds)
71 # This sets self.__dict__ = self, but it has to be done this way
71 # This sets self.__dict__ = self, but it has to be done this way
72 # because we are also overriding __setattr__.
72 # because we are also overriding __setattr__.
73 dict.__setattr__(self, '__dict__', self)
73 dict.__setattr__(self, '__dict__', self)
74
74
75 def _merge(self, other):
75 def _merge(self, other):
76 to_update = {}
76 to_update = {}
77 for k, v in other.iteritems():
77 for k, v in other.iteritems():
78 if not self.has_key(k):
78 if not self.has_key(k):
79 to_update[k] = v
79 to_update[k] = v
80 else: # I have this key
80 else: # I have this key
81 if isinstance(v, Config):
81 if isinstance(v, Config):
82 # Recursively merge common sub Configs
82 # Recursively merge common sub Configs
83 self[k]._merge(v)
83 self[k]._merge(v)
84 else:
84 else:
85 # Plain updates for non-Configs
85 # Plain updates for non-Configs
86 to_update[k] = v
86 to_update[k] = v
87
87
88 self.update(to_update)
88 self.update(to_update)
89
89
90 def _is_section_key(self, key):
90 def _is_section_key(self, key):
91 if key[0].upper()==key[0] and not key.startswith('_'):
91 if key[0].upper()==key[0] and not key.startswith('_'):
92 return True
92 return True
93 else:
93 else:
94 return False
94 return False
95
95
96 def __contains__(self, key):
96 def __contains__(self, key):
97 if self._is_section_key(key):
97 if self._is_section_key(key):
98 return True
98 return True
99 else:
99 else:
100 return super(Config, self).__contains__(key)
100 return super(Config, self).__contains__(key)
101 # .has_key is deprecated for dictionaries.
101 # .has_key is deprecated for dictionaries.
102 has_key = __contains__
102 has_key = __contains__
103
103
104 def _has_section(self, key):
104 def _has_section(self, key):
105 if self._is_section_key(key):
105 if self._is_section_key(key):
106 if super(Config, self).__contains__(key):
106 if super(Config, self).__contains__(key):
107 return True
107 return True
108 return False
108 return False
109
109
110 def copy(self):
110 def copy(self):
111 return type(self)(dict.copy(self))
111 return type(self)(dict.copy(self))
112
112
113 def __copy__(self):
113 def __copy__(self):
114 return self.copy()
114 return self.copy()
115
115
116 def __deepcopy__(self, memo):
116 def __deepcopy__(self, memo):
117 import copy
117 import copy
118 return type(self)(copy.deepcopy(self.items()))
118 return type(self)(copy.deepcopy(self.items()))
119
119
120 def __getitem__(self, key):
120 def __getitem__(self, key):
121 # We cannot use directly self._is_section_key, because it triggers
122 # infinite recursion on top of PyPy. Instead, we manually fish the
123 # bound method.
124 is_section_key = self.__class__._is_section_key.__get__(self)
125
121 # Because we use this for an exec namespace, we need to delegate
126 # Because we use this for an exec namespace, we need to delegate
122 # the lookup of names in __builtin__ to itself. This means
127 # the lookup of names in __builtin__ to itself. This means
123 # that you can't have section or attribute names that are
128 # that you can't have section or attribute names that are
124 # builtins.
129 # builtins.
125 try:
130 try:
126 return getattr(__builtin__, key)
131 return getattr(__builtin__, key)
127 except AttributeError:
132 except AttributeError:
128 pass
133 pass
129 if self._is_section_key(key):
134 if is_section_key(key):
130 try:
135 try:
131 return dict.__getitem__(self, key)
136 return dict.__getitem__(self, key)
132 except KeyError:
137 except KeyError:
133 c = Config()
138 c = Config()
134 dict.__setitem__(self, key, c)
139 dict.__setitem__(self, key, c)
135 return c
140 return c
136 else:
141 else:
137 return dict.__getitem__(self, key)
142 return dict.__getitem__(self, key)
138
143
139 def __setitem__(self, key, value):
144 def __setitem__(self, key, value):
140 # Don't allow names in __builtin__ to be modified.
145 # Don't allow names in __builtin__ to be modified.
141 if hasattr(__builtin__, key):
146 if hasattr(__builtin__, key):
142 raise ConfigError('Config variable names cannot have the same name '
147 raise ConfigError('Config variable names cannot have the same name '
143 'as a Python builtin: %s' % key)
148 'as a Python builtin: %s' % key)
144 if self._is_section_key(key):
149 if self._is_section_key(key):
145 if not isinstance(value, Config):
150 if not isinstance(value, Config):
146 raise ValueError('values whose keys begin with an uppercase '
151 raise ValueError('values whose keys begin with an uppercase '
147 'char must be Config instances: %r, %r' % (key, value))
152 'char must be Config instances: %r, %r' % (key, value))
148 else:
153 else:
149 dict.__setitem__(self, key, value)
154 dict.__setitem__(self, key, value)
150
155
151 def __getattr__(self, key):
156 def __getattr__(self, key):
152 try:
157 try:
153 return self.__getitem__(key)
158 return self.__getitem__(key)
154 except KeyError, e:
159 except KeyError, e:
155 raise AttributeError(e)
160 raise AttributeError(e)
156
161
157 def __setattr__(self, key, value):
162 def __setattr__(self, key, value):
158 try:
163 try:
159 self.__setitem__(key, value)
164 self.__setitem__(key, value)
160 except KeyError, e:
165 except KeyError, e:
161 raise AttributeError(e)
166 raise AttributeError(e)
162
167
163 def __delattr__(self, key):
168 def __delattr__(self, key):
164 try:
169 try:
165 dict.__delitem__(self, key)
170 dict.__delitem__(self, key)
166 except KeyError, e:
171 except KeyError, e:
167 raise AttributeError(e)
172 raise AttributeError(e)
168
173
169
174
170 #-----------------------------------------------------------------------------
175 #-----------------------------------------------------------------------------
171 # Config loading classes
176 # Config loading classes
172 #-----------------------------------------------------------------------------
177 #-----------------------------------------------------------------------------
173
178
174
179
175 class ConfigLoader(object):
180 class ConfigLoader(object):
176 """A object for loading configurations from just about anywhere.
181 """A object for loading configurations from just about anywhere.
177
182
178 The resulting configuration is packaged as a :class:`Struct`.
183 The resulting configuration is packaged as a :class:`Struct`.
179
184
180 Notes
185 Notes
181 -----
186 -----
182 A :class:`ConfigLoader` does one thing: load a config from a source
187 A :class:`ConfigLoader` does one thing: load a config from a source
183 (file, command line arguments) and returns the data as a :class:`Struct`.
188 (file, command line arguments) and returns the data as a :class:`Struct`.
184 There are lots of things that :class:`ConfigLoader` does not do. It does
189 There are lots of things that :class:`ConfigLoader` does not do. It does
185 not implement complex logic for finding config files. It does not handle
190 not implement complex logic for finding config files. It does not handle
186 default values or merge multiple configs. These things need to be
191 default values or merge multiple configs. These things need to be
187 handled elsewhere.
192 handled elsewhere.
188 """
193 """
189
194
190 def __init__(self):
195 def __init__(self):
191 """A base class for config loaders.
196 """A base class for config loaders.
192
197
193 Examples
198 Examples
194 --------
199 --------
195
200
196 >>> cl = ConfigLoader()
201 >>> cl = ConfigLoader()
197 >>> config = cl.load_config()
202 >>> config = cl.load_config()
198 >>> config
203 >>> config
199 {}
204 {}
200 """
205 """
201 self.clear()
206 self.clear()
202
207
203 def clear(self):
208 def clear(self):
204 self.config = Config()
209 self.config = Config()
205
210
206 def load_config(self):
211 def load_config(self):
207 """Load a config from somewhere, return a :class:`Config` instance.
212 """Load a config from somewhere, return a :class:`Config` instance.
208
213
209 Usually, this will cause self.config to be set and then returned.
214 Usually, this will cause self.config to be set and then returned.
210 However, in most cases, :meth:`ConfigLoader.clear` should be called
215 However, in most cases, :meth:`ConfigLoader.clear` should be called
211 to erase any previous state.
216 to erase any previous state.
212 """
217 """
213 self.clear()
218 self.clear()
214 return self.config
219 return self.config
215
220
216
221
217 class FileConfigLoader(ConfigLoader):
222 class FileConfigLoader(ConfigLoader):
218 """A base class for file based configurations.
223 """A base class for file based configurations.
219
224
220 As we add more file based config loaders, the common logic should go
225 As we add more file based config loaders, the common logic should go
221 here.
226 here.
222 """
227 """
223 pass
228 pass
224
229
225
230
226 class PyFileConfigLoader(FileConfigLoader):
231 class PyFileConfigLoader(FileConfigLoader):
227 """A config loader for pure python files.
232 """A config loader for pure python files.
228
233
229 This calls execfile on a plain python file and looks for attributes
234 This calls execfile on a plain python file and looks for attributes
230 that are all caps. These attribute are added to the config Struct.
235 that are all caps. These attribute are added to the config Struct.
231 """
236 """
232
237
233 def __init__(self, filename, path=None):
238 def __init__(self, filename, path=None):
234 """Build a config loader for a filename and path.
239 """Build a config loader for a filename and path.
235
240
236 Parameters
241 Parameters
237 ----------
242 ----------
238 filename : str
243 filename : str
239 The file name of the config file.
244 The file name of the config file.
240 path : str, list, tuple
245 path : str, list, tuple
241 The path to search for the config file on, or a sequence of
246 The path to search for the config file on, or a sequence of
242 paths to try in order.
247 paths to try in order.
243 """
248 """
244 super(PyFileConfigLoader, self).__init__()
249 super(PyFileConfigLoader, self).__init__()
245 self.filename = filename
250 self.filename = filename
246 self.path = path
251 self.path = path
247 self.full_filename = ''
252 self.full_filename = ''
248 self.data = None
253 self.data = None
249
254
250 def load_config(self):
255 def load_config(self):
251 """Load the config from a file and return it as a Struct."""
256 """Load the config from a file and return it as a Struct."""
252 self.clear()
257 self.clear()
253 self._find_file()
258 self._find_file()
254 self._read_file_as_dict()
259 self._read_file_as_dict()
255 self._convert_to_config()
260 self._convert_to_config()
256 return self.config
261 return self.config
257
262
258 def _find_file(self):
263 def _find_file(self):
259 """Try to find the file by searching the paths."""
264 """Try to find the file by searching the paths."""
260 self.full_filename = filefind(self.filename, self.path)
265 self.full_filename = filefind(self.filename, self.path)
261
266
262 def _read_file_as_dict(self):
267 def _read_file_as_dict(self):
263 """Load the config file into self.config, with recursive loading."""
268 """Load the config file into self.config, with recursive loading."""
264 # This closure is made available in the namespace that is used
269 # This closure is made available in the namespace that is used
265 # to exec the config file. This allows users to call
270 # to exec the config file. This allows users to call
266 # load_subconfig('myconfig.py') to load config files recursively.
271 # load_subconfig('myconfig.py') to load config files recursively.
267 # It needs to be a closure because it has references to self.path
272 # It needs to be a closure because it has references to self.path
268 # and self.config. The sub-config is loaded with the same path
273 # and self.config. The sub-config is loaded with the same path
269 # as the parent, but it uses an empty config which is then merged
274 # as the parent, but it uses an empty config which is then merged
270 # with the parents.
275 # with the parents.
271 def load_subconfig(fname):
276 def load_subconfig(fname):
272 loader = PyFileConfigLoader(fname, self.path)
277 loader = PyFileConfigLoader(fname, self.path)
273 try:
278 try:
274 sub_config = loader.load_config()
279 sub_config = loader.load_config()
275 except IOError:
280 except IOError:
276 # Pass silently if the sub config is not there. This happens
281 # Pass silently if the sub config is not there. This happens
277 # when a user us using a profile, but not the default config.
282 # when a user us using a profile, but not the default config.
278 pass
283 pass
279 else:
284 else:
280 self.config._merge(sub_config)
285 self.config._merge(sub_config)
281
286
282 # Again, this needs to be a closure and should be used in config
287 # Again, this needs to be a closure and should be used in config
283 # files to get the config being loaded.
288 # files to get the config being loaded.
284 def get_config():
289 def get_config():
285 return self.config
290 return self.config
286
291
287 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
292 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
288 fs_encoding = sys.getfilesystemencoding() or 'ascii'
293 fs_encoding = sys.getfilesystemencoding() or 'ascii'
289 conf_filename = self.full_filename.encode(fs_encoding)
294 conf_filename = self.full_filename.encode(fs_encoding)
290 execfile(conf_filename, namespace)
295 execfile(conf_filename, namespace)
291
296
292 def _convert_to_config(self):
297 def _convert_to_config(self):
293 if self.data is None:
298 if self.data is None:
294 ConfigLoaderError('self.data does not exist')
299 ConfigLoaderError('self.data does not exist')
295
300
296
301
297 class CommandLineConfigLoader(ConfigLoader):
302 class CommandLineConfigLoader(ConfigLoader):
298 """A config loader for command line arguments.
303 """A config loader for command line arguments.
299
304
300 As we add more command line based loaders, the common logic should go
305 As we add more command line based loaders, the common logic should go
301 here.
306 here.
302 """
307 """
303
308
304
309
305 class ArgParseConfigLoader(CommandLineConfigLoader):
310 class ArgParseConfigLoader(CommandLineConfigLoader):
306
311
307 def __init__(self, argv=None, *parser_args, **parser_kw):
312 def __init__(self, argv=None, *parser_args, **parser_kw):
308 """Create a config loader for use with argparse.
313 """Create a config loader for use with argparse.
309
314
310 Parameters
315 Parameters
311 ----------
316 ----------
312
317
313 argv : optional, list
318 argv : optional, list
314 If given, used to read command-line arguments from, otherwise
319 If given, used to read command-line arguments from, otherwise
315 sys.argv[1:] is used.
320 sys.argv[1:] is used.
316
321
317 parser_args : tuple
322 parser_args : tuple
318 A tuple of positional arguments that will be passed to the
323 A tuple of positional arguments that will be passed to the
319 constructor of :class:`argparse.ArgumentParser`.
324 constructor of :class:`argparse.ArgumentParser`.
320
325
321 parser_kw : dict
326 parser_kw : dict
322 A tuple of keyword arguments that will be passed to the
327 A tuple of keyword arguments that will be passed to the
323 constructor of :class:`argparse.ArgumentParser`.
328 constructor of :class:`argparse.ArgumentParser`.
324 """
329 """
325 super(CommandLineConfigLoader, self).__init__()
330 super(CommandLineConfigLoader, self).__init__()
326 if argv == None:
331 if argv == None:
327 argv = sys.argv[1:]
332 argv = sys.argv[1:]
328 self.argv = argv
333 self.argv = argv
329 self.parser_args = parser_args
334 self.parser_args = parser_args
330 self.version = parser_kw.pop("version", None)
335 self.version = parser_kw.pop("version", None)
331 kwargs = dict(argument_default=argparse.SUPPRESS)
336 kwargs = dict(argument_default=argparse.SUPPRESS)
332 kwargs.update(parser_kw)
337 kwargs.update(parser_kw)
333 self.parser_kw = kwargs
338 self.parser_kw = kwargs
334
339
335 def load_config(self, args=None):
340 def load_config(self, args=None):
336 """Parse command line arguments and return as a Struct.
341 """Parse command line arguments and return as a Struct.
337
342
338 Parameters
343 Parameters
339 ----------
344 ----------
340
345
341 args : optional, list
346 args : optional, list
342 If given, a list with the structure of sys.argv[1:] to parse
347 If given, a list with the structure of sys.argv[1:] to parse
343 arguments from. If not given, the instance's self.argv attribute
348 arguments from. If not given, the instance's self.argv attribute
344 (given at construction time) is used."""
349 (given at construction time) is used."""
345 self.clear()
350 self.clear()
346 if args is None:
351 if args is None:
347 args = self.argv
352 args = self.argv
348 self._create_parser()
353 self._create_parser()
349 self._parse_args(args)
354 self._parse_args(args)
350 self._convert_to_config()
355 self._convert_to_config()
351 return self.config
356 return self.config
352
357
353 def get_extra_args(self):
358 def get_extra_args(self):
354 if hasattr(self, 'extra_args'):
359 if hasattr(self, 'extra_args'):
355 return self.extra_args
360 return self.extra_args
356 else:
361 else:
357 return []
362 return []
358
363
359 def _create_parser(self):
364 def _create_parser(self):
360 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
365 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
361 self._add_arguments()
366 self._add_arguments()
362
367
363 def _add_arguments(self):
368 def _add_arguments(self):
364 raise NotImplementedError("subclasses must implement _add_arguments")
369 raise NotImplementedError("subclasses must implement _add_arguments")
365
370
366 def _parse_args(self, args):
371 def _parse_args(self, args):
367 """self.parser->self.parsed_data"""
372 """self.parser->self.parsed_data"""
368 # decode sys.argv to support unicode command-line options
373 # decode sys.argv to support unicode command-line options
369 uargs = []
374 uargs = []
370 for a in args:
375 for a in args:
371 if isinstance(a, str):
376 if isinstance(a, str):
372 # don't decode if we already got unicode
377 # don't decode if we already got unicode
373 a = a.decode(sys.stdin.encoding or
378 a = a.decode(sys.stdin.encoding or
374 sys.getdefaultencoding())
379 sys.getdefaultencoding())
375 uargs.append(a)
380 uargs.append(a)
376 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
381 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
377
382
378 def _convert_to_config(self):
383 def _convert_to_config(self):
379 """self.parsed_data->self.config"""
384 """self.parsed_data->self.config"""
380 for k, v in vars(self.parsed_data).iteritems():
385 for k, v in vars(self.parsed_data).iteritems():
381 exec_str = 'self.config.' + k + '= v'
386 exec_str = 'self.config.' + k + '= v'
382 exec exec_str in locals(), globals()
387 exec exec_str in locals(), globals()
383
388
384
389
General Comments 0
You need to be logged in to leave comments. Login now