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