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