##// END OF EJS Templates
Merging with upstream trunk.
Fernando Perez -
r2405:d5ed6f77 merge
parent child Browse files
Show More
@@ -1,373 +1,373 b''
1 1 # coding: utf-8
2 2 """A simple configuration system.
3 3
4 4 Authors
5 5 -------
6 6 * Brian Granger
7 7 * Fernando Perez
8 8 """
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2008-2009 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__
22 22 import os
23 23 import sys
24 24
25 25 from IPython.external import argparse
26 26 from IPython.utils.genutils import filefind
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Exceptions
30 30 #-----------------------------------------------------------------------------
31 31
32 32
33 33 class ConfigError(Exception):
34 34 pass
35 35
36 36
37 37 class ConfigLoaderError(ConfigError):
38 38 pass
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Argparse fix
42 42 #-----------------------------------------------------------------------------
43 43 # Unfortunately argparse by default prints help messages to stderr instead of
44 44 # stdout. This makes it annoying to capture long help screens at the command
45 45 # line, since one must know how to pipe stderr, which many users don't know how
46 46 # to do. So we override the print_help method with one that defaults to
47 47 # stdout and use our class instead.
48 48
49 49 class ArgumentParser(argparse.ArgumentParser):
50 50 """Simple argparse subclass that prints help to stdout by default."""
51 51
52 52 def print_help(self, file=None):
53 53 if file is None:
54 54 file = sys.stdout
55 55 return super(ArgumentParser, self).print_help(file)
56 56
57 57 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
58 58
59 59 #-----------------------------------------------------------------------------
60 60 # Config class for holding config information
61 61 #-----------------------------------------------------------------------------
62 62
63 63
64 64 class Config(dict):
65 65 """An attribute based dict that can do smart merges."""
66 66
67 67 def __init__(self, *args, **kwds):
68 68 dict.__init__(self, *args, **kwds)
69 69 # This sets self.__dict__ = self, but it has to be done this way
70 70 # because we are also overriding __setattr__.
71 71 dict.__setattr__(self, '__dict__', self)
72 72
73 73 def _merge(self, other):
74 74 to_update = {}
75 75 for k, v in other.items():
76 76 if not self.has_key(k):
77 77 to_update[k] = v
78 78 else: # I have this key
79 79 if isinstance(v, Config):
80 80 # Recursively merge common sub Configs
81 81 self[k]._merge(v)
82 82 else:
83 83 # Plain updates for non-Configs
84 84 to_update[k] = v
85 85
86 86 self.update(to_update)
87 87
88 88 def _is_section_key(self, key):
89 89 if key[0].upper()==key[0] and not key.startswith('_'):
90 90 return True
91 91 else:
92 92 return False
93 93
94 94 def has_key(self, key):
95 95 if self._is_section_key(key):
96 96 return True
97 97 else:
98 98 return dict.has_key(self, key)
99 99
100 100 def _has_section(self, key):
101 101 if self._is_section_key(key):
102 102 if dict.has_key(self, key):
103 103 return True
104 104 return False
105 105
106 106 def copy(self):
107 107 return type(self)(dict.copy(self))
108 108
109 109 def __copy__(self):
110 110 return self.copy()
111 111
112 112 def __deepcopy__(self, memo):
113 113 import copy
114 114 return type(self)(copy.deepcopy(self.items()))
115 115
116 116 def __getitem__(self, key):
117 117 # Because we use this for an exec namespace, we need to delegate
118 118 # the lookup of names in __builtin__ to itself. This means
119 119 # that you can't have section or attribute names that are
120 120 # builtins.
121 121 try:
122 122 return getattr(__builtin__, key)
123 123 except AttributeError:
124 124 pass
125 125 if self._is_section_key(key):
126 126 try:
127 127 return dict.__getitem__(self, key)
128 128 except KeyError:
129 129 c = Config()
130 130 dict.__setitem__(self, key, c)
131 131 return c
132 132 else:
133 133 return dict.__getitem__(self, key)
134 134
135 135 def __setitem__(self, key, value):
136 136 # Don't allow names in __builtin__ to be modified.
137 137 if hasattr(__builtin__, key):
138 138 raise ConfigError('Config variable names cannot have the same name '
139 139 'as a Python builtin: %s' % key)
140 140 if self._is_section_key(key):
141 141 if not isinstance(value, Config):
142 142 raise ValueError('values whose keys begin with an uppercase '
143 143 'char must be Config instances: %r, %r' % (key, value))
144 144 else:
145 145 dict.__setitem__(self, key, value)
146 146
147 147 def __getattr__(self, key):
148 148 try:
149 149 return self.__getitem__(key)
150 150 except KeyError, e:
151 151 raise AttributeError(e)
152 152
153 153 def __setattr__(self, key, value):
154 154 try:
155 155 self.__setitem__(key, value)
156 156 except KeyError, e:
157 157 raise AttributeError(e)
158 158
159 159 def __delattr__(self, key):
160 160 try:
161 161 dict.__delitem__(self, key)
162 162 except KeyError, e:
163 163 raise AttributeError(e)
164 164
165 165
166 166 #-----------------------------------------------------------------------------
167 167 # Config loading classes
168 168 #-----------------------------------------------------------------------------
169 169
170 170
171 171 class ConfigLoader(object):
172 172 """A object for loading configurations from just about anywhere.
173 173
174 174 The resulting configuration is packaged as a :class:`Struct`.
175 175
176 176 Notes
177 177 -----
178 178 A :class:`ConfigLoader` does one thing: load a config from a source
179 179 (file, command line arguments) and returns the data as a :class:`Struct`.
180 180 There are lots of things that :class:`ConfigLoader` does not do. It does
181 181 not implement complex logic for finding config files. It does not handle
182 182 default values or merge multiple configs. These things need to be
183 183 handled elsewhere.
184 184 """
185 185
186 186 def __init__(self):
187 187 """A base class for config loaders.
188 188
189 189 Examples
190 190 --------
191 191
192 192 >>> cl = ConfigLoader()
193 193 >>> config = cl.load_config()
194 194 >>> config
195 195 {}
196 196 """
197 197 self.clear()
198 198
199 199 def clear(self):
200 200 self.config = Config()
201 201
202 202 def load_config(self):
203 203 """Load a config from somewhere, return a Struct.
204 204
205 205 Usually, this will cause self.config to be set and then returned.
206 206 """
207 207 return self.config
208 208
209 209
210 210 class FileConfigLoader(ConfigLoader):
211 211 """A base class for file based configurations.
212 212
213 213 As we add more file based config loaders, the common logic should go
214 214 here.
215 215 """
216 216 pass
217 217
218 218
219 219 class PyFileConfigLoader(FileConfigLoader):
220 220 """A config loader for pure python files.
221 221
222 222 This calls execfile on a plain python file and looks for attributes
223 223 that are all caps. These attribute are added to the config Struct.
224 224 """
225 225
226 226 def __init__(self, filename, path=None):
227 227 """Build a config loader for a filename and path.
228 228
229 229 Parameters
230 230 ----------
231 231 filename : str
232 232 The file name of the config file.
233 233 path : str, list, tuple
234 234 The path to search for the config file on, or a sequence of
235 235 paths to try in order.
236 236 """
237 237 super(PyFileConfigLoader, self).__init__()
238 238 self.filename = filename
239 239 self.path = path
240 240 self.full_filename = ''
241 241 self.data = None
242 242
243 243 def load_config(self):
244 244 """Load the config from a file and return it as a Struct."""
245 245 self._find_file()
246 246 self._read_file_as_dict()
247 247 self._convert_to_config()
248 248 return self.config
249 249
250 250 def _find_file(self):
251 251 """Try to find the file by searching the paths."""
252 252 self.full_filename = filefind(self.filename, self.path)
253 253
254 254 def _read_file_as_dict(self):
255 255 """Load the config file into self.config, with recursive loading."""
256 256 # This closure is made available in the namespace that is used
257 257 # to exec the config file. This allows users to call
258 258 # load_subconfig('myconfig.py') to load config files recursively.
259 259 # It needs to be a closure because it has references to self.path
260 260 # and self.config. The sub-config is loaded with the same path
261 261 # as the parent, but it uses an empty config which is then merged
262 262 # with the parents.
263 263 def load_subconfig(fname):
264 264 loader = PyFileConfigLoader(fname, self.path)
265 265 try:
266 266 sub_config = loader.load_config()
267 267 except IOError:
268 268 # Pass silently if the sub config is not there. This happens
269 269 # when a user us using a profile, but not the default config.
270 270 pass
271 271 else:
272 272 self.config._merge(sub_config)
273 273
274 274 # Again, this needs to be a closure and should be used in config
275 275 # files to get the config being loaded.
276 276 def get_config():
277 277 return self.config
278 278
279 279 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
280 280 execfile(self.full_filename, namespace)
281 281
282 282 def _convert_to_config(self):
283 283 if self.data is None:
284 284 ConfigLoaderError('self.data does not exist')
285 285
286 286
287 287 class CommandLineConfigLoader(ConfigLoader):
288 288 """A config loader for command line arguments.
289 289
290 290 As we add more command line based loaders, the common logic should go
291 291 here.
292 292 """
293 293
294 294
295 class NoConfigDefault(object): pass
296 NoConfigDefault = NoConfigDefault()
295 class __NoConfigDefault(object): pass
296 NoConfigDefault = __NoConfigDefault()
297 297
298 298
299 299 class ArgParseConfigLoader(CommandLineConfigLoader):
300 300
301 301 def __init__(self, argv=None, arguments=(), *args, **kw):
302 302 """Create a config loader for use with argparse.
303 303
304 304 With the exception of ``argv`` and ``arguments``, other args and kwargs
305 305 arguments here are passed onto the constructor of
306 306 :class:`argparse.ArgumentParser`.
307 307
308 308 Parameters
309 309 ----------
310 310
311 311 argv : optional, list
312 312 If given, used to read command-line arguments from, otherwise
313 313 sys.argv[1:] is used.
314 314
315 315 arguments : optional, tuple
316 316 Description of valid command-line arguments, to be called in sequence
317 317 with parser.add_argument() to configure the parser.
318 318 """
319 319 super(CommandLineConfigLoader, self).__init__()
320 320 if argv == None:
321 321 argv = sys.argv[1:]
322 322 self.argv = argv
323 323 self.arguments = arguments
324 324 self.args = args
325 325 self.kw = kw
326 326
327 327 def load_config(self, args=None):
328 328 """Parse command line arguments and return as a Struct.
329 329
330 330 Parameters
331 331 ----------
332 332
333 333 args : optional, list
334 334 If given, a list with the structure of sys.argv[1:] to parse arguments
335 335 from. If not given, the instance's self.argv attribute (given at
336 336 construction time) is used."""
337 337
338 338 if args is None:
339 339 args = self.argv
340 340 self._create_parser()
341 341 self._parse_args(args)
342 342 self._convert_to_config()
343 343 return self.config
344 344
345 345 def get_extra_args(self):
346 346 if hasattr(self, 'extra_args'):
347 347 return self.extra_args
348 348 else:
349 349 return []
350 350
351 351 def _create_parser(self):
352 352 self.parser = ArgumentParser(*self.args, **self.kw)
353 353 self._add_arguments()
354 354 self._add_other_arguments()
355 355
356 356 def _add_other_arguments(self):
357 357 pass
358 358
359 359 def _add_arguments(self):
360 360 for argument in self.arguments:
361 361 argument[1].setdefault('default', NoConfigDefault)
362 362 self.parser.add_argument(*argument[0],**argument[1])
363 363
364 364 def _parse_args(self, args):
365 365 """self.parser->self.parsed_data"""
366 366 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
367 367
368 368 def _convert_to_config(self):
369 369 """self.parsed_data->self.config"""
370 370 for k, v in vars(self.parsed_data).items():
371 371 if v is not NoConfigDefault:
372 372 exec_str = 'self.config.' + k + '= v'
373 373 exec exec_str in locals(), globals()
@@ -1,118 +1,118 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A context manager for managing things injected into :mod:`__builtin__`.
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2009 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import __builtin__
23 23
24 24 from IPython.core.component import Component
25 25 from IPython.core.quitter import Quitter
26 26
27 27 from IPython.utils.autoattr import auto_attr
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes and functions
31 31 #-----------------------------------------------------------------------------
32 32
33 33
34 class BuiltinUndefined(object): pass
35 BuiltinUndefined = BuiltinUndefined()
34 class __BuiltinUndefined(object): pass
35 BuiltinUndefined = __BuiltinUndefined()
36 36
37 37
38 38 class BuiltinTrap(Component):
39 39
40 40 def __init__(self, parent):
41 41 super(BuiltinTrap, self).__init__(parent, None, None)
42 42 self._orig_builtins = {}
43 43 # We define this to track if a single BuiltinTrap is nested.
44 44 # Only turn off the trap when the outermost call to __exit__ is made.
45 45 self._nested_level = 0
46 46
47 47 @auto_attr
48 48 def shell(self):
49 49 return Component.get_instances(
50 50 root=self.root,
51 51 klass='IPython.core.iplib.InteractiveShell')[0]
52 52
53 53 def __enter__(self):
54 54 if self._nested_level == 0:
55 55 self.set()
56 56 self._nested_level += 1
57 57 # I return self, so callers can use add_builtin in a with clause.
58 58 return self
59 59
60 60 def __exit__(self, type, value, traceback):
61 61 if self._nested_level == 1:
62 62 self.unset()
63 63 self._nested_level -= 1
64 64 # Returning False will cause exceptions to propagate
65 65 return False
66 66
67 67 def add_builtin(self, key, value):
68 68 """Add a builtin and save the original."""
69 69 orig = __builtin__.__dict__.get(key, BuiltinUndefined)
70 70 self._orig_builtins[key] = orig
71 71 __builtin__.__dict__[key] = value
72 72
73 73 def remove_builtin(self, key):
74 74 """Remove an added builtin and re-set the original."""
75 75 try:
76 76 orig = self._orig_builtins.pop(key)
77 77 except KeyError:
78 78 pass
79 79 else:
80 80 if orig is BuiltinUndefined:
81 81 del __builtin__.__dict__[key]
82 82 else:
83 83 __builtin__.__dict__[key] = orig
84 84
85 85 def set(self):
86 86 """Store ipython references in the __builtin__ namespace."""
87 87 self.add_builtin('exit', Quitter(self.shell, 'exit'))
88 88 self.add_builtin('quit', Quitter(self.shell, 'quit'))
89 89 self.add_builtin('get_ipython', self.shell.get_ipython)
90 90
91 91 # Recursive reload function
92 92 try:
93 93 from IPython.lib import deepreload
94 94 if self.shell.deep_reload:
95 95 self.add_builtin('reload', deepreload.reload)
96 96 else:
97 97 self.add_builtin('dreload', deepreload.reload)
98 98 del deepreload
99 99 except ImportError:
100 100 pass
101 101
102 102 # Keep in the builtins a flag for when IPython is active. We set it
103 103 # with setdefault so that multiple nested IPythons don't clobber one
104 104 # another. Each will increase its value by one upon being activated,
105 105 # which also gives us a way to determine the nesting level.
106 106 __builtin__.__dict__.setdefault('__IPYTHON__active',0)
107 107
108 108 def unset(self):
109 109 """Remove any builtins which might have been added by add_builtins, or
110 110 restore overwritten ones to their previous values."""
111 111 for key in self._orig_builtins.keys():
112 112 self.remove_builtin(key)
113 113 self._orig_builtins.clear()
114 114 self._builtins_added = False
115 115 try:
116 116 del __builtin__.__dict__['__IPYTHON__active']
117 117 except KeyError:
118 118 pass
@@ -1,346 +1,346 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 A lightweight component system for IPython.
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * Fernando Perez
10 10 """
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2008-2009 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 from copy import deepcopy
24 24 import datetime
25 25 from weakref import WeakValueDictionary
26 26
27 27 from IPython.utils.importstring import import_item
28 28 from IPython.config.loader import Config
29 29 from IPython.utils.traitlets import (
30 30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
31 31 )
32 32
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Helper classes for Components
36 36 #-----------------------------------------------------------------------------
37 37
38 38
39 39 class ComponentError(Exception):
40 40 pass
41 41
42 42 class MetaComponentTracker(type):
43 43 """A metaclass that tracks instances of Components and its subclasses."""
44 44
45 45 def __init__(cls, name, bases, d):
46 46 super(MetaComponentTracker, cls).__init__(name, bases, d)
47 47 cls.__instance_refs = WeakValueDictionary()
48 48 cls.__numcreated = 0
49 49
50 50 def __call__(cls, *args, **kw):
51 51 """Called when a class is called (instantiated)!!!
52 52
53 53 When a Component or subclass is instantiated, this is called and
54 54 the instance is saved in a WeakValueDictionary for tracking.
55 55 """
56 56 instance = cls.__new__(cls, *args, **kw)
57 57
58 58 # Register the instance before __init__ is called so get_instances
59 59 # works inside __init__ methods!
60 60 indices = cls.register_instance(instance)
61 61
62 62 # This is in a try/except because of the __init__ method fails, the
63 63 # instance is discarded and shouldn't be tracked.
64 64 try:
65 65 if isinstance(instance, cls):
66 66 cls.__init__(instance, *args, **kw)
67 67 except:
68 68 # Unregister the instance because __init__ failed!
69 69 cls.unregister_instances(indices)
70 70 raise
71 71 else:
72 72 return instance
73 73
74 74 def register_instance(cls, instance):
75 75 """Register instance with cls and its subclasses."""
76 76 # indices is a list of the keys used to register the instance
77 77 # with. This list is needed if the instance needs to be unregistered.
78 78 indices = []
79 79 for c in cls.__mro__:
80 80 if issubclass(cls, c) and issubclass(c, Component):
81 81 c.__numcreated += 1
82 82 indices.append(c.__numcreated)
83 83 c.__instance_refs[c.__numcreated] = instance
84 84 else:
85 85 break
86 86 return indices
87 87
88 88 def unregister_instances(cls, indices):
89 89 """Unregister instance with cls and its subclasses."""
90 90 for c, index in zip(cls.__mro__, indices):
91 91 try:
92 92 del c.__instance_refs[index]
93 93 except KeyError:
94 94 pass
95 95
96 96 def clear_instances(cls):
97 97 """Clear all instances tracked by cls."""
98 98 cls.__instance_refs.clear()
99 99 cls.__numcreated = 0
100 100
101 101 def get_instances(cls, name=None, root=None, klass=None):
102 102 """Get all instances of cls and its subclasses.
103 103
104 104 Parameters
105 105 ----------
106 106 name : str
107 107 Limit to components with this name.
108 108 root : Component or subclass
109 109 Limit to components having this root.
110 110 klass : class or str
111 111 Limits to instances of the class or its subclasses. If a str
112 112 is given ut must be in the form 'foo.bar.MyClass'. The str
113 113 form of this argument is useful for forward declarations.
114 114 """
115 115 if klass is not None:
116 116 if isinstance(klass, basestring):
117 117 klass = import_item(klass)
118 118 # Limit search to instances of klass for performance
119 119 if issubclass(klass, Component):
120 120 return klass.get_instances(name=name, root=root)
121 121 instances = cls.__instance_refs.values()
122 122 if name is not None:
123 123 instances = [i for i in instances if i.name == name]
124 124 if klass is not None:
125 125 instances = [i for i in instances if isinstance(i, klass)]
126 126 if root is not None:
127 127 instances = [i for i in instances if i.root == root]
128 128 return instances
129 129
130 130 def get_instances_by_condition(cls, call, name=None, root=None,
131 131 klass=None):
132 132 """Get all instances of cls, i such that call(i)==True.
133 133
134 134 This also takes the ``name`` and ``root`` and ``classname``
135 135 arguments of :meth:`get_instance`
136 136 """
137 137 return [i for i in cls.get_instances(name, root, klass) if call(i)]
138 138
139 139
140 140 def masquerade_as(instance, cls):
141 141 """Let instance masquerade as an instance of cls.
142 142
143 143 Sometimes, such as in testing code, it is useful to let a class
144 144 masquerade as another. Python, being duck typed, allows this by
145 145 default. But, instances of components are tracked by their class type.
146 146
147 147 After calling this, ``cls.get_instances()`` will return ``instance``. This
148 148 does not, however, cause ``isinstance(instance, cls)`` to return ``True``.
149 149
150 150 Parameters
151 151 ----------
152 152 instance : an instance of a Component or Component subclass
153 153 The instance that will pretend to be a cls.
154 154 cls : subclass of Component
155 155 The Component subclass that instance will pretend to be.
156 156 """
157 157 cls.register_instance(instance)
158 158
159 159
160 class ComponentNameGenerator(object):
160 class __ComponentNameGenerator(object):
161 161 """A Singleton to generate unique component names."""
162 162
163 163 def __init__(self, prefix):
164 164 self.prefix = prefix
165 165 self.i = 0
166 166
167 167 def __call__(self):
168 168 count = self.i
169 169 self.i += 1
170 170 return "%s%s" % (self.prefix, count)
171 171
172 172
173 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
173 ComponentNameGenerator = __ComponentNameGenerator('ipython.component')
174 174
175 175
176 176 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
177 177 pass
178 178
179 179
180 180 #-----------------------------------------------------------------------------
181 181 # Component implementation
182 182 #-----------------------------------------------------------------------------
183 183
184 184
185 185 class Component(HasTraitlets):
186 186
187 187 __metaclass__ = MetaComponent
188 188
189 189 # Traitlets are fun!
190 190 config = Instance(Config,(),{})
191 191 parent = This()
192 192 root = This()
193 193 created = None
194 194
195 195 def __init__(self, parent, name=None, config=None):
196 196 """Create a component given a parent and possibly and name and config.
197 197
198 198 Parameters
199 199 ----------
200 200 parent : Component subclass
201 201 The parent in the component graph. The parent is used
202 202 to get the root of the component graph.
203 203 name : str
204 204 The unique name of the component. If empty, then a unique
205 205 one will be autogenerated.
206 206 config : Config
207 207 If this is empty, self.config = parent.config, otherwise
208 208 self.config = config and root.config is ignored. This argument
209 209 should only be used to *override* the automatic inheritance of
210 210 parent.config. If a caller wants to modify parent.config
211 211 (not override), the caller should make a copy and change
212 212 attributes and then pass the copy to this argument.
213 213
214 214 Notes
215 215 -----
216 216 Subclasses of Component must call the :meth:`__init__` method of
217 217 :class:`Component` *before* doing anything else and using
218 218 :func:`super`::
219 219
220 220 class MyComponent(Component):
221 221 def __init__(self, parent, name=None, config=None):
222 222 super(MyComponent, self).__init__(parent, name, config)
223 223 # Then any other code you need to finish initialization.
224 224
225 225 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
226 226 attributes are handled properly.
227 227 """
228 228 super(Component, self).__init__()
229 229 self._children = []
230 230 if name is None:
231 231 self.name = ComponentNameGenerator()
232 232 else:
233 233 self.name = name
234 234 self.root = self # This is the default, it is set when parent is set
235 235 self.parent = parent
236 236 if config is not None:
237 237 self.config = config
238 238 # We used to deepcopy, but for now we are trying to just save
239 239 # by reference. This *could* have side effects as all components
240 240 # will share config. In fact, I did find such a side effect in
241 241 # _config_changed below. If a config attribute value was a mutable type
242 242 # all instances of a component were getting the same copy, effectively
243 243 # making that a class attribute.
244 244 # self.config = deepcopy(config)
245 245 else:
246 246 if self.parent is not None:
247 247 self.config = self.parent.config
248 248 # We used to deepcopy, but for now we are trying to just save
249 249 # by reference. This *could* have side effects as all components
250 250 # will share config. In fact, I did find such a side effect in
251 251 # _config_changed below. If a config attribute value was a mutable type
252 252 # all instances of a component were getting the same copy, effectively
253 253 # making that a class attribute.
254 254 # self.config = deepcopy(self.parent.config)
255 255
256 256 self.created = datetime.datetime.now()
257 257
258 258 #-------------------------------------------------------------------------
259 259 # Static traitlet notifiations
260 260 #-------------------------------------------------------------------------
261 261
262 262 def _parent_changed(self, name, old, new):
263 263 if old is not None:
264 264 old._remove_child(self)
265 265 if new is not None:
266 266 new._add_child(self)
267 267
268 268 if new is None:
269 269 self.root = self
270 270 else:
271 271 self.root = new.root
272 272
273 273 def _root_changed(self, name, old, new):
274 274 if self.parent is None:
275 275 if not (new is self):
276 276 raise ComponentError("Root not self, but parent is None.")
277 277 else:
278 278 if not self.parent.root is new:
279 279 raise ComponentError("Error in setting the root attribute: "
280 280 "root != parent.root")
281 281
282 282 def _config_changed(self, name, old, new):
283 283 """Update all the class traits having ``config=True`` as metadata.
284 284
285 285 For any class traitlet with a ``config`` metadata attribute that is
286 286 ``True``, we update the traitlet with the value of the corresponding
287 287 config entry.
288 288 """
289 289 # Get all traitlets with a config metadata entry that is True
290 290 traitlets = self.traitlets(config=True)
291 291
292 292 # We auto-load config section for this class as well as any parent
293 293 # classes that are Component subclasses. This starts with Component
294 294 # and works down the mro loading the config for each section.
295 295 section_names = [cls.__name__ for cls in \
296 296 reversed(self.__class__.__mro__) if
297 297 issubclass(cls, Component) and issubclass(self.__class__, cls)]
298 298
299 299 for sname in section_names:
300 300 # Don't do a blind getattr as that would cause the config to
301 301 # dynamically create the section with name self.__class__.__name__.
302 302 if new._has_section(sname):
303 303 my_config = new[sname]
304 304 for k, v in traitlets.items():
305 305 # Don't allow traitlets with config=True to start with
306 306 # uppercase. Otherwise, they are confused with Config
307 307 # subsections. But, developers shouldn't have uppercase
308 308 # attributes anyways! (PEP 6)
309 309 if k[0].upper()==k[0] and not k.startswith('_'):
310 310 raise ComponentError('Component traitlets with '
311 311 'config=True must start with a lowercase so they are '
312 312 'not confused with Config subsections: %s.%s' % \
313 313 (self.__class__.__name__, k))
314 314 try:
315 315 # Here we grab the value from the config
316 316 # If k has the naming convention of a config
317 317 # section, it will be auto created.
318 318 config_value = my_config[k]
319 319 except KeyError:
320 320 pass
321 321 else:
322 322 # print "Setting %s.%s from %s.%s=%r" % \
323 323 # (self.__class__.__name__,k,sname,k,config_value)
324 324 # We have to do a deepcopy here if we don't deepcopy the entire
325 325 # config object. If we don't, a mutable config_value will be
326 326 # shared by all instances, effectively making it a class attribute.
327 327 setattr(self, k, deepcopy(config_value))
328 328
329 329 @property
330 330 def children(self):
331 331 """A list of all my child components."""
332 332 return self._children
333 333
334 334 def _remove_child(self, child):
335 335 """A private method for removing children components."""
336 336 if child in self._children:
337 337 index = self._children.index(child)
338 338 del self._children[index]
339 339
340 340 def _add_child(self, child):
341 341 """A private method for adding children components."""
342 342 if child not in self._children:
343 343 self._children.append(child)
344 344
345 345 def __repr__(self):
346 346 return "<%s('%s')>" % (self.__class__.__name__, self.name)
@@ -1,43 +1,65 b''
1 1 #!/usr/bin/env python
2 2 """Script to auto-generate our API docs.
3 3 """
4 4 # stdlib imports
5 5 import os
6 6 import sys
7 7
8 8 # local imports
9 9 sys.path.append(os.path.abspath('sphinxext'))
10 10 from apigen import ApiDocWriter
11 11
12 12 #*****************************************************************************
13 13 if __name__ == '__main__':
14 14 pjoin = os.path.join
15 15 package = 'IPython'
16 16 outdir = pjoin('source','api','generated')
17 17 docwriter = ApiDocWriter(package,rst_extension='.txt')
18 18 # You have to escape the . here because . is a special char for regexps.
19 19 # You must do make clean if you change this!
20 20 docwriter.package_skip_patterns += [r'\.fixes$',
21 21 r'\.external$',
22 22 r'\.extensions',
23 23 r'\.kernel\.config',
24 24 r'\.attic',
25 25 r'\.quarantine',
26 26 r'\.deathrow',
27 27 r'\.config\.default',
28 28 r'\.config\.profile',
29 29 r'\.frontend',
30 30 r'\.gui'
31 31 ]
32
32 33 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
34
35 # XXX These need fixing, disabling for
36 # now but we need to figure out why
37 # they are breaking. Error from sphinx
38 # for each group copied below
39
40 # AttributeError: __abstractmethods__
41 r'\.core\.component',
42 r'\.utils\.traitlets',
43
44 # AttributeError: __provides__
45 r'\.kernel\.clusterdir',
46 r'\.kernel\.configobjfactory',
47 r'\.kernel\.fcutil',
48 r'\.kernel\.ipcontrollerapp',
49 r'\.kernel\.launcher',
50 r'\.kernel\.task',
51 r'\.kernel\.winhpcjob',
52 r'\.testing\.util',
53
54 # Keeping these disabled is OK
33 55 r'\.cocoa',
34 56 r'\.ipdoctest',
35 57 r'\.Gnuplot',
36 58 r'\.frontend\.process\.winprocess',
37 59 r'\.Shell',
38 60 ]
39 61 docwriter.write_api_docs(outdir)
40 62 docwriter.write_index(outdir, 'gen',
41 63 relative_to = pjoin('source','api')
42 64 )
43 65 print '%d files written' % len(docwriter.written_modules)
General Comments 0
You need to be logged in to leave comments. Login now