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