##// END OF EJS Templates
parse cl_args agnostic of leading '-'...
MinRK -
Show More
@@ -1,398 +1,397 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for a configurable application.
3 A base class for a configurable application.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Min RK
8 * Min RK
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 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 logging
22 import logging
23 import os
23 import os
24 import re
24 import re
25 import sys
25 import sys
26 from copy import deepcopy
26 from copy import deepcopy
27
27
28 from IPython.config.configurable import SingletonConfigurable
28 from IPython.config.configurable import SingletonConfigurable
29 from IPython.config.loader import (
29 from IPython.config.loader import (
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
31 )
31 )
32
32
33 from IPython.utils.traitlets import (
33 from IPython.utils.traitlets import (
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
35 )
35 )
36 from IPython.utils.importstring import import_item
36 from IPython.utils.importstring import import_item
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # function for re-wrapping a helpstring
40 # function for re-wrapping a helpstring
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Descriptions for the various sections
44 # Descriptions for the various sections
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 flag_description = """
47 flag_description = """
48 Flags are command-line arguments passed as '--<flag>'.
48 Flags are command-line arguments passed as '--<flag>'.
49 These take no parameters, unlike regular key-value arguments.
49 These take no parameters, unlike regular key-value arguments.
50 They are typically used for setting boolean flags, or enabling
50 They are typically used for setting boolean flags, or enabling
51 modes that involve setting multiple options together.
51 modes that involve setting multiple options together.
52
53 Flags *always* begin with '--', never just one '-'.
54 """.strip() # trim newlines of front and back
52 """.strip() # trim newlines of front and back
55
53
56 alias_description = """
54 alias_description = """
57 These are commonly set parameters, given abbreviated aliases for convenience.
55 These are commonly set parameters, given abbreviated aliases for convenience.
58 They are set in the same `name=value` way as class parameters, where
56 They are set in the same `--name=value` way as class parameters, where
59 <name> is replaced by the real parameter for which it is an alias.
57 <name> is replaced by the real parameter for which it is an alias.
60 """.strip() # trim newlines of front and back
58 """.strip() # trim newlines of front and back
61
59
62 keyvalue_description = """
60 keyvalue_description = """
63 Parameters are set from command-line arguments of the form:
61 Parameters are set from command-line arguments of the form:
64 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
62 `--Class.trait=value`.
65 This line is evaluated in Python, so simple expressions are allowed, e.g.
63 This line is evaluated in Python, so simple expressions are allowed, e.g.
66 `C.a='range(3)'` For setting C.a=[0,1,2]
64 `--C.a='range(3)'` For setting C.a=[0,1,2]
67 """.strip() # trim newlines of front and back
65 """.strip() # trim newlines of front and back
68
66
69 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
70 # Application class
68 # Application class
71 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
72
70
73
71
74 class ApplicationError(Exception):
72 class ApplicationError(Exception):
75 pass
73 pass
76
74
77
75
78 class Application(SingletonConfigurable):
76 class Application(SingletonConfigurable):
79 """A singleton application with full configuration support."""
77 """A singleton application with full configuration support."""
80
78
81 # The name of the application, will usually match the name of the command
79 # The name of the application, will usually match the name of the command
82 # line application
80 # line application
83 name = Unicode(u'application')
81 name = Unicode(u'application')
84
82
85 # The description of the application that is printed at the beginning
83 # The description of the application that is printed at the beginning
86 # of the help.
84 # of the help.
87 description = Unicode(u'This is an application.')
85 description = Unicode(u'This is an application.')
88 # default section descriptions
86 # default section descriptions
89 flag_description = Unicode(flag_description)
87 flag_description = Unicode(flag_description)
90 alias_description = Unicode(alias_description)
88 alias_description = Unicode(alias_description)
91 keyvalue_description = Unicode(keyvalue_description)
89 keyvalue_description = Unicode(keyvalue_description)
92
90
93
91
94 # A sequence of Configurable subclasses whose config=True attributes will
92 # A sequence of Configurable subclasses whose config=True attributes will
95 # be exposed at the command line.
93 # be exposed at the command line.
96 classes = List([])
94 classes = List([])
97
95
98 # The version string of this application.
96 # The version string of this application.
99 version = Unicode(u'0.0')
97 version = Unicode(u'0.0')
100
98
101 # The log level for the application
99 # The log level for the application
102 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
100 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
103 default_value=logging.WARN,
101 default_value=logging.WARN,
104 config=True,
102 config=True,
105 help="Set the log level by value or name.")
103 help="Set the log level by value or name.")
106 def _log_level_changed(self, name, old, new):
104 def _log_level_changed(self, name, old, new):
107 """Adjust the log level when log_level is set."""
105 """Adjust the log level when log_level is set."""
108 if isinstance(new, basestring):
106 if isinstance(new, basestring):
109 new = getattr(logging, new)
107 new = getattr(logging, new)
110 self.log_level = new
108 self.log_level = new
111 self.log.setLevel(new)
109 self.log.setLevel(new)
112
110
113 # the alias map for configurables
111 # the alias map for configurables
114 aliases = Dict(dict(log_level='Application.log_level'))
112 aliases = Dict(dict(log_level='Application.log_level'))
115
113
116 # flags for loading Configurables or store_const style flags
114 # flags for loading Configurables or store_const style flags
117 # flags are loaded from this dict by '--key' flags
115 # flags are loaded from this dict by '--key' flags
118 # this must be a dict of two-tuples, the first element being the Config/dict
116 # this must be a dict of two-tuples, the first element being the Config/dict
119 # and the second being the help string for the flag
117 # and the second being the help string for the flag
120 flags = Dict()
118 flags = Dict()
121 def _flags_changed(self, name, old, new):
119 def _flags_changed(self, name, old, new):
122 """ensure flags dict is valid"""
120 """ensure flags dict is valid"""
123 for key,value in new.iteritems():
121 for key,value in new.iteritems():
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
122 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
123 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
124 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
127
125
128
126
129 # subcommands for launching other applications
127 # subcommands for launching other applications
130 # if this is not empty, this will be a parent Application
128 # if this is not empty, this will be a parent Application
131 # this must be a dict of two-tuples,
129 # this must be a dict of two-tuples,
132 # the first element being the application class/import string
130 # the first element being the application class/import string
133 # and the second being the help string for the subcommand
131 # and the second being the help string for the subcommand
134 subcommands = Dict()
132 subcommands = Dict()
135 # parse_command_line will initialize a subapp, if requested
133 # parse_command_line will initialize a subapp, if requested
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
134 subapp = Instance('IPython.config.application.Application', allow_none=True)
137
135
138 # extra command-line arguments that don't set config values
136 # extra command-line arguments that don't set config values
139 extra_args = List(Unicode)
137 extra_args = List(Unicode)
140
138
141
139
142 def __init__(self, **kwargs):
140 def __init__(self, **kwargs):
143 SingletonConfigurable.__init__(self, **kwargs)
141 SingletonConfigurable.__init__(self, **kwargs)
144 # Add my class to self.classes so my attributes appear in command line
142 # Add my class to self.classes so my attributes appear in command line
145 # options.
143 # options.
146 self.classes.insert(0, self.__class__)
144 self.classes.insert(0, self.__class__)
147
145
148 self.init_logging()
146 self.init_logging()
149
147
150 def _config_changed(self, name, old, new):
148 def _config_changed(self, name, old, new):
151 SingletonConfigurable._config_changed(self, name, old, new)
149 SingletonConfigurable._config_changed(self, name, old, new)
152 self.log.debug('Config changed:')
150 self.log.debug('Config changed:')
153 self.log.debug(repr(new))
151 self.log.debug(repr(new))
154
152
155 def init_logging(self):
153 def init_logging(self):
156 """Start logging for this application.
154 """Start logging for this application.
157
155
158 The default is to log to stdout using a StreaHandler. The log level
156 The default is to log to stdout using a StreaHandler. The log level
159 starts at loggin.WARN, but this can be adjusted by setting the
157 starts at loggin.WARN, but this can be adjusted by setting the
160 ``log_level`` attribute.
158 ``log_level`` attribute.
161 """
159 """
162 self.log = logging.getLogger(self.__class__.__name__)
160 self.log = logging.getLogger(self.__class__.__name__)
163 self.log.setLevel(self.log_level)
161 self.log.setLevel(self.log_level)
164 if sys.executable.endswith('pythonw.exe'):
162 if sys.executable.endswith('pythonw.exe'):
165 # this should really go to a file, but file-logging is only
163 # this should really go to a file, but file-logging is only
166 # hooked up in parallel applications
164 # hooked up in parallel applications
167 self._log_handler = logging.StreamHandler(open(os.devnull, 'w'))
165 self._log_handler = logging.StreamHandler(open(os.devnull, 'w'))
168 else:
166 else:
169 self._log_handler = logging.StreamHandler()
167 self._log_handler = logging.StreamHandler()
170 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
168 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
171 self._log_handler.setFormatter(self._log_formatter)
169 self._log_handler.setFormatter(self._log_formatter)
172 self.log.addHandler(self._log_handler)
170 self.log.addHandler(self._log_handler)
173
171
174 def initialize(self, argv=None):
172 def initialize(self, argv=None):
175 """Do the basic steps to configure me.
173 """Do the basic steps to configure me.
176
174
177 Override in subclasses.
175 Override in subclasses.
178 """
176 """
179 self.parse_command_line(argv)
177 self.parse_command_line(argv)
180
178
181
179
182 def start(self):
180 def start(self):
183 """Start the app mainloop.
181 """Start the app mainloop.
184
182
185 Override in subclasses.
183 Override in subclasses.
186 """
184 """
187 if self.subapp is not None:
185 if self.subapp is not None:
188 return self.subapp.start()
186 return self.subapp.start()
189
187
190 def print_alias_help(self):
188 def print_alias_help(self):
191 """Print the alias part of the help."""
189 """Print the alias part of the help."""
192 if not self.aliases:
190 if not self.aliases:
193 return
191 return
194
192
195 lines = ['Aliases']
193 lines = ['Aliases']
196 lines.append('-'*len(lines[0]))
194 lines.append('-'*len(lines[0]))
197 lines.append('')
195 lines.append('')
198 for p in wrap_paragraphs(self.alias_description):
196 for p in wrap_paragraphs(self.alias_description):
199 lines.append(p)
197 lines.append(p)
200 lines.append('')
198 lines.append('')
201
199
202 classdict = {}
200 classdict = {}
203 for cls in self.classes:
201 for cls in self.classes:
204 # include all parents (up to, but excluding Configurable) in available names
202 # include all parents (up to, but excluding Configurable) in available names
205 for c in cls.mro()[:-3]:
203 for c in cls.mro()[:-3]:
206 classdict[c.__name__] = c
204 classdict[c.__name__] = c
207
205
208 for alias, longname in self.aliases.iteritems():
206 for alias, longname in self.aliases.iteritems():
209 classname, traitname = longname.split('.',1)
207 classname, traitname = longname.split('.',1)
210 cls = classdict[classname]
208 cls = classdict[classname]
211
209
212 trait = cls.class_traits(config=True)[traitname]
210 trait = cls.class_traits(config=True)[traitname]
213 help = cls.class_get_trait_help(trait)
211 help = cls.class_get_trait_help(trait).splitlines()
214 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
212 # reformat first line
215 lines.append(help)
213 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
214 lines.extend(help)
216 lines.append('')
215 lines.append('')
217 print '\n'.join(lines)
216 print os.linesep.join(lines)
218
217
219 def print_flag_help(self):
218 def print_flag_help(self):
220 """Print the flag part of the help."""
219 """Print the flag part of the help."""
221 if not self.flags:
220 if not self.flags:
222 return
221 return
223
222
224 lines = ['Flags']
223 lines = ['Flags']
225 lines.append('-'*len(lines[0]))
224 lines.append('-'*len(lines[0]))
226 lines.append('')
225 lines.append('')
227 for p in wrap_paragraphs(self.flag_description):
226 for p in wrap_paragraphs(self.flag_description):
228 lines.append(p)
227 lines.append(p)
229 lines.append('')
228 lines.append('')
230
229
231 for m, (cfg,help) in self.flags.iteritems():
230 for m, (cfg,help) in self.flags.iteritems():
232 lines.append('--'+m)
231 lines.append('--'+m)
233 lines.append(indent(dedent(help.strip())))
232 lines.append(indent(dedent(help.strip())))
234 lines.append('')
233 lines.append('')
235 print '\n'.join(lines)
234 print '\n'.join(lines)
236
235
237 def print_subcommands(self):
236 def print_subcommands(self):
238 """Print the subcommand part of the help."""
237 """Print the subcommand part of the help."""
239 if not self.subcommands:
238 if not self.subcommands:
240 return
239 return
241
240
242 lines = ["Subcommands"]
241 lines = ["Subcommands"]
243 lines.append('-'*len(lines[0]))
242 lines.append('-'*len(lines[0]))
244 for subc, (cls,help) in self.subcommands.iteritems():
243 for subc, (cls,help) in self.subcommands.iteritems():
245 lines.append("%s : %s"%(subc, cls))
244 lines.append("%s : %s"%(subc, cls))
246 if help:
245 if help:
247 lines.append(indent(dedent(help.strip())))
246 lines.append(indent(dedent(help.strip())))
248 lines.append('')
247 lines.append('')
249 print '\n'.join(lines)
248 print '\n'.join(lines)
250
249
251 def print_help(self, classes=False):
250 def print_help(self, classes=False):
252 """Print the help for each Configurable class in self.classes.
251 """Print the help for each Configurable class in self.classes.
253
252
254 If classes=False (the default), only flags and aliases are printed.
253 If classes=False (the default), only flags and aliases are printed.
255 """
254 """
256 self.print_subcommands()
255 self.print_subcommands()
257 self.print_flag_help()
256 self.print_flag_help()
258 self.print_alias_help()
257 self.print_alias_help()
259
258
260 if classes:
259 if classes:
261 if self.classes:
260 if self.classes:
262 print "Class parameters"
261 print "Class parameters"
263 print "----------------"
262 print "----------------"
264 print
263 print
265 for p in wrap_paragraphs(self.keyvalue_description):
264 for p in wrap_paragraphs(self.keyvalue_description):
266 print p
265 print p
267 print
266 print
268
267
269 for cls in self.classes:
268 for cls in self.classes:
270 cls.class_print_help()
269 cls.class_print_help()
271 print
270 print
272 else:
271 else:
273 print "To see all available configurables, use `--help-all`"
272 print "To see all available configurables, use `--help-all`"
274 print
273 print
275
274
276 def print_description(self):
275 def print_description(self):
277 """Print the application description."""
276 """Print the application description."""
278 for p in wrap_paragraphs(self.description):
277 for p in wrap_paragraphs(self.description):
279 print p
278 print p
280 print
279 print
281
280
282 def print_version(self):
281 def print_version(self):
283 """Print the version string."""
282 """Print the version string."""
284 print self.version
283 print self.version
285
284
286 def update_config(self, config):
285 def update_config(self, config):
287 """Fire the traits events when the config is updated."""
286 """Fire the traits events when the config is updated."""
288 # Save a copy of the current config.
287 # Save a copy of the current config.
289 newconfig = deepcopy(self.config)
288 newconfig = deepcopy(self.config)
290 # Merge the new config into the current one.
289 # Merge the new config into the current one.
291 newconfig._merge(config)
290 newconfig._merge(config)
292 # Save the combined config as self.config, which triggers the traits
291 # Save the combined config as self.config, which triggers the traits
293 # events.
292 # events.
294 self.config = newconfig
293 self.config = newconfig
295
294
296 def initialize_subcommand(self, subc, argv=None):
295 def initialize_subcommand(self, subc, argv=None):
297 """Initialize a subcommand with argv."""
296 """Initialize a subcommand with argv."""
298 subapp,help = self.subcommands.get(subc)
297 subapp,help = self.subcommands.get(subc)
299
298
300 if isinstance(subapp, basestring):
299 if isinstance(subapp, basestring):
301 subapp = import_item(subapp)
300 subapp = import_item(subapp)
302
301
303 # clear existing instances
302 # clear existing instances
304 self.__class__.clear_instance()
303 self.__class__.clear_instance()
305 # instantiate
304 # instantiate
306 self.subapp = subapp.instance()
305 self.subapp = subapp.instance()
307 # and initialize subapp
306 # and initialize subapp
308 self.subapp.initialize(argv)
307 self.subapp.initialize(argv)
309
308
310 def parse_command_line(self, argv=None):
309 def parse_command_line(self, argv=None):
311 """Parse the command line arguments."""
310 """Parse the command line arguments."""
312 argv = sys.argv[1:] if argv is None else argv
311 argv = sys.argv[1:] if argv is None else argv
313
312
314 if self.subcommands and len(argv) > 0:
313 if self.subcommands and len(argv) > 0:
315 # we have subcommands, and one may have been specified
314 # we have subcommands, and one may have been specified
316 subc, subargv = argv[0], argv[1:]
315 subc, subargv = argv[0], argv[1:]
317 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
316 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
318 # it's a subcommand, and *not* a flag or class parameter
317 # it's a subcommand, and *not* a flag or class parameter
319 return self.initialize_subcommand(subc, subargv)
318 return self.initialize_subcommand(subc, subargv)
320
319
321 if '-h' in argv or '--help' in argv or '--help-all' in argv:
320 if '-h' in argv or '--help' in argv or '--help-all' in argv:
322 self.print_description()
321 self.print_description()
323 self.print_help('--help-all' in argv)
322 self.print_help('--help-all' in argv)
324 self.exit(0)
323 self.exit(0)
325
324
326 if '--version' in argv:
325 if '--version' in argv:
327 self.print_version()
326 self.print_version()
328 self.exit(0)
327 self.exit(0)
329
328
330 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
329 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
331 flags=self.flags)
330 flags=self.flags)
332 try:
331 try:
333 config = loader.load_config()
332 config = loader.load_config()
334 self.update_config(config)
333 self.update_config(config)
335 except (TraitError, ArgumentError) as e:
334 except (TraitError, ArgumentError) as e:
336 self.print_description()
335 self.print_description()
337 self.print_help()
336 self.print_help()
338 self.log.fatal(str(e))
337 self.log.fatal(str(e))
339 self.exit(1)
338 self.exit(1)
340 # store unparsed args in extra_args
339 # store unparsed args in extra_args
341 self.extra_args = loader.extra_args
340 self.extra_args = loader.extra_args
342
341
343 def load_config_file(self, filename, path=None):
342 def load_config_file(self, filename, path=None):
344 """Load a .py based config file by filename and path."""
343 """Load a .py based config file by filename and path."""
345 loader = PyFileConfigLoader(filename, path=path)
344 loader = PyFileConfigLoader(filename, path=path)
346 config = loader.load_config()
345 config = loader.load_config()
347 self.update_config(config)
346 self.update_config(config)
348
347
349 def generate_config_file(self):
348 def generate_config_file(self):
350 """generate default config file from Configurables"""
349 """generate default config file from Configurables"""
351 lines = ["# Configuration file for %s."%self.name]
350 lines = ["# Configuration file for %s."%self.name]
352 lines.append('')
351 lines.append('')
353 lines.append('c = get_config()')
352 lines.append('c = get_config()')
354 lines.append('')
353 lines.append('')
355 for cls in self.classes:
354 for cls in self.classes:
356 lines.append(cls.class_config_section())
355 lines.append(cls.class_config_section())
357 return '\n'.join(lines)
356 return '\n'.join(lines)
358
357
359 def exit(self, exit_status=0):
358 def exit(self, exit_status=0):
360 self.log.debug("Exiting application: %s" % self.name)
359 self.log.debug("Exiting application: %s" % self.name)
361 sys.exit(exit_status)
360 sys.exit(exit_status)
362
361
363 #-----------------------------------------------------------------------------
362 #-----------------------------------------------------------------------------
364 # utility functions, for convenience
363 # utility functions, for convenience
365 #-----------------------------------------------------------------------------
364 #-----------------------------------------------------------------------------
366
365
367 def boolean_flag(name, configurable, set_help='', unset_help=''):
366 def boolean_flag(name, configurable, set_help='', unset_help=''):
368 """Helper for building basic --trait, --no-trait flags.
367 """Helper for building basic --trait, --no-trait flags.
369
368
370 Parameters
369 Parameters
371 ----------
370 ----------
372
371
373 name : str
372 name : str
374 The name of the flag.
373 The name of the flag.
375 configurable : str
374 configurable : str
376 The 'Class.trait' string of the trait to be set/unset with the flag
375 The 'Class.trait' string of the trait to be set/unset with the flag
377 set_help : unicode
376 set_help : unicode
378 help string for --name flag
377 help string for --name flag
379 unset_help : unicode
378 unset_help : unicode
380 help string for --no-name flag
379 help string for --no-name flag
381
380
382 Returns
381 Returns
383 -------
382 -------
384
383
385 cfg : dict
384 cfg : dict
386 A dict with two keys: 'name', and 'no-name', for setting and unsetting
385 A dict with two keys: 'name', and 'no-name', for setting and unsetting
387 the trait, respectively.
386 the trait, respectively.
388 """
387 """
389 # default helpstrings
388 # default helpstrings
390 set_help = set_help or "set %s=True"%configurable
389 set_help = set_help or "set %s=True"%configurable
391 unset_help = unset_help or "set %s=False"%configurable
390 unset_help = unset_help or "set %s=False"%configurable
392
391
393 cls,trait = configurable.split('.')
392 cls,trait = configurable.split('.')
394
393
395 setter = {cls : {trait : True}}
394 setter = {cls : {trait : True}}
396 unsetter = {cls : {trait : False}}
395 unsetter = {cls : {trait : False}}
397 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
396 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
398
397
@@ -1,328 +1,328 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A base class for objects that are configurable.
4 A base class for objects that are configurable.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 * Min RK
10 * Min RK
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import datetime
24 import datetime
25 from copy import deepcopy
25 from copy import deepcopy
26
26
27 from loader import Config
27 from loader import Config
28 from IPython.utils.traitlets import HasTraits, Instance
28 from IPython.utils.traitlets import HasTraits, Instance
29 from IPython.utils.text import indent, wrap_paragraphs
29 from IPython.utils.text import indent, wrap_paragraphs
30
30
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Helper classes for Configurables
33 # Helper classes for Configurables
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36
36
37 class ConfigurableError(Exception):
37 class ConfigurableError(Exception):
38 pass
38 pass
39
39
40
40
41 class MultipleInstanceError(ConfigurableError):
41 class MultipleInstanceError(ConfigurableError):
42 pass
42 pass
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # Configurable implementation
45 # Configurable implementation
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47
47
48 class Configurable(HasTraits):
48 class Configurable(HasTraits):
49
49
50 config = Instance(Config,(),{})
50 config = Instance(Config,(),{})
51 created = None
51 created = None
52
52
53 def __init__(self, **kwargs):
53 def __init__(self, **kwargs):
54 """Create a configurable given a config config.
54 """Create a configurable given a config config.
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58 config : Config
58 config : Config
59 If this is empty, default values are used. If config is a
59 If this is empty, default values are used. If config is a
60 :class:`Config` instance, it will be used to configure the
60 :class:`Config` instance, it will be used to configure the
61 instance.
61 instance.
62
62
63 Notes
63 Notes
64 -----
64 -----
65 Subclasses of Configurable must call the :meth:`__init__` method of
65 Subclasses of Configurable must call the :meth:`__init__` method of
66 :class:`Configurable` *before* doing anything else and using
66 :class:`Configurable` *before* doing anything else and using
67 :func:`super`::
67 :func:`super`::
68
68
69 class MyConfigurable(Configurable):
69 class MyConfigurable(Configurable):
70 def __init__(self, config=None):
70 def __init__(self, config=None):
71 super(MyConfigurable, self).__init__(config)
71 super(MyConfigurable, self).__init__(config)
72 # Then any other code you need to finish initialization.
72 # Then any other code you need to finish initialization.
73
73
74 This ensures that instances will be configured properly.
74 This ensures that instances will be configured properly.
75 """
75 """
76 config = kwargs.pop('config', None)
76 config = kwargs.pop('config', None)
77 if config is not None:
77 if config is not None:
78 # We used to deepcopy, but for now we are trying to just save
78 # We used to deepcopy, but for now we are trying to just save
79 # by reference. This *could* have side effects as all components
79 # by reference. This *could* have side effects as all components
80 # will share config. In fact, I did find such a side effect in
80 # will share config. In fact, I did find such a side effect in
81 # _config_changed below. If a config attribute value was a mutable type
81 # _config_changed below. If a config attribute value was a mutable type
82 # all instances of a component were getting the same copy, effectively
82 # all instances of a component were getting the same copy, effectively
83 # making that a class attribute.
83 # making that a class attribute.
84 # self.config = deepcopy(config)
84 # self.config = deepcopy(config)
85 self.config = config
85 self.config = config
86 # This should go second so individual keyword arguments override
86 # This should go second so individual keyword arguments override
87 # the values in config.
87 # the values in config.
88 super(Configurable, self).__init__(**kwargs)
88 super(Configurable, self).__init__(**kwargs)
89 self.created = datetime.datetime.now()
89 self.created = datetime.datetime.now()
90
90
91 #-------------------------------------------------------------------------
91 #-------------------------------------------------------------------------
92 # Static trait notifiations
92 # Static trait notifiations
93 #-------------------------------------------------------------------------
93 #-------------------------------------------------------------------------
94
94
95 def _config_changed(self, name, old, new):
95 def _config_changed(self, name, old, new):
96 """Update all the class traits having ``config=True`` as metadata.
96 """Update all the class traits having ``config=True`` as metadata.
97
97
98 For any class trait with a ``config`` metadata attribute that is
98 For any class trait with a ``config`` metadata attribute that is
99 ``True``, we update the trait with the value of the corresponding
99 ``True``, we update the trait with the value of the corresponding
100 config entry.
100 config entry.
101 """
101 """
102 # Get all traits with a config metadata entry that is True
102 # Get all traits with a config metadata entry that is True
103 traits = self.traits(config=True)
103 traits = self.traits(config=True)
104
104
105 # We auto-load config section for this class as well as any parent
105 # We auto-load config section for this class as well as any parent
106 # classes that are Configurable subclasses. This starts with Configurable
106 # classes that are Configurable subclasses. This starts with Configurable
107 # and works down the mro loading the config for each section.
107 # and works down the mro loading the config for each section.
108 section_names = [cls.__name__ for cls in \
108 section_names = [cls.__name__ for cls in \
109 reversed(self.__class__.__mro__) if
109 reversed(self.__class__.__mro__) if
110 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
110 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
111
111
112 for sname in section_names:
112 for sname in section_names:
113 # Don't do a blind getattr as that would cause the config to
113 # Don't do a blind getattr as that would cause the config to
114 # dynamically create the section with name self.__class__.__name__.
114 # dynamically create the section with name self.__class__.__name__.
115 if new._has_section(sname):
115 if new._has_section(sname):
116 my_config = new[sname]
116 my_config = new[sname]
117 for k, v in traits.iteritems():
117 for k, v in traits.iteritems():
118 # Don't allow traitlets with config=True to start with
118 # Don't allow traitlets with config=True to start with
119 # uppercase. Otherwise, they are confused with Config
119 # uppercase. Otherwise, they are confused with Config
120 # subsections. But, developers shouldn't have uppercase
120 # subsections. But, developers shouldn't have uppercase
121 # attributes anyways! (PEP 6)
121 # attributes anyways! (PEP 6)
122 if k[0].upper()==k[0] and not k.startswith('_'):
122 if k[0].upper()==k[0] and not k.startswith('_'):
123 raise ConfigurableError('Configurable traitlets with '
123 raise ConfigurableError('Configurable traitlets with '
124 'config=True must start with a lowercase so they are '
124 'config=True must start with a lowercase so they are '
125 'not confused with Config subsections: %s.%s' % \
125 'not confused with Config subsections: %s.%s' % \
126 (self.__class__.__name__, k))
126 (self.__class__.__name__, k))
127 try:
127 try:
128 # Here we grab the value from the config
128 # Here we grab the value from the config
129 # If k has the naming convention of a config
129 # If k has the naming convention of a config
130 # section, it will be auto created.
130 # section, it will be auto created.
131 config_value = my_config[k]
131 config_value = my_config[k]
132 except KeyError:
132 except KeyError:
133 pass
133 pass
134 else:
134 else:
135 # print "Setting %s.%s from %s.%s=%r" % \
135 # print "Setting %s.%s from %s.%s=%r" % \
136 # (self.__class__.__name__,k,sname,k,config_value)
136 # (self.__class__.__name__,k,sname,k,config_value)
137 # We have to do a deepcopy here if we don't deepcopy the entire
137 # We have to do a deepcopy here if we don't deepcopy the entire
138 # config object. If we don't, a mutable config_value will be
138 # config object. If we don't, a mutable config_value will be
139 # shared by all instances, effectively making it a class attribute.
139 # shared by all instances, effectively making it a class attribute.
140 setattr(self, k, deepcopy(config_value))
140 setattr(self, k, deepcopy(config_value))
141
141
142 @classmethod
142 @classmethod
143 def class_get_help(cls):
143 def class_get_help(cls):
144 """Get the help string for this class in ReST format."""
144 """Get the help string for this class in ReST format."""
145 cls_traits = cls.class_traits(config=True)
145 cls_traits = cls.class_traits(config=True)
146 final_help = []
146 final_help = []
147 final_help.append(u'%s options' % cls.__name__)
147 final_help.append(u'%s options' % cls.__name__)
148 final_help.append(len(final_help[0])*u'-')
148 final_help.append(len(final_help[0])*u'-')
149 for k,v in cls.class_traits(config=True).iteritems():
149 for k,v in cls.class_traits(config=True).iteritems():
150 help = cls.class_get_trait_help(v)
150 help = cls.class_get_trait_help(v)
151 final_help.append(help)
151 final_help.append(help)
152 return '\n'.join(final_help)
152 return '\n'.join(final_help)
153
153
154 @classmethod
154 @classmethod
155 def class_get_trait_help(cls, trait):
155 def class_get_trait_help(cls, trait):
156 """Get the help string for a single trait."""
156 """Get the help string for a single trait."""
157 lines = []
157 lines = []
158 header = "%s.%s : %s" % (cls.__name__, trait.name, trait.__class__.__name__)
158 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
159 lines.append(header)
159 lines.append(header)
160 try:
160 try:
161 dvr = repr(trait.get_default_value())
161 dvr = repr(trait.get_default_value())
162 except Exception:
162 except Exception:
163 dvr = None # ignore defaults we can't construct
163 dvr = None # ignore defaults we can't construct
164 if dvr is not None:
164 if dvr is not None:
165 if len(dvr) > 64:
165 if len(dvr) > 64:
166 dvr = dvr[:61]+'...'
166 dvr = dvr[:61]+'...'
167 lines.append(indent('Default: %s'%dvr, 4))
167 lines.append(indent('Default: %s'%dvr, 4))
168 if 'Enum' in trait.__class__.__name__:
168 if 'Enum' in trait.__class__.__name__:
169 # include Enum choices
169 # include Enum choices
170 lines.append(indent('Choices: %r'%(trait.values,)))
170 lines.append(indent('Choices: %r'%(trait.values,)))
171
171
172 help = trait.get_metadata('help')
172 help = trait.get_metadata('help')
173 if help is not None:
173 if help is not None:
174 help = '\n'.join(wrap_paragraphs(help, 76))
174 help = '\n'.join(wrap_paragraphs(help, 76))
175 lines.append(indent(help, 4))
175 lines.append(indent(help, 4))
176 return '\n'.join(lines)
176 return '\n'.join(lines)
177
177
178 @classmethod
178 @classmethod
179 def class_print_help(cls):
179 def class_print_help(cls):
180 """Get the help string for a single trait and print it."""
180 """Get the help string for a single trait and print it."""
181 print cls.class_get_help()
181 print cls.class_get_help()
182
182
183 @classmethod
183 @classmethod
184 def class_config_section(cls):
184 def class_config_section(cls):
185 """Get the config class config section"""
185 """Get the config class config section"""
186 def c(s):
186 def c(s):
187 """return a commented, wrapped block."""
187 """return a commented, wrapped block."""
188 s = '\n\n'.join(wrap_paragraphs(s, 78))
188 s = '\n\n'.join(wrap_paragraphs(s, 78))
189
189
190 return '# ' + s.replace('\n', '\n# ')
190 return '# ' + s.replace('\n', '\n# ')
191
191
192 # section header
192 # section header
193 breaker = '#' + '-'*78
193 breaker = '#' + '-'*78
194 s = "# %s configuration"%cls.__name__
194 s = "# %s configuration"%cls.__name__
195 lines = [breaker, s, breaker, '']
195 lines = [breaker, s, breaker, '']
196 # get the description trait
196 # get the description trait
197 desc = cls.class_traits().get('description')
197 desc = cls.class_traits().get('description')
198 if desc:
198 if desc:
199 desc = desc.default_value
199 desc = desc.default_value
200 else:
200 else:
201 # no description trait, use __doc__
201 # no description trait, use __doc__
202 desc = getattr(cls, '__doc__', '')
202 desc = getattr(cls, '__doc__', '')
203 if desc:
203 if desc:
204 lines.append(c(desc))
204 lines.append(c(desc))
205 lines.append('')
205 lines.append('')
206
206
207 parents = []
207 parents = []
208 for parent in cls.mro():
208 for parent in cls.mro():
209 # only include parents that are not base classes
209 # only include parents that are not base classes
210 # and are not the class itself
210 # and are not the class itself
211 if issubclass(parent, Configurable) and \
211 if issubclass(parent, Configurable) and \
212 not parent in (Configurable, SingletonConfigurable, cls):
212 not parent in (Configurable, SingletonConfigurable, cls):
213 parents.append(parent)
213 parents.append(parent)
214
214
215 if parents:
215 if parents:
216 pstr = ', '.join([ p.__name__ for p in parents ])
216 pstr = ', '.join([ p.__name__ for p in parents ])
217 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
217 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
218 lines.append('')
218 lines.append('')
219
219
220 for name,trait in cls.class_traits(config=True).iteritems():
220 for name,trait in cls.class_traits(config=True).iteritems():
221 help = trait.get_metadata('help') or ''
221 help = trait.get_metadata('help') or ''
222 lines.append(c(help))
222 lines.append(c(help))
223 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
223 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
224 lines.append('')
224 lines.append('')
225 return '\n'.join(lines)
225 return '\n'.join(lines)
226
226
227
227
228
228
229 class SingletonConfigurable(Configurable):
229 class SingletonConfigurable(Configurable):
230 """A configurable that only allows one instance.
230 """A configurable that only allows one instance.
231
231
232 This class is for classes that should only have one instance of itself
232 This class is for classes that should only have one instance of itself
233 or *any* subclass. To create and retrieve such a class use the
233 or *any* subclass. To create and retrieve such a class use the
234 :meth:`SingletonConfigurable.instance` method.
234 :meth:`SingletonConfigurable.instance` method.
235 """
235 """
236
236
237 _instance = None
237 _instance = None
238
238
239 @classmethod
239 @classmethod
240 def _walk_mro(cls):
240 def _walk_mro(cls):
241 """Walk the cls.mro() for parent classes that are also singletons
241 """Walk the cls.mro() for parent classes that are also singletons
242
242
243 For use in instance()
243 For use in instance()
244 """
244 """
245
245
246 for subclass in cls.mro():
246 for subclass in cls.mro():
247 if issubclass(cls, subclass) and \
247 if issubclass(cls, subclass) and \
248 issubclass(subclass, SingletonConfigurable) and \
248 issubclass(subclass, SingletonConfigurable) and \
249 subclass != SingletonConfigurable:
249 subclass != SingletonConfigurable:
250 yield subclass
250 yield subclass
251
251
252 @classmethod
252 @classmethod
253 def clear_instance(cls):
253 def clear_instance(cls):
254 """unset _instance for this class and singleton parents.
254 """unset _instance for this class and singleton parents.
255 """
255 """
256 if not cls.initialized():
256 if not cls.initialized():
257 return
257 return
258 for subclass in cls._walk_mro():
258 for subclass in cls._walk_mro():
259 if isinstance(subclass._instance, cls):
259 if isinstance(subclass._instance, cls):
260 # only clear instances that are instances
260 # only clear instances that are instances
261 # of the calling class
261 # of the calling class
262 subclass._instance = None
262 subclass._instance = None
263
263
264 @classmethod
264 @classmethod
265 def instance(cls, *args, **kwargs):
265 def instance(cls, *args, **kwargs):
266 """Returns a global instance of this class.
266 """Returns a global instance of this class.
267
267
268 This method create a new instance if none have previously been created
268 This method create a new instance if none have previously been created
269 and returns a previously created instance is one already exists.
269 and returns a previously created instance is one already exists.
270
270
271 The arguments and keyword arguments passed to this method are passed
271 The arguments and keyword arguments passed to this method are passed
272 on to the :meth:`__init__` method of the class upon instantiation.
272 on to the :meth:`__init__` method of the class upon instantiation.
273
273
274 Examples
274 Examples
275 --------
275 --------
276
276
277 Create a singleton class using instance, and retrieve it::
277 Create a singleton class using instance, and retrieve it::
278
278
279 >>> from IPython.config.configurable import SingletonConfigurable
279 >>> from IPython.config.configurable import SingletonConfigurable
280 >>> class Foo(SingletonConfigurable): pass
280 >>> class Foo(SingletonConfigurable): pass
281 >>> foo = Foo.instance()
281 >>> foo = Foo.instance()
282 >>> foo == Foo.instance()
282 >>> foo == Foo.instance()
283 True
283 True
284
284
285 Create a subclass that is retrived using the base class instance::
285 Create a subclass that is retrived using the base class instance::
286
286
287 >>> class Bar(SingletonConfigurable): pass
287 >>> class Bar(SingletonConfigurable): pass
288 >>> class Bam(Bar): pass
288 >>> class Bam(Bar): pass
289 >>> bam = Bam.instance()
289 >>> bam = Bam.instance()
290 >>> bam == Bar.instance()
290 >>> bam == Bar.instance()
291 True
291 True
292 """
292 """
293 # Create and save the instance
293 # Create and save the instance
294 if cls._instance is None:
294 if cls._instance is None:
295 inst = cls(*args, **kwargs)
295 inst = cls(*args, **kwargs)
296 # Now make sure that the instance will also be returned by
296 # Now make sure that the instance will also be returned by
297 # parent classes' _instance attribute.
297 # parent classes' _instance attribute.
298 for subclass in cls._walk_mro():
298 for subclass in cls._walk_mro():
299 subclass._instance = inst
299 subclass._instance = inst
300
300
301 if isinstance(cls._instance, cls):
301 if isinstance(cls._instance, cls):
302 return cls._instance
302 return cls._instance
303 else:
303 else:
304 raise MultipleInstanceError(
304 raise MultipleInstanceError(
305 'Multiple incompatible subclass instances of '
305 'Multiple incompatible subclass instances of '
306 '%s are being created.' % cls.__name__
306 '%s are being created.' % cls.__name__
307 )
307 )
308
308
309 @classmethod
309 @classmethod
310 def initialized(cls):
310 def initialized(cls):
311 """Has an instance been created?"""
311 """Has an instance been created?"""
312 return hasattr(cls, "_instance") and cls._instance is not None
312 return hasattr(cls, "_instance") and cls._instance is not None
313
313
314
314
315 class LoggingConfigurable(Configurable):
315 class LoggingConfigurable(Configurable):
316 """A parent class for Configurables that log.
316 """A parent class for Configurables that log.
317
317
318 Subclasses have a log trait, and the default behavior
318 Subclasses have a log trait, and the default behavior
319 is to get the logger from the currently running Application
319 is to get the logger from the currently running Application
320 via Application.instance().log.
320 via Application.instance().log.
321 """
321 """
322
322
323 log = Instance('logging.Logger')
323 log = Instance('logging.Logger')
324 def _log_default(self):
324 def _log_default(self):
325 from IPython.config.application import Application
325 from IPython.config.application import Application
326 return Application.instance().log
326 return Application.instance().log
327
327
328
328
@@ -1,555 +1,562 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__
21 import __builtin__
22 import re
22 import re
23 import sys
23 import sys
24
24
25 from IPython.external import argparse
25 from IPython.external import argparse
26 from IPython.utils.path import filefind, get_ipython_dir
26 from IPython.utils.path import filefind, get_ipython_dir
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 class ArgumentError(ConfigLoaderError):
40 class ArgumentError(ConfigLoaderError):
41 pass
41 pass
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Argparse fix
44 # Argparse fix
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 # Unfortunately argparse by default prints help messages to stderr instead of
47 # Unfortunately argparse by default prints help messages to stderr instead of
48 # stdout. This makes it annoying to capture long help screens at the command
48 # stdout. This makes it annoying to capture long help screens at the command
49 # line, since one must know how to pipe stderr, which many users don't know how
49 # line, since one must know how to pipe stderr, which many users don't know how
50 # to do. So we override the print_help method with one that defaults to
50 # to do. So we override the print_help method with one that defaults to
51 # stdout and use our class instead.
51 # stdout and use our class instead.
52
52
53 class ArgumentParser(argparse.ArgumentParser):
53 class ArgumentParser(argparse.ArgumentParser):
54 """Simple argparse subclass that prints help to stdout by default."""
54 """Simple argparse subclass that prints help to stdout by default."""
55
55
56 def print_help(self, file=None):
56 def print_help(self, file=None):
57 if file is None:
57 if file is None:
58 file = sys.stdout
58 file = sys.stdout
59 return super(ArgumentParser, self).print_help(file)
59 return super(ArgumentParser, self).print_help(file)
60
60
61 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
61 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Config class for holding config information
64 # Config class for holding config information
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67
67
68 class Config(dict):
68 class Config(dict):
69 """An attribute based dict that can do smart merges."""
69 """An attribute based dict that can do smart merges."""
70
70
71 def __init__(self, *args, **kwds):
71 def __init__(self, *args, **kwds):
72 dict.__init__(self, *args, **kwds)
72 dict.__init__(self, *args, **kwds)
73 # This sets self.__dict__ = self, but it has to be done this way
73 # This sets self.__dict__ = self, but it has to be done this way
74 # because we are also overriding __setattr__.
74 # because we are also overriding __setattr__.
75 dict.__setattr__(self, '__dict__', self)
75 dict.__setattr__(self, '__dict__', self)
76
76
77 def _merge(self, other):
77 def _merge(self, other):
78 to_update = {}
78 to_update = {}
79 for k, v in other.iteritems():
79 for k, v in other.iteritems():
80 if not self.has_key(k):
80 if not self.has_key(k):
81 to_update[k] = v
81 to_update[k] = v
82 else: # I have this key
82 else: # I have this key
83 if isinstance(v, Config):
83 if isinstance(v, Config):
84 # Recursively merge common sub Configs
84 # Recursively merge common sub Configs
85 self[k]._merge(v)
85 self[k]._merge(v)
86 else:
86 else:
87 # Plain updates for non-Configs
87 # Plain updates for non-Configs
88 to_update[k] = v
88 to_update[k] = v
89
89
90 self.update(to_update)
90 self.update(to_update)
91
91
92 def _is_section_key(self, key):
92 def _is_section_key(self, key):
93 if key[0].upper()==key[0] and not key.startswith('_'):
93 if key[0].upper()==key[0] and not key.startswith('_'):
94 return True
94 return True
95 else:
95 else:
96 return False
96 return False
97
97
98 def __contains__(self, key):
98 def __contains__(self, key):
99 if self._is_section_key(key):
99 if self._is_section_key(key):
100 return True
100 return True
101 else:
101 else:
102 return super(Config, self).__contains__(key)
102 return super(Config, self).__contains__(key)
103 # .has_key is deprecated for dictionaries.
103 # .has_key is deprecated for dictionaries.
104 has_key = __contains__
104 has_key = __contains__
105
105
106 def _has_section(self, key):
106 def _has_section(self, key):
107 if self._is_section_key(key):
107 if self._is_section_key(key):
108 if super(Config, self).__contains__(key):
108 if super(Config, self).__contains__(key):
109 return True
109 return True
110 return False
110 return False
111
111
112 def copy(self):
112 def copy(self):
113 return type(self)(dict.copy(self))
113 return type(self)(dict.copy(self))
114
114
115 def __copy__(self):
115 def __copy__(self):
116 return self.copy()
116 return self.copy()
117
117
118 def __deepcopy__(self, memo):
118 def __deepcopy__(self, memo):
119 import copy
119 import copy
120 return type(self)(copy.deepcopy(self.items()))
120 return type(self)(copy.deepcopy(self.items()))
121
121
122 def __getitem__(self, key):
122 def __getitem__(self, key):
123 # We cannot use directly self._is_section_key, because it triggers
123 # We cannot use directly self._is_section_key, because it triggers
124 # infinite recursion on top of PyPy. Instead, we manually fish the
124 # infinite recursion on top of PyPy. Instead, we manually fish the
125 # bound method.
125 # bound method.
126 is_section_key = self.__class__._is_section_key.__get__(self)
126 is_section_key = self.__class__._is_section_key.__get__(self)
127
127
128 # Because we use this for an exec namespace, we need to delegate
128 # Because we use this for an exec namespace, we need to delegate
129 # the lookup of names in __builtin__ to itself. This means
129 # the lookup of names in __builtin__ to itself. This means
130 # that you can't have section or attribute names that are
130 # that you can't have section or attribute names that are
131 # builtins.
131 # builtins.
132 try:
132 try:
133 return getattr(__builtin__, key)
133 return getattr(__builtin__, key)
134 except AttributeError:
134 except AttributeError:
135 pass
135 pass
136 if is_section_key(key):
136 if is_section_key(key):
137 try:
137 try:
138 return dict.__getitem__(self, key)
138 return dict.__getitem__(self, key)
139 except KeyError:
139 except KeyError:
140 c = Config()
140 c = Config()
141 dict.__setitem__(self, key, c)
141 dict.__setitem__(self, key, c)
142 return c
142 return c
143 else:
143 else:
144 return dict.__getitem__(self, key)
144 return dict.__getitem__(self, key)
145
145
146 def __setitem__(self, key, value):
146 def __setitem__(self, key, value):
147 # Don't allow names in __builtin__ to be modified.
147 # Don't allow names in __builtin__ to be modified.
148 if hasattr(__builtin__, key):
148 if hasattr(__builtin__, key):
149 raise ConfigError('Config variable names cannot have the same name '
149 raise ConfigError('Config variable names cannot have the same name '
150 'as a Python builtin: %s' % key)
150 'as a Python builtin: %s' % key)
151 if self._is_section_key(key):
151 if self._is_section_key(key):
152 if not isinstance(value, Config):
152 if not isinstance(value, Config):
153 raise ValueError('values whose keys begin with an uppercase '
153 raise ValueError('values whose keys begin with an uppercase '
154 'char must be Config instances: %r, %r' % (key, value))
154 'char must be Config instances: %r, %r' % (key, value))
155 else:
155 else:
156 dict.__setitem__(self, key, value)
156 dict.__setitem__(self, key, value)
157
157
158 def __getattr__(self, key):
158 def __getattr__(self, key):
159 try:
159 try:
160 return self.__getitem__(key)
160 return self.__getitem__(key)
161 except KeyError, e:
161 except KeyError, e:
162 raise AttributeError(e)
162 raise AttributeError(e)
163
163
164 def __setattr__(self, key, value):
164 def __setattr__(self, key, value):
165 try:
165 try:
166 self.__setitem__(key, value)
166 self.__setitem__(key, value)
167 except KeyError, e:
167 except KeyError, e:
168 raise AttributeError(e)
168 raise AttributeError(e)
169
169
170 def __delattr__(self, key):
170 def __delattr__(self, key):
171 try:
171 try:
172 dict.__delitem__(self, key)
172 dict.__delitem__(self, key)
173 except KeyError, e:
173 except KeyError, e:
174 raise AttributeError(e)
174 raise AttributeError(e)
175
175
176
176
177 #-----------------------------------------------------------------------------
177 #-----------------------------------------------------------------------------
178 # Config loading classes
178 # Config loading classes
179 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
180
180
181
181
182 class ConfigLoader(object):
182 class ConfigLoader(object):
183 """A object for loading configurations from just about anywhere.
183 """A object for loading configurations from just about anywhere.
184
184
185 The resulting configuration is packaged as a :class:`Struct`.
185 The resulting configuration is packaged as a :class:`Struct`.
186
186
187 Notes
187 Notes
188 -----
188 -----
189 A :class:`ConfigLoader` does one thing: load a config from a source
189 A :class:`ConfigLoader` does one thing: load a config from a source
190 (file, command line arguments) and returns the data as a :class:`Struct`.
190 (file, command line arguments) and returns the data as a :class:`Struct`.
191 There are lots of things that :class:`ConfigLoader` does not do. It does
191 There are lots of things that :class:`ConfigLoader` does not do. It does
192 not implement complex logic for finding config files. It does not handle
192 not implement complex logic for finding config files. It does not handle
193 default values or merge multiple configs. These things need to be
193 default values or merge multiple configs. These things need to be
194 handled elsewhere.
194 handled elsewhere.
195 """
195 """
196
196
197 def __init__(self):
197 def __init__(self):
198 """A base class for config loaders.
198 """A base class for config loaders.
199
199
200 Examples
200 Examples
201 --------
201 --------
202
202
203 >>> cl = ConfigLoader()
203 >>> cl = ConfigLoader()
204 >>> config = cl.load_config()
204 >>> config = cl.load_config()
205 >>> config
205 >>> config
206 {}
206 {}
207 """
207 """
208 self.clear()
208 self.clear()
209
209
210 def clear(self):
210 def clear(self):
211 self.config = Config()
211 self.config = Config()
212
212
213 def load_config(self):
213 def load_config(self):
214 """Load a config from somewhere, return a :class:`Config` instance.
214 """Load a config from somewhere, return a :class:`Config` instance.
215
215
216 Usually, this will cause self.config to be set and then returned.
216 Usually, this will cause self.config to be set and then returned.
217 However, in most cases, :meth:`ConfigLoader.clear` should be called
217 However, in most cases, :meth:`ConfigLoader.clear` should be called
218 to erase any previous state.
218 to erase any previous state.
219 """
219 """
220 self.clear()
220 self.clear()
221 return self.config
221 return self.config
222
222
223
223
224 class FileConfigLoader(ConfigLoader):
224 class FileConfigLoader(ConfigLoader):
225 """A base class for file based configurations.
225 """A base class for file based configurations.
226
226
227 As we add more file based config loaders, the common logic should go
227 As we add more file based config loaders, the common logic should go
228 here.
228 here.
229 """
229 """
230 pass
230 pass
231
231
232
232
233 class PyFileConfigLoader(FileConfigLoader):
233 class PyFileConfigLoader(FileConfigLoader):
234 """A config loader for pure python files.
234 """A config loader for pure python files.
235
235
236 This calls execfile on a plain python file and looks for attributes
236 This calls execfile on a plain python file and looks for attributes
237 that are all caps. These attribute are added to the config Struct.
237 that are all caps. These attribute are added to the config Struct.
238 """
238 """
239
239
240 def __init__(self, filename, path=None):
240 def __init__(self, filename, path=None):
241 """Build a config loader for a filename and path.
241 """Build a config loader for a filename and path.
242
242
243 Parameters
243 Parameters
244 ----------
244 ----------
245 filename : str
245 filename : str
246 The file name of the config file.
246 The file name of the config file.
247 path : str, list, tuple
247 path : str, list, tuple
248 The path to search for the config file on, or a sequence of
248 The path to search for the config file on, or a sequence of
249 paths to try in order.
249 paths to try in order.
250 """
250 """
251 super(PyFileConfigLoader, self).__init__()
251 super(PyFileConfigLoader, self).__init__()
252 self.filename = filename
252 self.filename = filename
253 self.path = path
253 self.path = path
254 self.full_filename = ''
254 self.full_filename = ''
255 self.data = None
255 self.data = None
256
256
257 def load_config(self):
257 def load_config(self):
258 """Load the config from a file and return it as a Struct."""
258 """Load the config from a file and return it as a Struct."""
259 self.clear()
259 self.clear()
260 self._find_file()
260 self._find_file()
261 self._read_file_as_dict()
261 self._read_file_as_dict()
262 self._convert_to_config()
262 self._convert_to_config()
263 return self.config
263 return self.config
264
264
265 def _find_file(self):
265 def _find_file(self):
266 """Try to find the file by searching the paths."""
266 """Try to find the file by searching the paths."""
267 self.full_filename = filefind(self.filename, self.path)
267 self.full_filename = filefind(self.filename, self.path)
268
268
269 def _read_file_as_dict(self):
269 def _read_file_as_dict(self):
270 """Load the config file into self.config, with recursive loading."""
270 """Load the config file into self.config, with recursive loading."""
271 # This closure is made available in the namespace that is used
271 # This closure is made available in the namespace that is used
272 # to exec the config file. It allows users to call
272 # to exec the config file. It allows users to call
273 # load_subconfig('myconfig.py') to load config files recursively.
273 # load_subconfig('myconfig.py') to load config files recursively.
274 # It needs to be a closure because it has references to self.path
274 # It needs to be a closure because it has references to self.path
275 # and self.config. The sub-config is loaded with the same path
275 # and self.config. The sub-config is loaded with the same path
276 # as the parent, but it uses an empty config which is then merged
276 # as the parent, but it uses an empty config which is then merged
277 # with the parents.
277 # with the parents.
278
278
279 # If a profile is specified, the config file will be loaded
279 # If a profile is specified, the config file will be loaded
280 # from that profile
280 # from that profile
281
281
282 def load_subconfig(fname, profile=None):
282 def load_subconfig(fname, profile=None):
283 # import here to prevent circular imports
283 # import here to prevent circular imports
284 from IPython.core.profiledir import ProfileDir, ProfileDirError
284 from IPython.core.profiledir import ProfileDir, ProfileDirError
285 if profile is not None:
285 if profile is not None:
286 try:
286 try:
287 profile_dir = ProfileDir.find_profile_dir_by_name(
287 profile_dir = ProfileDir.find_profile_dir_by_name(
288 get_ipython_dir(),
288 get_ipython_dir(),
289 profile,
289 profile,
290 )
290 )
291 except ProfileDirError:
291 except ProfileDirError:
292 return
292 return
293 path = profile_dir.location
293 path = profile_dir.location
294 else:
294 else:
295 path = self.path
295 path = self.path
296 loader = PyFileConfigLoader(fname, path)
296 loader = PyFileConfigLoader(fname, path)
297 try:
297 try:
298 sub_config = loader.load_config()
298 sub_config = loader.load_config()
299 except IOError:
299 except IOError:
300 # Pass silently if the sub config is not there. This happens
300 # Pass silently if the sub config is not there. This happens
301 # when a user s using a profile, but not the default config.
301 # when a user s using a profile, but not the default config.
302 pass
302 pass
303 else:
303 else:
304 self.config._merge(sub_config)
304 self.config._merge(sub_config)
305
305
306 # Again, this needs to be a closure and should be used in config
306 # Again, this needs to be a closure and should be used in config
307 # files to get the config being loaded.
307 # files to get the config being loaded.
308 def get_config():
308 def get_config():
309 return self.config
309 return self.config
310
310
311 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
311 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
312 fs_encoding = sys.getfilesystemencoding() or 'ascii'
312 fs_encoding = sys.getfilesystemencoding() or 'ascii'
313 conf_filename = self.full_filename.encode(fs_encoding)
313 conf_filename = self.full_filename.encode(fs_encoding)
314 execfile(conf_filename, namespace)
314 execfile(conf_filename, namespace)
315
315
316 def _convert_to_config(self):
316 def _convert_to_config(self):
317 if self.data is None:
317 if self.data is None:
318 ConfigLoaderError('self.data does not exist')
318 ConfigLoaderError('self.data does not exist')
319
319
320
320
321 class CommandLineConfigLoader(ConfigLoader):
321 class CommandLineConfigLoader(ConfigLoader):
322 """A config loader for command line arguments.
322 """A config loader for command line arguments.
323
323
324 As we add more command line based loaders, the common logic should go
324 As we add more command line based loaders, the common logic should go
325 here.
325 here.
326 """
326 """
327
327
328 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.*')
328 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.*')
329 flag_pattern = re.compile(r'\-\-\w+(\-\w)*')
329 flag_pattern = re.compile(r'\w+(\-\w)*')
330
330
331 class KeyValueConfigLoader(CommandLineConfigLoader):
331 class KeyValueConfigLoader(CommandLineConfigLoader):
332 """A config loader that loads key value pairs from the command line.
332 """A config loader that loads key value pairs from the command line.
333
333
334 This allows command line options to be gives in the following form::
334 This allows command line options to be gives in the following form::
335
335
336 ipython Global.profile="foo" InteractiveShell.autocall=False
336 ipython Global.profile="foo" InteractiveShell.autocall=False
337 """
337 """
338
338
339 def __init__(self, argv=None, aliases=None, flags=None):
339 def __init__(self, argv=None, aliases=None, flags=None):
340 """Create a key value pair config loader.
340 """Create a key value pair config loader.
341
341
342 Parameters
342 Parameters
343 ----------
343 ----------
344 argv : list
344 argv : list
345 A list that has the form of sys.argv[1:] which has unicode
345 A list that has the form of sys.argv[1:] which has unicode
346 elements of the form u"key=value". If this is None (default),
346 elements of the form u"key=value". If this is None (default),
347 then sys.argv[1:] will be used.
347 then sys.argv[1:] will be used.
348 aliases : dict
348 aliases : dict
349 A dict of aliases for configurable traits.
349 A dict of aliases for configurable traits.
350 Keys are the short aliases, Values are the resolved trait.
350 Keys are the short aliases, Values are the resolved trait.
351 Of the form: `{'alias' : 'Configurable.trait'}`
351 Of the form: `{'alias' : 'Configurable.trait'}`
352 flags : dict
352 flags : dict
353 A dict of flags, keyed by str name. Vaues can be Config objects,
353 A dict of flags, keyed by str name. Vaues can be Config objects,
354 dicts, or "key=value" strings. If Config or dict, when the flag
354 dicts, or "key=value" strings. If Config or dict, when the flag
355 is triggered, The flag is loaded as `self.config.update(m)`.
355 is triggered, The flag is loaded as `self.config.update(m)`.
356
356
357 Returns
357 Returns
358 -------
358 -------
359 config : Config
359 config : Config
360 The resulting Config object.
360 The resulting Config object.
361
361
362 Examples
362 Examples
363 --------
363 --------
364
364
365 >>> from IPython.config.loader import KeyValueConfigLoader
365 >>> from IPython.config.loader import KeyValueConfigLoader
366 >>> cl = KeyValueConfigLoader()
366 >>> cl = KeyValueConfigLoader()
367 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
367 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
368 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
368 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
369 """
369 """
370 self.clear()
370 self.clear()
371 if argv is None:
371 if argv is None:
372 argv = sys.argv[1:]
372 argv = sys.argv[1:]
373 self.argv = argv
373 self.argv = argv
374 self.aliases = aliases or {}
374 self.aliases = aliases or {}
375 self.flags = flags or {}
375 self.flags = flags or {}
376
376
377
377
378 def clear(self):
378 def clear(self):
379 super(KeyValueConfigLoader, self).clear()
379 super(KeyValueConfigLoader, self).clear()
380 self.extra_args = []
380 self.extra_args = []
381
381
382
382
383 def _decode_argv(self, argv, enc=None):
383 def _decode_argv(self, argv, enc=None):
384 """decode argv if bytes, using stin.encoding, falling back on default enc"""
384 """decode argv if bytes, using stin.encoding, falling back on default enc"""
385 uargv = []
385 uargv = []
386 if enc is None:
386 if enc is None:
387 enc = sys.stdin.encoding or sys.getdefaultencoding()
387 enc = sys.stdin.encoding or sys.getdefaultencoding()
388 for arg in argv:
388 for arg in argv:
389 if not isinstance(arg, unicode):
389 if not isinstance(arg, unicode):
390 # only decode if not already decoded
390 # only decode if not already decoded
391 arg = arg.decode(enc)
391 arg = arg.decode(enc)
392 uargv.append(arg)
392 uargv.append(arg)
393 return uargv
393 return uargv
394
394
395
395
396 def load_config(self, argv=None, aliases=None, flags=None):
396 def load_config(self, argv=None, aliases=None, flags=None):
397 """Parse the configuration and generate the Config object.
397 """Parse the configuration and generate the Config object.
398
398
399 After loading, any arguments that are not key-value or
399 After loading, any arguments that are not key-value or
400 flags will be stored in self.extra_args - a list of
400 flags will be stored in self.extra_args - a list of
401 unparsed command-line arguments. This is used for
401 unparsed command-line arguments. This is used for
402 arguments such as input files or subcommands.
402 arguments such as input files or subcommands.
403
403
404 Parameters
404 Parameters
405 ----------
405 ----------
406 argv : list, optional
406 argv : list, optional
407 A list that has the form of sys.argv[1:] which has unicode
407 A list that has the form of sys.argv[1:] which has unicode
408 elements of the form u"key=value". If this is None (default),
408 elements of the form u"key=value". If this is None (default),
409 then self.argv will be used.
409 then self.argv will be used.
410 aliases : dict
410 aliases : dict
411 A dict of aliases for configurable traits.
411 A dict of aliases for configurable traits.
412 Keys are the short aliases, Values are the resolved trait.
412 Keys are the short aliases, Values are the resolved trait.
413 Of the form: `{'alias' : 'Configurable.trait'}`
413 Of the form: `{'alias' : 'Configurable.trait'}`
414 flags : dict
414 flags : dict
415 A dict of flags, keyed by str name. Values can be Config objects
415 A dict of flags, keyed by str name. Values can be Config objects
416 or dicts. When the flag is triggered, The config is loaded as
416 or dicts. When the flag is triggered, The config is loaded as
417 `self.config.update(cfg)`.
417 `self.config.update(cfg)`.
418 """
418 """
419 from IPython.config.configurable import Configurable
419 from IPython.config.configurable import Configurable
420
420
421 self.clear()
421 self.clear()
422 if argv is None:
422 if argv is None:
423 argv = self.argv
423 argv = self.argv
424 if aliases is None:
424 if aliases is None:
425 aliases = self.aliases
425 aliases = self.aliases
426 if flags is None:
426 if flags is None:
427 flags = self.flags
427 flags = self.flags
428
428
429 for item in self._decode_argv(argv):
429 # ensure argv is a list of unicode strings:
430 uargv = self._decode_argv(argv)
431 for idx,raw in enumerate(uargv):
432 # strip leading '-'
433 item = raw.lstrip('-')
434
435 if raw == '--':
436 # don't parse arguments after '--'
437 self.extra_args.extend(uargv[idx+1:])
438 break
439
430 if kv_pattern.match(item):
440 if kv_pattern.match(item):
431 lhs,rhs = item.split('=',1)
441 lhs,rhs = item.split('=',1)
432 # Substitute longnames for aliases.
442 # Substitute longnames for aliases.
433 if lhs in aliases:
443 if lhs in aliases:
434 lhs = aliases[lhs]
444 lhs = aliases[lhs]
435 exec_str = 'self.config.' + lhs + '=' + rhs
445 exec_str = 'self.config.' + lhs + '=' + rhs
436 try:
446 try:
437 # Try to see if regular Python syntax will work. This
447 # Try to see if regular Python syntax will work. This
438 # won't handle strings as the quote marks are removed
448 # won't handle strings as the quote marks are removed
439 # by the system shell.
449 # by the system shell.
440 exec exec_str in locals(), globals()
450 exec exec_str in locals(), globals()
441 except (NameError, SyntaxError):
451 except (NameError, SyntaxError):
442 # This case happens if the rhs is a string but without
452 # This case happens if the rhs is a string but without
443 # the quote marks. Use repr, to get quote marks, and
453 # the quote marks. Use repr, to get quote marks, and
444 # 'u' prefix and see if
454 # 'u' prefix and see if
445 # it succeeds. If it still fails, we let it raise.
455 # it succeeds. If it still fails, we let it raise.
446 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
456 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
447 exec exec_str in locals(), globals()
457 exec exec_str in locals(), globals()
448 elif flag_pattern.match(item):
458 elif item in flags:
449 # trim leading '--'
459 cfg,help = flags[item]
450 m = item[2:]
460 if isinstance(cfg, (dict, Config)):
451 cfg,_ = flags.get(m, (None,None))
452 if cfg is None:
453 raise ArgumentError("Unrecognized flag: %r"%item)
454 elif isinstance(cfg, (dict, Config)):
455 # don't clobber whole config sections, update
461 # don't clobber whole config sections, update
456 # each section from config:
462 # each section from config:
457 for sec,c in cfg.iteritems():
463 for sec,c in cfg.iteritems():
458 self.config[sec].update(c)
464 self.config[sec].update(c)
459 else:
465 else:
460 raise ValueError("Invalid flag: %r"%flag)
466 raise ValueError("Invalid flag: '%s'"%raw)
461 elif item.startswith('-'):
467 elif raw.startswith('-'):
462 # this shouldn't ever be valid
468 raise ArgumentError("invalid argument: '%s'"%raw)
463 raise ArgumentError("Invalid argument: %r"%item)
464 else:
469 else:
465 # keep all args that aren't valid in a list,
470 # keep all args that aren't valid in a list,
466 # in case our parent knows what to do with them.
471 # in case our parent knows what to do with them.
467 self.extra_args.append(item)
472 # self.extra_args.append(item)
473 self.extra_args.extend(uargv[idx:])
474 break
468 return self.config
475 return self.config
469
476
470 class ArgParseConfigLoader(CommandLineConfigLoader):
477 class ArgParseConfigLoader(CommandLineConfigLoader):
471 """A loader that uses the argparse module to load from the command line."""
478 """A loader that uses the argparse module to load from the command line."""
472
479
473 def __init__(self, argv=None, *parser_args, **parser_kw):
480 def __init__(self, argv=None, *parser_args, **parser_kw):
474 """Create a config loader for use with argparse.
481 """Create a config loader for use with argparse.
475
482
476 Parameters
483 Parameters
477 ----------
484 ----------
478
485
479 argv : optional, list
486 argv : optional, list
480 If given, used to read command-line arguments from, otherwise
487 If given, used to read command-line arguments from, otherwise
481 sys.argv[1:] is used.
488 sys.argv[1:] is used.
482
489
483 parser_args : tuple
490 parser_args : tuple
484 A tuple of positional arguments that will be passed to the
491 A tuple of positional arguments that will be passed to the
485 constructor of :class:`argparse.ArgumentParser`.
492 constructor of :class:`argparse.ArgumentParser`.
486
493
487 parser_kw : dict
494 parser_kw : dict
488 A tuple of keyword arguments that will be passed to the
495 A tuple of keyword arguments that will be passed to the
489 constructor of :class:`argparse.ArgumentParser`.
496 constructor of :class:`argparse.ArgumentParser`.
490
497
491 Returns
498 Returns
492 -------
499 -------
493 config : Config
500 config : Config
494 The resulting Config object.
501 The resulting Config object.
495 """
502 """
496 super(CommandLineConfigLoader, self).__init__()
503 super(CommandLineConfigLoader, self).__init__()
497 if argv == None:
504 if argv == None:
498 argv = sys.argv[1:]
505 argv = sys.argv[1:]
499 self.argv = argv
506 self.argv = argv
500 self.parser_args = parser_args
507 self.parser_args = parser_args
501 self.version = parser_kw.pop("version", None)
508 self.version = parser_kw.pop("version", None)
502 kwargs = dict(argument_default=argparse.SUPPRESS)
509 kwargs = dict(argument_default=argparse.SUPPRESS)
503 kwargs.update(parser_kw)
510 kwargs.update(parser_kw)
504 self.parser_kw = kwargs
511 self.parser_kw = kwargs
505
512
506 def load_config(self, argv=None):
513 def load_config(self, argv=None):
507 """Parse command line arguments and return as a Config object.
514 """Parse command line arguments and return as a Config object.
508
515
509 Parameters
516 Parameters
510 ----------
517 ----------
511
518
512 args : optional, list
519 args : optional, list
513 If given, a list with the structure of sys.argv[1:] to parse
520 If given, a list with the structure of sys.argv[1:] to parse
514 arguments from. If not given, the instance's self.argv attribute
521 arguments from. If not given, the instance's self.argv attribute
515 (given at construction time) is used."""
522 (given at construction time) is used."""
516 self.clear()
523 self.clear()
517 if argv is None:
524 if argv is None:
518 argv = self.argv
525 argv = self.argv
519 self._create_parser()
526 self._create_parser()
520 self._parse_args(argv)
527 self._parse_args(argv)
521 self._convert_to_config()
528 self._convert_to_config()
522 return self.config
529 return self.config
523
530
524 def get_extra_args(self):
531 def get_extra_args(self):
525 if hasattr(self, 'extra_args'):
532 if hasattr(self, 'extra_args'):
526 return self.extra_args
533 return self.extra_args
527 else:
534 else:
528 return []
535 return []
529
536
530 def _create_parser(self):
537 def _create_parser(self):
531 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
538 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
532 self._add_arguments()
539 self._add_arguments()
533
540
534 def _add_arguments(self):
541 def _add_arguments(self):
535 raise NotImplementedError("subclasses must implement _add_arguments")
542 raise NotImplementedError("subclasses must implement _add_arguments")
536
543
537 def _parse_args(self, args):
544 def _parse_args(self, args):
538 """self.parser->self.parsed_data"""
545 """self.parser->self.parsed_data"""
539 # decode sys.argv to support unicode command-line options
546 # decode sys.argv to support unicode command-line options
540 uargs = []
547 uargs = []
541 for a in args:
548 for a in args:
542 if isinstance(a, str):
549 if isinstance(a, str):
543 # don't decode if we already got unicode
550 # don't decode if we already got unicode
544 a = a.decode(sys.stdin.encoding or
551 a = a.decode(sys.stdin.encoding or
545 sys.getdefaultencoding())
552 sys.getdefaultencoding())
546 uargs.append(a)
553 uargs.append(a)
547 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
554 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
548
555
549 def _convert_to_config(self):
556 def _convert_to_config(self):
550 """self.parsed_data->self.config"""
557 """self.parsed_data->self.config"""
551 for k, v in vars(self.parsed_data).iteritems():
558 for k, v in vars(self.parsed_data).iteritems():
552 exec_str = 'self.config.' + k + '= v'
559 exec_str = 'self.config.' + k + '= v'
553 exec exec_str in locals(), globals()
560 exec exec_str in locals(), globals()
554
561
555
562
@@ -1,135 +1,135 b''
1 """
1 """
2 Tests for IPython.config.application.Application
2 Tests for IPython.config.application.Application
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2011 The IPython Development Team
10 # Copyright (C) 2008-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from unittest import TestCase
20 from unittest import TestCase
21
21
22 from IPython.config.configurable import Configurable
22 from IPython.config.configurable import Configurable
23
23
24 from IPython.config.application import (
24 from IPython.config.application import (
25 Application
25 Application
26 )
26 )
27
27
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Bool, Unicode, Int, Float, List, Dict
29 Bool, Unicode, Int, Float, List, Dict
30 )
30 )
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Code
33 # Code
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class Foo(Configurable):
36 class Foo(Configurable):
37
37
38 i = Int(0, config=True, help="The integer i.")
38 i = Int(0, config=True, help="The integer i.")
39 j = Int(1, config=True, help="The integer j.")
39 j = Int(1, config=True, help="The integer j.")
40 name = Unicode(u'Brian', config=True, help="First name.")
40 name = Unicode(u'Brian', config=True, help="First name.")
41
41
42
42
43 class Bar(Configurable):
43 class Bar(Configurable):
44
44
45 b = Int(0, config=True, help="The integer b.")
45 b = Int(0, config=True, help="The integer b.")
46 enabled = Bool(True, config=True, help="Enable bar.")
46 enabled = Bool(True, config=True, help="Enable bar.")
47
47
48
48
49 class MyApp(Application):
49 class MyApp(Application):
50
50
51 name = Unicode(u'myapp')
51 name = Unicode(u'myapp')
52 running = Bool(False, config=True,
52 running = Bool(False, config=True,
53 help="Is the app running?")
53 help="Is the app running?")
54 classes = List([Bar, Foo])
54 classes = List([Bar, Foo])
55 config_file = Unicode(u'', config=True,
55 config_file = Unicode(u'', config=True,
56 help="Load this config file")
56 help="Load this config file")
57
57
58 aliases = Dict(dict(i='Foo.i',j='Foo.j',name='Foo.name',
58 aliases = Dict(dict(i='Foo.i',j='Foo.j',name='Foo.name',
59 enabled='Bar.enabled', log_level='MyApp.log_level'))
59 enabled='Bar.enabled', log_level='MyApp.log_level'))
60
60
61 flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"),
61 flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"),
62 disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False")))
62 disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False")))
63
63
64 def init_foo(self):
64 def init_foo(self):
65 self.foo = Foo(config=self.config)
65 self.foo = Foo(config=self.config)
66
66
67 def init_bar(self):
67 def init_bar(self):
68 self.bar = Bar(config=self.config)
68 self.bar = Bar(config=self.config)
69
69
70
70
71 class TestApplication(TestCase):
71 class TestApplication(TestCase):
72
72
73 def test_basic(self):
73 def test_basic(self):
74 app = MyApp()
74 app = MyApp()
75 self.assertEquals(app.name, u'myapp')
75 self.assertEquals(app.name, u'myapp')
76 self.assertEquals(app.running, False)
76 self.assertEquals(app.running, False)
77 self.assertEquals(app.classes, [MyApp,Bar,Foo])
77 self.assertEquals(app.classes, [MyApp,Bar,Foo])
78 self.assertEquals(app.config_file, u'')
78 self.assertEquals(app.config_file, u'')
79
79
80 def test_config(self):
80 def test_config(self):
81 app = MyApp()
81 app = MyApp()
82 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=50"])
82 app.parse_command_line(["--i=10","Foo.j=10","--enabled=False","-log_level=50"])
83 config = app.config
83 config = app.config
84 self.assertEquals(config.Foo.i, 10)
84 self.assertEquals(config.Foo.i, 10)
85 self.assertEquals(config.Foo.j, 10)
85 self.assertEquals(config.Foo.j, 10)
86 self.assertEquals(config.Bar.enabled, False)
86 self.assertEquals(config.Bar.enabled, False)
87 self.assertEquals(config.MyApp.log_level,50)
87 self.assertEquals(config.MyApp.log_level,50)
88
88
89 def test_config_propagation(self):
89 def test_config_propagation(self):
90 app = MyApp()
90 app = MyApp()
91 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=50"])
91 app.parse_command_line(["i=10","--Foo.j=10","enabled=False","log_level=50"])
92 app.init_foo()
92 app.init_foo()
93 app.init_bar()
93 app.init_bar()
94 self.assertEquals(app.foo.i, 10)
94 self.assertEquals(app.foo.i, 10)
95 self.assertEquals(app.foo.j, 10)
95 self.assertEquals(app.foo.j, 10)
96 self.assertEquals(app.bar.enabled, False)
96 self.assertEquals(app.bar.enabled, False)
97
97
98 def test_flags(self):
98 def test_flags(self):
99 app = MyApp()
99 app = MyApp()
100 app.parse_command_line(["--disable"])
100 app.parse_command_line(["-disable"])
101 app.init_bar()
101 app.init_bar()
102 self.assertEquals(app.bar.enabled, False)
102 self.assertEquals(app.bar.enabled, False)
103 app.parse_command_line(["--enable"])
103 app.parse_command_line(["--enable"])
104 app.init_bar()
104 app.init_bar()
105 self.assertEquals(app.bar.enabled, True)
105 self.assertEquals(app.bar.enabled, True)
106
106
107 def test_aliases(self):
107 def test_aliases(self):
108 app = MyApp()
108 app = MyApp()
109 app.parse_command_line(["i=5", "j=10"])
109 app.parse_command_line(["i=5", "j=10"])
110 app.init_foo()
110 app.init_foo()
111 self.assertEquals(app.foo.i, 5)
111 self.assertEquals(app.foo.i, 5)
112 app.init_foo()
112 app.init_foo()
113 self.assertEquals(app.foo.j, 10)
113 self.assertEquals(app.foo.j, 10)
114
114
115 def test_flag_clobber(self):
115 def test_flag_clobber(self):
116 """test that setting flags doesn't clobber existing settings"""
116 """test that setting flags doesn't clobber existing settings"""
117 app = MyApp()
117 app = MyApp()
118 app.parse_command_line(["Bar.b=5", "--disable"])
118 app.parse_command_line(["Bar.b=5", "--disable"])
119 app.init_bar()
119 app.init_bar()
120 self.assertEquals(app.bar.enabled, False)
120 self.assertEquals(app.bar.enabled, False)
121 self.assertEquals(app.bar.b, 5)
121 self.assertEquals(app.bar.b, 5)
122 app.parse_command_line(["--enable", "Bar.b=10"])
122 app.parse_command_line(["--enable", "Bar.b=10"])
123 app.init_bar()
123 app.init_bar()
124 self.assertEquals(app.bar.enabled, True)
124 self.assertEquals(app.bar.enabled, True)
125 self.assertEquals(app.bar.b, 10)
125 self.assertEquals(app.bar.b, 10)
126
126
127 def test_extra_args(self):
127 def test_extra_args(self):
128 app = MyApp()
128 app = MyApp()
129 app.parse_command_line(['extra', "Bar.b=5", "--disable", 'args'])
129 app.parse_command_line(["Bar.b=5", 'extra', "--disable", 'args'])
130 app.init_bar()
130 app.init_bar()
131 self.assertEquals(app.bar.enabled, False)
131 self.assertEquals(app.bar.enabled, True)
132 self.assertEquals(app.bar.b, 5)
132 self.assertEquals(app.bar.b, 5)
133 self.assertEquals(app.extra_args, ['extra', 'args'])
133 self.assertEquals(app.extra_args, ['extra', "--disable", 'args'])
134
134
135
135
@@ -1,166 +1,166 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Tests for IPython.config.configurable
4 Tests for IPython.config.configurable
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez (design help)
9 * Fernando Perez (design help)
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2010 The IPython Development Team
13 # Copyright (C) 2008-2010 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 unittest import TestCase
23 from unittest import TestCase
24
24
25 from IPython.config.configurable import (
25 from IPython.config.configurable import (
26 Configurable,
26 Configurable,
27 SingletonConfigurable
27 SingletonConfigurable
28 )
28 )
29
29
30 from IPython.utils.traitlets import (
30 from IPython.utils.traitlets import (
31 Int, Float, Unicode
31 Int, Float, Unicode
32 )
32 )
33
33
34 from IPython.config.loader import Config
34 from IPython.config.loader import Config
35
35
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # Test cases
38 # Test cases
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41
41
42 class MyConfigurable(Configurable):
42 class MyConfigurable(Configurable):
43 a = Int(1, config=True, help="The integer a.")
43 a = Int(1, config=True, help="The integer a.")
44 b = Float(1.0, config=True, help="The integer b.")
44 b = Float(1.0, config=True, help="The integer b.")
45 c = Unicode('no config')
45 c = Unicode('no config')
46
46
47
47
48 mc_help=u"""MyConfigurable options
48 mc_help=u"""MyConfigurable options
49 ----------------------
49 ----------------------
50 MyConfigurable.a : Int
50 --MyConfigurable.a=<Int>
51 Default: 1
51 Default: 1
52 The integer a.
52 The integer a.
53 MyConfigurable.b : Float
53 --MyConfigurable.b=<Float>
54 Default: 1.0
54 Default: 1.0
55 The integer b."""
55 The integer b."""
56
56
57 class Foo(Configurable):
57 class Foo(Configurable):
58 a = Int(0, config=True, help="The integer a.")
58 a = Int(0, config=True, help="The integer a.")
59 b = Unicode('nope', config=True)
59 b = Unicode('nope', config=True)
60
60
61
61
62 class Bar(Foo):
62 class Bar(Foo):
63 b = Unicode('gotit', config=False, help="The string b.")
63 b = Unicode('gotit', config=False, help="The string b.")
64 c = Float(config=True, help="The string c.")
64 c = Float(config=True, help="The string c.")
65
65
66
66
67 class TestConfigurable(TestCase):
67 class TestConfigurable(TestCase):
68
68
69 def test_default(self):
69 def test_default(self):
70 c1 = Configurable()
70 c1 = Configurable()
71 c2 = Configurable(config=c1.config)
71 c2 = Configurable(config=c1.config)
72 c3 = Configurable(config=c2.config)
72 c3 = Configurable(config=c2.config)
73 self.assertEquals(c1.config, c2.config)
73 self.assertEquals(c1.config, c2.config)
74 self.assertEquals(c2.config, c3.config)
74 self.assertEquals(c2.config, c3.config)
75
75
76 def test_custom(self):
76 def test_custom(self):
77 config = Config()
77 config = Config()
78 config.foo = 'foo'
78 config.foo = 'foo'
79 config.bar = 'bar'
79 config.bar = 'bar'
80 c1 = Configurable(config=config)
80 c1 = Configurable(config=config)
81 c2 = Configurable(config=c1.config)
81 c2 = Configurable(config=c1.config)
82 c3 = Configurable(config=c2.config)
82 c3 = Configurable(config=c2.config)
83 self.assertEquals(c1.config, config)
83 self.assertEquals(c1.config, config)
84 self.assertEquals(c2.config, config)
84 self.assertEquals(c2.config, config)
85 self.assertEquals(c3.config, config)
85 self.assertEquals(c3.config, config)
86 # Test that copies are not made
86 # Test that copies are not made
87 self.assert_(c1.config is config)
87 self.assert_(c1.config is config)
88 self.assert_(c2.config is config)
88 self.assert_(c2.config is config)
89 self.assert_(c3.config is config)
89 self.assert_(c3.config is config)
90 self.assert_(c1.config is c2.config)
90 self.assert_(c1.config is c2.config)
91 self.assert_(c2.config is c3.config)
91 self.assert_(c2.config is c3.config)
92
92
93 def test_inheritance(self):
93 def test_inheritance(self):
94 config = Config()
94 config = Config()
95 config.MyConfigurable.a = 2
95 config.MyConfigurable.a = 2
96 config.MyConfigurable.b = 2.0
96 config.MyConfigurable.b = 2.0
97 c1 = MyConfigurable(config=config)
97 c1 = MyConfigurable(config=config)
98 c2 = MyConfigurable(config=c1.config)
98 c2 = MyConfigurable(config=c1.config)
99 self.assertEquals(c1.a, config.MyConfigurable.a)
99 self.assertEquals(c1.a, config.MyConfigurable.a)
100 self.assertEquals(c1.b, config.MyConfigurable.b)
100 self.assertEquals(c1.b, config.MyConfigurable.b)
101 self.assertEquals(c2.a, config.MyConfigurable.a)
101 self.assertEquals(c2.a, config.MyConfigurable.a)
102 self.assertEquals(c2.b, config.MyConfigurable.b)
102 self.assertEquals(c2.b, config.MyConfigurable.b)
103
103
104 def test_parent(self):
104 def test_parent(self):
105 config = Config()
105 config = Config()
106 config.Foo.a = 10
106 config.Foo.a = 10
107 config.Foo.b = "wow"
107 config.Foo.b = "wow"
108 config.Bar.b = 'later'
108 config.Bar.b = 'later'
109 config.Bar.c = 100.0
109 config.Bar.c = 100.0
110 f = Foo(config=config)
110 f = Foo(config=config)
111 b = Bar(config=f.config)
111 b = Bar(config=f.config)
112 self.assertEquals(f.a, 10)
112 self.assertEquals(f.a, 10)
113 self.assertEquals(f.b, 'wow')
113 self.assertEquals(f.b, 'wow')
114 self.assertEquals(b.b, 'gotit')
114 self.assertEquals(b.b, 'gotit')
115 self.assertEquals(b.c, 100.0)
115 self.assertEquals(b.c, 100.0)
116
116
117 def test_override1(self):
117 def test_override1(self):
118 config = Config()
118 config = Config()
119 config.MyConfigurable.a = 2
119 config.MyConfigurable.a = 2
120 config.MyConfigurable.b = 2.0
120 config.MyConfigurable.b = 2.0
121 c = MyConfigurable(a=3, config=config)
121 c = MyConfigurable(a=3, config=config)
122 self.assertEquals(c.a, 3)
122 self.assertEquals(c.a, 3)
123 self.assertEquals(c.b, config.MyConfigurable.b)
123 self.assertEquals(c.b, config.MyConfigurable.b)
124 self.assertEquals(c.c, 'no config')
124 self.assertEquals(c.c, 'no config')
125
125
126 def test_override2(self):
126 def test_override2(self):
127 config = Config()
127 config = Config()
128 config.Foo.a = 1
128 config.Foo.a = 1
129 config.Bar.b = 'or' # Up above b is config=False, so this won't do it.
129 config.Bar.b = 'or' # Up above b is config=False, so this won't do it.
130 config.Bar.c = 10.0
130 config.Bar.c = 10.0
131 c = Bar(config=config)
131 c = Bar(config=config)
132 self.assertEquals(c.a, config.Foo.a)
132 self.assertEquals(c.a, config.Foo.a)
133 self.assertEquals(c.b, 'gotit')
133 self.assertEquals(c.b, 'gotit')
134 self.assertEquals(c.c, config.Bar.c)
134 self.assertEquals(c.c, config.Bar.c)
135 c = Bar(a=2, b='and', c=20.0, config=config)
135 c = Bar(a=2, b='and', c=20.0, config=config)
136 self.assertEquals(c.a, 2)
136 self.assertEquals(c.a, 2)
137 self.assertEquals(c.b, 'and')
137 self.assertEquals(c.b, 'and')
138 self.assertEquals(c.c, 20.0)
138 self.assertEquals(c.c, 20.0)
139
139
140 def test_help(self):
140 def test_help(self):
141 self.assertEquals(MyConfigurable.class_get_help(), mc_help)
141 self.assertEquals(MyConfigurable.class_get_help(), mc_help)
142
142
143
143
144 class TestSingletonConfigurable(TestCase):
144 class TestSingletonConfigurable(TestCase):
145
145
146 def test_instance(self):
146 def test_instance(self):
147 from IPython.config.configurable import SingletonConfigurable
147 from IPython.config.configurable import SingletonConfigurable
148 class Foo(SingletonConfigurable): pass
148 class Foo(SingletonConfigurable): pass
149 self.assertEquals(Foo.initialized(), False)
149 self.assertEquals(Foo.initialized(), False)
150 foo = Foo.instance()
150 foo = Foo.instance()
151 self.assertEquals(Foo.initialized(), True)
151 self.assertEquals(Foo.initialized(), True)
152 self.assertEquals(foo, Foo.instance())
152 self.assertEquals(foo, Foo.instance())
153 self.assertEquals(SingletonConfigurable._instance, None)
153 self.assertEquals(SingletonConfigurable._instance, None)
154
154
155 def test_inheritance(self):
155 def test_inheritance(self):
156 class Bar(SingletonConfigurable): pass
156 class Bar(SingletonConfigurable): pass
157 class Bam(Bar): pass
157 class Bam(Bar): pass
158 self.assertEquals(Bar.initialized(), False)
158 self.assertEquals(Bar.initialized(), False)
159 self.assertEquals(Bam.initialized(), False)
159 self.assertEquals(Bam.initialized(), False)
160 bam = Bam.instance()
160 bam = Bam.instance()
161 bam == Bar.instance()
161 bam == Bar.instance()
162 self.assertEquals(Bar.initialized(), True)
162 self.assertEquals(Bar.initialized(), True)
163 self.assertEquals(Bam.initialized(), True)
163 self.assertEquals(Bam.initialized(), True)
164 self.assertEquals(bam, Bam._instance)
164 self.assertEquals(bam, Bam._instance)
165 self.assertEquals(bam, Bar._instance)
165 self.assertEquals(bam, Bar._instance)
166 self.assertEquals(SingletonConfigurable._instance, None)
166 self.assertEquals(SingletonConfigurable._instance, None)
@@ -1,217 +1,219 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Tests for IPython.config.loader
4 Tests for IPython.config.loader
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez (design help)
9 * Fernando Perez (design help)
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 import os
23 import os
24 import sys
24 import sys
25 from tempfile import mkstemp
25 from tempfile import mkstemp
26 from unittest import TestCase
26 from unittest import TestCase
27
27
28 from nose import SkipTest
28 from nose import SkipTest
29
29
30 from IPython.utils.traitlets import Int, Unicode
30 from IPython.utils.traitlets import Int, Unicode
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
32 from IPython.config.loader import (
32 from IPython.config.loader import (
33 Config,
33 Config,
34 PyFileConfigLoader,
34 PyFileConfigLoader,
35 KeyValueConfigLoader,
35 KeyValueConfigLoader,
36 ArgParseConfigLoader,
36 ArgParseConfigLoader,
37 ConfigError
37 ConfigError
38 )
38 )
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Actual tests
41 # Actual tests
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44
44
45 pyfile = """
45 pyfile = """
46 c = get_config()
46 c = get_config()
47 c.a=10
47 c.a=10
48 c.b=20
48 c.b=20
49 c.Foo.Bar.value=10
49 c.Foo.Bar.value=10
50 c.Foo.Bam.value=range(10)
50 c.Foo.Bam.value=range(10)
51 c.D.C.value='hi there'
51 c.D.C.value='hi there'
52 """
52 """
53
53
54 class TestPyFileCL(TestCase):
54 class TestPyFileCL(TestCase):
55
55
56 def test_basic(self):
56 def test_basic(self):
57 fd, fname = mkstemp('.py')
57 fd, fname = mkstemp('.py')
58 f = os.fdopen(fd, 'w')
58 f = os.fdopen(fd, 'w')
59 f.write(pyfile)
59 f.write(pyfile)
60 f.close()
60 f.close()
61 # Unlink the file
61 # Unlink the file
62 cl = PyFileConfigLoader(fname)
62 cl = PyFileConfigLoader(fname)
63 config = cl.load_config()
63 config = cl.load_config()
64 self.assertEquals(config.a, 10)
64 self.assertEquals(config.a, 10)
65 self.assertEquals(config.b, 20)
65 self.assertEquals(config.b, 20)
66 self.assertEquals(config.Foo.Bar.value, 10)
66 self.assertEquals(config.Foo.Bar.value, 10)
67 self.assertEquals(config.Foo.Bam.value, range(10))
67 self.assertEquals(config.Foo.Bam.value, range(10))
68 self.assertEquals(config.D.C.value, 'hi there')
68 self.assertEquals(config.D.C.value, 'hi there')
69
69
70 class MyLoader1(ArgParseConfigLoader):
70 class MyLoader1(ArgParseConfigLoader):
71 def _add_arguments(self):
71 def _add_arguments(self):
72 p = self.parser
72 p = self.parser
73 p.add_argument('-f', '--foo', dest='Global.foo', type=str)
73 p.add_argument('-f', '--foo', dest='Global.foo', type=str)
74 p.add_argument('-b', dest='MyClass.bar', type=int)
74 p.add_argument('-b', dest='MyClass.bar', type=int)
75 p.add_argument('-n', dest='n', action='store_true')
75 p.add_argument('-n', dest='n', action='store_true')
76 p.add_argument('Global.bam', type=str)
76 p.add_argument('Global.bam', type=str)
77
77
78 class MyLoader2(ArgParseConfigLoader):
78 class MyLoader2(ArgParseConfigLoader):
79 def _add_arguments(self):
79 def _add_arguments(self):
80 subparsers = self.parser.add_subparsers(dest='subparser_name')
80 subparsers = self.parser.add_subparsers(dest='subparser_name')
81 subparser1 = subparsers.add_parser('1')
81 subparser1 = subparsers.add_parser('1')
82 subparser1.add_argument('-x',dest='Global.x')
82 subparser1.add_argument('-x',dest='Global.x')
83 subparser2 = subparsers.add_parser('2')
83 subparser2 = subparsers.add_parser('2')
84 subparser2.add_argument('y')
84 subparser2.add_argument('y')
85
85
86 class TestArgParseCL(TestCase):
86 class TestArgParseCL(TestCase):
87
87
88 def test_basic(self):
88 def test_basic(self):
89 cl = MyLoader1()
89 cl = MyLoader1()
90 config = cl.load_config('-f hi -b 10 -n wow'.split())
90 config = cl.load_config('-f hi -b 10 -n wow'.split())
91 self.assertEquals(config.Global.foo, 'hi')
91 self.assertEquals(config.Global.foo, 'hi')
92 self.assertEquals(config.MyClass.bar, 10)
92 self.assertEquals(config.MyClass.bar, 10)
93 self.assertEquals(config.n, True)
93 self.assertEquals(config.n, True)
94 self.assertEquals(config.Global.bam, 'wow')
94 self.assertEquals(config.Global.bam, 'wow')
95 config = cl.load_config(['wow'])
95 config = cl.load_config(['wow'])
96 self.assertEquals(config.keys(), ['Global'])
96 self.assertEquals(config.keys(), ['Global'])
97 self.assertEquals(config.Global.keys(), ['bam'])
97 self.assertEquals(config.Global.keys(), ['bam'])
98 self.assertEquals(config.Global.bam, 'wow')
98 self.assertEquals(config.Global.bam, 'wow')
99
99
100 def test_add_arguments(self):
100 def test_add_arguments(self):
101 cl = MyLoader2()
101 cl = MyLoader2()
102 config = cl.load_config('2 frobble'.split())
102 config = cl.load_config('2 frobble'.split())
103 self.assertEquals(config.subparser_name, '2')
103 self.assertEquals(config.subparser_name, '2')
104 self.assertEquals(config.y, 'frobble')
104 self.assertEquals(config.y, 'frobble')
105 config = cl.load_config('1 -x frobble'.split())
105 config = cl.load_config('1 -x frobble'.split())
106 self.assertEquals(config.subparser_name, '1')
106 self.assertEquals(config.subparser_name, '1')
107 self.assertEquals(config.Global.x, 'frobble')
107 self.assertEquals(config.Global.x, 'frobble')
108
108
109 def test_argv(self):
109 def test_argv(self):
110 cl = MyLoader1(argv='-f hi -b 10 -n wow'.split())
110 cl = MyLoader1(argv='-f hi -b 10 -n wow'.split())
111 config = cl.load_config()
111 config = cl.load_config()
112 self.assertEquals(config.Global.foo, 'hi')
112 self.assertEquals(config.Global.foo, 'hi')
113 self.assertEquals(config.MyClass.bar, 10)
113 self.assertEquals(config.MyClass.bar, 10)
114 self.assertEquals(config.n, True)
114 self.assertEquals(config.n, True)
115 self.assertEquals(config.Global.bam, 'wow')
115 self.assertEquals(config.Global.bam, 'wow')
116
116
117
117
118 class TestKeyValueCL(TestCase):
118 class TestKeyValueCL(TestCase):
119
119
120 def test_basic(self):
120 def test_basic(self):
121 cl = KeyValueConfigLoader()
121 cl = KeyValueConfigLoader()
122 argv = [s.strip('c.') for s in pyfile.split('\n')[2:-1]]
122 argv = [s.strip('c.') for s in pyfile.split('\n')[2:-1]]
123 config = cl.load_config(argv)
123 config = cl.load_config(argv)
124 self.assertEquals(config.a, 10)
124 self.assertEquals(config.a, 10)
125 self.assertEquals(config.b, 20)
125 self.assertEquals(config.b, 20)
126 self.assertEquals(config.Foo.Bar.value, 10)
126 self.assertEquals(config.Foo.Bar.value, 10)
127 self.assertEquals(config.Foo.Bam.value, range(10))
127 self.assertEquals(config.Foo.Bam.value, range(10))
128 self.assertEquals(config.D.C.value, 'hi there')
128 self.assertEquals(config.D.C.value, 'hi there')
129
129
130 def test_extra_args(self):
130 def test_extra_args(self):
131 cl = KeyValueConfigLoader()
131 cl = KeyValueConfigLoader()
132 config = cl.load_config(['a=5', 'b', 'c=10', 'd'])
132 config = cl.load_config(['a=5', 'b', 'c=10', 'd'])
133 self.assertEquals(cl.extra_args, ['b', 'd'])
133 self.assertEquals(cl.extra_args, ['b', 'c=10' , 'd'])
134 self.assertEquals(config.a, 5)
134 self.assertEquals(config.a, 5)
135 self.assertEquals(config.c, 10)
135 self.assertRaises(AttributeError, getattr, config, 'c')
136 config = cl.load_config(['--', 'a=5', 'c=10'])
137 self.assertEquals(cl.extra_args, ['a=5', 'c=10'])
136
138
137 def test_unicode_args(self):
139 def test_unicode_args(self):
138 cl = KeyValueConfigLoader()
140 cl = KeyValueConfigLoader()
139 argv = [u'a=épsîlön']
141 argv = [u'a=épsîlön']
140 config = cl.load_config(argv)
142 config = cl.load_config(argv)
141 self.assertEquals(config.a, u'épsîlön')
143 self.assertEquals(config.a, u'épsîlön')
142
144
143 def test_unicode_bytes_args(self):
145 def test_unicode_bytes_args(self):
144 uarg = u'a=é'
146 uarg = u'a=é'
145 try:
147 try:
146 barg = uarg.encode(sys.stdin.encoding)
148 barg = uarg.encode(sys.stdin.encoding)
147 except (TypeError, UnicodeEncodeError):
149 except (TypeError, UnicodeEncodeError):
148 raise SkipTest("sys.stdin.encoding can't handle 'é'")
150 raise SkipTest("sys.stdin.encoding can't handle 'é'")
149
151
150 cl = KeyValueConfigLoader()
152 cl = KeyValueConfigLoader()
151 config = cl.load_config([barg])
153 config = cl.load_config([barg])
152 self.assertEquals(config.a, u'é')
154 self.assertEquals(config.a, u'é')
153
155
154
156
155 class TestConfig(TestCase):
157 class TestConfig(TestCase):
156
158
157 def test_setget(self):
159 def test_setget(self):
158 c = Config()
160 c = Config()
159 c.a = 10
161 c.a = 10
160 self.assertEquals(c.a, 10)
162 self.assertEquals(c.a, 10)
161 self.assertEquals(c.has_key('b'), False)
163 self.assertEquals(c.has_key('b'), False)
162
164
163 def test_auto_section(self):
165 def test_auto_section(self):
164 c = Config()
166 c = Config()
165 self.assertEquals(c.has_key('A'), True)
167 self.assertEquals(c.has_key('A'), True)
166 self.assertEquals(c._has_section('A'), False)
168 self.assertEquals(c._has_section('A'), False)
167 A = c.A
169 A = c.A
168 A.foo = 'hi there'
170 A.foo = 'hi there'
169 self.assertEquals(c._has_section('A'), True)
171 self.assertEquals(c._has_section('A'), True)
170 self.assertEquals(c.A.foo, 'hi there')
172 self.assertEquals(c.A.foo, 'hi there')
171 del c.A
173 del c.A
172 self.assertEquals(len(c.A.keys()),0)
174 self.assertEquals(len(c.A.keys()),0)
173
175
174 def test_merge_doesnt_exist(self):
176 def test_merge_doesnt_exist(self):
175 c1 = Config()
177 c1 = Config()
176 c2 = Config()
178 c2 = Config()
177 c2.bar = 10
179 c2.bar = 10
178 c2.Foo.bar = 10
180 c2.Foo.bar = 10
179 c1._merge(c2)
181 c1._merge(c2)
180 self.assertEquals(c1.Foo.bar, 10)
182 self.assertEquals(c1.Foo.bar, 10)
181 self.assertEquals(c1.bar, 10)
183 self.assertEquals(c1.bar, 10)
182 c2.Bar.bar = 10
184 c2.Bar.bar = 10
183 c1._merge(c2)
185 c1._merge(c2)
184 self.assertEquals(c1.Bar.bar, 10)
186 self.assertEquals(c1.Bar.bar, 10)
185
187
186 def test_merge_exists(self):
188 def test_merge_exists(self):
187 c1 = Config()
189 c1 = Config()
188 c2 = Config()
190 c2 = Config()
189 c1.Foo.bar = 10
191 c1.Foo.bar = 10
190 c1.Foo.bam = 30
192 c1.Foo.bam = 30
191 c2.Foo.bar = 20
193 c2.Foo.bar = 20
192 c2.Foo.wow = 40
194 c2.Foo.wow = 40
193 c1._merge(c2)
195 c1._merge(c2)
194 self.assertEquals(c1.Foo.bam, 30)
196 self.assertEquals(c1.Foo.bam, 30)
195 self.assertEquals(c1.Foo.bar, 20)
197 self.assertEquals(c1.Foo.bar, 20)
196 self.assertEquals(c1.Foo.wow, 40)
198 self.assertEquals(c1.Foo.wow, 40)
197 c2.Foo.Bam.bam = 10
199 c2.Foo.Bam.bam = 10
198 c1._merge(c2)
200 c1._merge(c2)
199 self.assertEquals(c1.Foo.Bam.bam, 10)
201 self.assertEquals(c1.Foo.Bam.bam, 10)
200
202
201 def test_deepcopy(self):
203 def test_deepcopy(self):
202 c1 = Config()
204 c1 = Config()
203 c1.Foo.bar = 10
205 c1.Foo.bar = 10
204 c1.Foo.bam = 30
206 c1.Foo.bam = 30
205 c1.a = 'asdf'
207 c1.a = 'asdf'
206 c1.b = range(10)
208 c1.b = range(10)
207 import copy
209 import copy
208 c2 = copy.deepcopy(c1)
210 c2 = copy.deepcopy(c1)
209 self.assertEquals(c1, c2)
211 self.assertEquals(c1, c2)
210 self.assert_(c1 is not c2)
212 self.assert_(c1 is not c2)
211 self.assert_(c1.Foo is not c2.Foo)
213 self.assert_(c1.Foo is not c2.Foo)
212
214
213 def test_builtin(self):
215 def test_builtin(self):
214 c1 = Config()
216 c1 = Config()
215 exec 'foo = True' in c1
217 exec 'foo = True' in c1
216 self.assertEquals(c1.foo, True)
218 self.assertEquals(c1.foo, True)
217 self.assertRaises(ConfigError, setattr, c1, 'ValueError', 10)
219 self.assertRaises(ConfigError, setattr, c1, 'ValueError', 10)
@@ -1,242 +1,249 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A mixin for :class:`~IPython.core.application.Application` classes that
4 A mixin for :class:`~IPython.core.application.Application` classes that
5 launch InteractiveShell instances, load extensions, etc.
5 launch InteractiveShell instances, load extensions, etc.
6
6
7 Authors
7 Authors
8 -------
8 -------
9
9
10 * Min Ragan-Kelley
10 * Min Ragan-Kelley
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 from __future__ import absolute_import
24 from __future__ import absolute_import
25
25
26 import os
26 import os
27 import sys
27 import sys
28
28
29 from IPython.config.application import boolean_flag
29 from IPython.config.application import boolean_flag
30 from IPython.config.configurable import Configurable
30 from IPython.config.configurable import Configurable
31 from IPython.config.loader import Config
31 from IPython.config.loader import Config
32 from IPython.utils.path import filefind
32 from IPython.utils.path import filefind
33 from IPython.utils.traitlets import Unicode, Instance, List
33 from IPython.utils.traitlets import Unicode, Instance, List
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Aliases and Flags
36 # Aliases and Flags
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 shell_flags = {}
39 shell_flags = {}
40
40
41 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
41 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
42 addflag('autoindent', 'InteractiveShell.autoindent',
42 addflag('autoindent', 'InteractiveShell.autoindent',
43 'Turn on autoindenting.', 'Turn off autoindenting.'
43 'Turn on autoindenting.', 'Turn off autoindenting.'
44 )
44 )
45 addflag('automagic', 'InteractiveShell.automagic',
45 addflag('automagic', 'InteractiveShell.automagic',
46 """Turn on the auto calling of magic commands. Type %%magic at the
46 """Turn on the auto calling of magic commands. Type %%magic at the
47 IPython prompt for more information.""",
47 IPython prompt for more information.""",
48 'Turn off the auto calling of magic commands.'
48 'Turn off the auto calling of magic commands.'
49 )
49 )
50 addflag('pdb', 'InteractiveShell.pdb',
50 addflag('pdb', 'InteractiveShell.pdb',
51 "Enable auto calling the pdb debugger after every exception.",
51 "Enable auto calling the pdb debugger after every exception.",
52 "Disable auto calling the pdb debugger after every exception."
52 "Disable auto calling the pdb debugger after every exception."
53 )
53 )
54 addflag('pprint', 'PlainTextFormatter.pprint',
54 addflag('pprint', 'PlainTextFormatter.pprint',
55 "Enable auto pretty printing of results.",
55 "Enable auto pretty printing of results.",
56 "Disable auto auto pretty printing of results."
56 "Disable auto auto pretty printing of results."
57 )
57 )
58 addflag('color-info', 'InteractiveShell.color_info',
58 addflag('color-info', 'InteractiveShell.color_info',
59 """IPython can display information about objects via a set of func-
59 """IPython can display information about objects via a set of func-
60 tions, and optionally can use colors for this, syntax highlighting
60 tions, and optionally can use colors for this, syntax highlighting
61 source code and various other elements. However, because this
61 source code and various other elements. However, because this
62 information is passed through a pager (like 'less') and many pagers get
62 information is passed through a pager (like 'less') and many pagers get
63 confused with color codes, this option is off by default. You can test
63 confused with color codes, this option is off by default. You can test
64 it and turn it on permanently in your ipython_config.py file if it
64 it and turn it on permanently in your ipython_config.py file if it
65 works for you. Test it and turn it on permanently if it works with
65 works for you. Test it and turn it on permanently if it works with
66 your system. The magic function %%color_info allows you to toggle this
66 your system. The magic function %%color_info allows you to toggle this
67 interactively for testing.""",
67 interactively for testing.""",
68 "Disable using colors for info related things."
68 "Disable using colors for info related things."
69 )
69 )
70 addflag('deep-reload', 'InteractiveShell.deep_reload',
70 addflag('deep-reload', 'InteractiveShell.deep_reload',
71 """Enable deep (recursive) reloading by default. IPython can use the
71 """Enable deep (recursive) reloading by default. IPython can use the
72 deep_reload module which reloads changes in modules recursively (it
72 deep_reload module which reloads changes in modules recursively (it
73 replaces the reload() function, so you don't need to change anything to
73 replaces the reload() function, so you don't need to change anything to
74 use it). deep_reload() forces a full reload of modules whose code may
74 use it). deep_reload() forces a full reload of modules whose code may
75 have changed, which the default reload() function does not. When
75 have changed, which the default reload() function does not. When
76 deep_reload is off, IPython will use the normal reload(), but
76 deep_reload is off, IPython will use the normal reload(), but
77 deep_reload will still be available as dreload(). This feature is off
77 deep_reload will still be available as dreload(). This feature is off
78 by default [which means that you have both normal reload() and
78 by default [which means that you have both normal reload() and
79 dreload()].""",
79 dreload()].""",
80 "Disable deep (recursive) reloading by default."
80 "Disable deep (recursive) reloading by default."
81 )
81 )
82 nosep_config = Config()
82 nosep_config = Config()
83 nosep_config.InteractiveShell.separate_in = ''
83 nosep_config.InteractiveShell.separate_in = ''
84 nosep_config.InteractiveShell.separate_out = ''
84 nosep_config.InteractiveShell.separate_out = ''
85 nosep_config.InteractiveShell.separate_out2 = ''
85 nosep_config.InteractiveShell.separate_out2 = ''
86
86
87 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
87 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
88
88
89
89
90 # it's possible we don't want short aliases for *all* of these:
90 # it's possible we don't want short aliases for *all* of these:
91 shell_aliases = dict(
91 shell_aliases = dict(
92 autocall='InteractiveShell.autocall',
92 autocall='InteractiveShell.autocall',
93 cache_size='InteractiveShell.cache_size',
93 cache_size='InteractiveShell.cache_size',
94 colors='InteractiveShell.colors',
94 colors='InteractiveShell.colors',
95 logfile='InteractiveShell.logfile',
95 logfile='InteractiveShell.logfile',
96 log_append='InteractiveShell.logappend',
96 logappend='InteractiveShell.logappend',
97 c='InteractiveShellApp.code_to_run',
97 c='InteractiveShellApp.code_to_run',
98 ext='InteractiveShellApp.extra_extension',
98 ext='InteractiveShellApp.extra_extension',
99 )
99 )
100
100
101 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
102 # Main classes and functions
102 # Main classes and functions
103 #-----------------------------------------------------------------------------
103 #-----------------------------------------------------------------------------
104
104
105 class InteractiveShellApp(Configurable):
105 class InteractiveShellApp(Configurable):
106 """A Mixin for applications that start InteractiveShell instances.
106 """A Mixin for applications that start InteractiveShell instances.
107
107
108 Provides configurables for loading extensions and executing files
108 Provides configurables for loading extensions and executing files
109 as part of configuring a Shell environment.
109 as part of configuring a Shell environment.
110
110
111 Provides init_extensions() and init_code() methods, to be called
111 Provides init_extensions() and init_code() methods, to be called
112 after init_shell(), which must be implemented by subclasses.
112 after init_shell(), which must be implemented by subclasses.
113 """
113 """
114 extensions = List(Unicode, config=True,
114 extensions = List(Unicode, config=True,
115 help="A list of dotted module names of IPython extensions to load."
115 help="A list of dotted module names of IPython extensions to load."
116 )
116 )
117 extra_extension = Unicode('', config=True,
117 extra_extension = Unicode('', config=True,
118 help="dotted module name of an IPython extension to load."
118 help="dotted module name of an IPython extension to load."
119 )
119 )
120 def _extra_extension_changed(self, name, old, new):
120 def _extra_extension_changed(self, name, old, new):
121 if new:
121 if new:
122 # add to self.extensions
122 # add to self.extensions
123 self.extensions.append(new)
123 self.extensions.append(new)
124
124
125 exec_files = List(Unicode, config=True,
125 exec_files = List(Unicode, config=True,
126 help="""List of files to run at IPython startup."""
126 help="""List of files to run at IPython startup."""
127 )
127 )
128 file_to_run = Unicode('', config=True,
128 file_to_run = Unicode('', config=True,
129 help="""A file to be run""")
129 help="""A file to be run""")
130
130
131 exec_lines = List(Unicode, config=True,
131 exec_lines = List(Unicode, config=True,
132 help="""lines of code to run at IPython startup."""
132 help="""lines of code to run at IPython startup."""
133 )
133 )
134 code_to_run = Unicode('', config=True,
134 code_to_run = Unicode('', config=True,
135 help="Execute the given command string."
135 help="Execute the given command string."
136 )
136 )
137 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
137 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
138
138
139 def init_shell(self):
139 def init_shell(self):
140 raise NotImplementedError("Override in subclasses")
140 raise NotImplementedError("Override in subclasses")
141
141
142 def init_extensions(self):
142 def init_extensions(self):
143 """Load all IPython extensions in IPythonApp.extensions.
143 """Load all IPython extensions in IPythonApp.extensions.
144
144
145 This uses the :meth:`ExtensionManager.load_extensions` to load all
145 This uses the :meth:`ExtensionManager.load_extensions` to load all
146 the extensions listed in ``self.extensions``.
146 the extensions listed in ``self.extensions``.
147 """
147 """
148 if not self.extensions:
148 if not self.extensions:
149 return
149 return
150 try:
150 try:
151 self.log.debug("Loading IPython extensions...")
151 self.log.debug("Loading IPython extensions...")
152 extensions = self.extensions
152 extensions = self.extensions
153 for ext in extensions:
153 for ext in extensions:
154 try:
154 try:
155 self.log.info("Loading IPython extension: %s" % ext)
155 self.log.info("Loading IPython extension: %s" % ext)
156 self.shell.extension_manager.load_extension(ext)
156 self.shell.extension_manager.load_extension(ext)
157 except:
157 except:
158 self.log.warn("Error in loading extension: %s" % ext)
158 self.log.warn("Error in loading extension: %s" % ext)
159 self.shell.showtraceback()
159 self.shell.showtraceback()
160 except:
160 except:
161 self.log.warn("Unknown error in loading extensions:")
161 self.log.warn("Unknown error in loading extensions:")
162 self.shell.showtraceback()
162 self.shell.showtraceback()
163
163
164 def init_code(self):
164 def init_code(self):
165 """run the pre-flight code, specified via exec_lines"""
165 """run the pre-flight code, specified via exec_lines"""
166 self._run_exec_lines()
166 self._run_exec_lines()
167 self._run_exec_files()
167 self._run_exec_files()
168 self._run_cmd_line_code()
168 self._run_cmd_line_code()
169
169
170 def _run_exec_lines(self):
170 def _run_exec_lines(self):
171 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
171 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
172 if not self.exec_lines:
172 if not self.exec_lines:
173 return
173 return
174 try:
174 try:
175 self.log.debug("Running code from IPythonApp.exec_lines...")
175 self.log.debug("Running code from IPythonApp.exec_lines...")
176 for line in self.exec_lines:
176 for line in self.exec_lines:
177 try:
177 try:
178 self.log.info("Running code in user namespace: %s" %
178 self.log.info("Running code in user namespace: %s" %
179 line)
179 line)
180 self.shell.run_cell(line, store_history=False)
180 self.shell.run_cell(line, store_history=False)
181 except:
181 except:
182 self.log.warn("Error in executing line in user "
182 self.log.warn("Error in executing line in user "
183 "namespace: %s" % line)
183 "namespace: %s" % line)
184 self.shell.showtraceback()
184 self.shell.showtraceback()
185 except:
185 except:
186 self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
186 self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
187 self.shell.showtraceback()
187 self.shell.showtraceback()
188
188
189 def _exec_file(self, fname):
189 def _exec_file(self, fname):
190 full_filename = filefind(fname, [u'.', self.ipython_dir])
190 full_filename = filefind(fname, [u'.', self.ipython_dir])
191 if os.path.isfile(full_filename):
191 # Make sure that the running script gets a proper sys.argv as if it
192 if full_filename.endswith('.ipy'):
192 # were run from a system shell.
193 self.log.info("Running file in user namespace: %s" %
193 save_argv = sys.argv
194 full_filename)
194 sys.argv = sys.argv[sys.argv.index(fname):]
195 self.shell.safe_execfile_ipy(full_filename)
195 try:
196 else:
196 if os.path.isfile(full_filename):
197 # default to python, even without extension
197 if full_filename.endswith('.ipy'):
198 self.log.info("Running file in user namespace: %s" %
198 self.log.info("Running file in user namespace: %s" %
199 full_filename)
199 full_filename)
200 # Ensure that __file__ is always defined to match Python behavior
200 self.shell.safe_execfile_ipy(full_filename)
201 self.shell.user_ns['__file__'] = fname
201 else:
202 try:
202 # default to python, even without extension
203 self.shell.safe_execfile(full_filename, self.shell.user_ns)
203 self.log.info("Running file in user namespace: %s" %
204 finally:
204 full_filename)
205 del self.shell.user_ns['__file__']
205 # Ensure that __file__ is always defined to match Python behavior
206 self.shell.user_ns['__file__'] = fname
207 try:
208 self.shell.safe_execfile(full_filename, self.shell.user_ns)
209 finally:
210 del self.shell.user_ns['__file__']
211 finally:
212 sys.argv = save_argv
206
213
207 def _run_exec_files(self):
214 def _run_exec_files(self):
208 """Run files from IPythonApp.exec_files"""
215 """Run files from IPythonApp.exec_files"""
209 if not self.exec_files:
216 if not self.exec_files:
210 return
217 return
211
218
212 self.log.debug("Running files in IPythonApp.exec_files...")
219 self.log.debug("Running files in IPythonApp.exec_files...")
213 try:
220 try:
214 for fname in self.exec_files:
221 for fname in self.exec_files:
215 self._exec_file(fname)
222 self._exec_file(fname)
216 except:
223 except:
217 self.log.warn("Unknown error in handling IPythonApp.exec_files:")
224 self.log.warn("Unknown error in handling IPythonApp.exec_files:")
218 self.shell.showtraceback()
225 self.shell.showtraceback()
219
226
220 def _run_cmd_line_code(self):
227 def _run_cmd_line_code(self):
221 """Run code or file specified at the command-line"""
228 """Run code or file specified at the command-line"""
222 if self.code_to_run:
229 if self.code_to_run:
223 line = self.code_to_run
230 line = self.code_to_run
224 try:
231 try:
225 self.log.info("Running code given at command line (c=): %s" %
232 self.log.info("Running code given at command line (c=): %s" %
226 line)
233 line)
227 self.shell.run_cell(line, store_history=False)
234 self.shell.run_cell(line, store_history=False)
228 except:
235 except:
229 self.log.warn("Error in executing line in user namespace: %s" %
236 self.log.warn("Error in executing line in user namespace: %s" %
230 line)
237 line)
231 self.shell.showtraceback()
238 self.shell.showtraceback()
232
239
233 # Like Python itself, ignore the second if the first of these is present
240 # Like Python itself, ignore the second if the first of these is present
234 elif self.file_to_run:
241 elif self.file_to_run:
235 fname = self.file_to_run
242 fname = self.file_to_run
236 try:
243 try:
237 self._exec_file(fname)
244 self._exec_file(fname)
238 except:
245 except:
239 self.log.warn("Error in executing file in user namespace: %s" %
246 self.log.warn("Error in executing file in user namespace: %s" %
240 fname)
247 fname)
241 self.shell.showtraceback()
248 self.shell.showtraceback()
242
249
General Comments 0
You need to be logged in to leave comments. Login now