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