##// END OF EJS Templates
prevent flags from clobbering entire config sections...
MinRK -
Show More
@@ -1,362 +1,360 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 """
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 from copy import deepcopy
22 22 import logging
23 23 import re
24 24 import sys
25 25
26 26 from IPython.config.configurable import SingletonConfigurable
27 27 from IPython.config.loader import (
28 28 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
29 29 )
30 30
31 31 from IPython.utils.traitlets import (
32 32 Unicode, List, Int, Enum, Dict, Instance
33 33 )
34 34 from IPython.utils.importstring import import_item
35 35 from IPython.utils.text import indent
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Descriptions for the various sections
39 39 #-----------------------------------------------------------------------------
40 40
41 41 flag_description = """
42 42 Flags are command-line arguments passed as '--<flag>'.
43 43 These take no parameters, unlike regular key-value arguments.
44 44 They are typically used for setting boolean flags, or enabling
45 45 modes that involve setting multiple options together.
46 46 """.strip() # trim newlines of front and back
47 47
48 48 alias_description = """
49 49 These are commonly set parameters, given abbreviated aliases for convenience.
50 50 They are set in the same `name=value` way as class parameters, where
51 51 <name> is replaced by the real parameter for which it is an alias.
52 52 """.strip() # trim newlines of front and back
53 53
54 54 keyvalue_description = """
55 55 Parameters are set from command-line arguments of the form:
56 56 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
57 57 This line is evaluated in Python, so simple expressions are allowed, e.g.
58 58 `C.a='range(3)'` For setting C.a=[0,1,2]
59 59 """.strip() # trim newlines of front and back
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # Application class
63 63 #-----------------------------------------------------------------------------
64 64
65 65
66 66 class ApplicationError(Exception):
67 67 pass
68 68
69 69
70 70 class Application(SingletonConfigurable):
71 71 """A singleton application with full configuration support."""
72 72
73 73 # The name of the application, will usually match the name of the command
74 74 # line application
75 75 name = Unicode(u'application')
76 76
77 77 # The description of the application that is printed at the beginning
78 78 # of the help.
79 79 description = Unicode(u'This is an application.')
80 80 # default section descriptions
81 81 flag_description = Unicode(flag_description)
82 82 alias_description = Unicode(alias_description)
83 83 keyvalue_description = Unicode(keyvalue_description)
84 84
85 85
86 86 # A sequence of Configurable subclasses whose config=True attributes will
87 87 # be exposed at the command line.
88 88 classes = List([])
89 89
90 90 # The version string of this application.
91 91 version = Unicode(u'0.0')
92 92
93 93 # The log level for the application
94 94 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
95 95 config=True,
96 96 help="Set the log level.")
97 97
98 98 # the alias map for configurables
99 99 aliases = Dict(dict(log_level='Application.log_level'))
100 100
101 101 # flags for loading Configurables or store_const style flags
102 102 # flags are loaded from this dict by '--key' flags
103 103 # this must be a dict of two-tuples, the first element being the Config/dict
104 104 # and the second being the help string for the flag
105 105 flags = Dict()
106 106
107 107 # subcommands for launching other applications
108 108 # if this is not empty, this will be a parent Application
109 109 # this must be a dict of two-tuples, the first element being the application class/import string
110 110 # and the second being the help string for the subcommand
111 111 subcommands = Dict()
112 112 # parse_command_line will initialize a subapp, if requested
113 113 subapp = Instance('IPython.config.application.Application', allow_none=True)
114 114
115 115
116 116 def __init__(self, **kwargs):
117 117 SingletonConfigurable.__init__(self, **kwargs)
118 118 # Add my class to self.classes so my attributes appear in command line
119 119 # options.
120 120 self.classes.insert(0, self.__class__)
121 121
122 122 # ensure self.flags dict is valid
123 123 for key,value in self.flags.iteritems():
124 124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
125 125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
126 126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
127 127 self.init_logging()
128 128
129 129 def _config_changed(self, name, old, new):
130 130 SingletonConfigurable._config_changed(self, name, old, new)
131 131 self.log.debug('Config changed:')
132 132 self.log.debug(repr(new))
133 133
134 134 def init_logging(self):
135 135 """Start logging for this application.
136 136
137 137 The default is to log to stdout using a StreaHandler. The log level
138 138 starts at loggin.WARN, but this can be adjusted by setting the
139 139 ``log_level`` attribute.
140 140 """
141 141 self.log = logging.getLogger(self.__class__.__name__)
142 142 self.log.setLevel(self.log_level)
143 143 self._log_handler = logging.StreamHandler()
144 144 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
145 145 self._log_handler.setFormatter(self._log_formatter)
146 146 self.log.addHandler(self._log_handler)
147 147
148 148 def initialize(self, argv=None):
149 149 """Do the basic steps to configure me.
150 150
151 151 Override in subclasses.
152 152 """
153 153 self.parse_command_line(argv)
154 154
155 155
156 156 def start(self):
157 157 """Start the app mainloop.
158 158
159 159 Override in subclasses.
160 160 """
161 161 if self.subapp is not None:
162 162 return self.subapp.start()
163 163
164 164 def _log_level_changed(self, name, old, new):
165 165 """Adjust the log level when log_level is set."""
166 166 self.log.setLevel(new)
167 167
168 168 def print_alias_help(self):
169 169 """print the alias part of the help"""
170 170 if not self.aliases:
171 171 return
172 172
173 173 lines = ['Aliases']
174 174 lines.append('-'*len(lines[0]))
175 175 lines.append(self.alias_description)
176 176 lines.append('')
177 177
178 178 classdict = {}
179 179 for cls in self.classes:
180 180 # include all parents (up to, but excluding Configurable) in available names
181 181 for c in cls.mro()[:-3]:
182 182 classdict[c.__name__] = c
183 183
184 184 for alias, longname in self.aliases.iteritems():
185 185 classname, traitname = longname.split('.',1)
186 186 cls = classdict[classname]
187 187
188 188 trait = cls.class_traits(config=True)[traitname]
189 189 help = cls.class_get_trait_help(trait)
190 190 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
191 191 lines.append(help)
192 192 lines.append('')
193 193 print '\n'.join(lines)
194 194
195 195 def print_flag_help(self):
196 196 """print the flag part of the help"""
197 197 if not self.flags:
198 198 return
199 199
200 200 lines = ['Flags']
201 201 lines.append('-'*len(lines[0]))
202 202 lines.append(self.flag_description)
203 203 lines.append('')
204 204
205 205 for m, (cfg,help) in self.flags.iteritems():
206 206 lines.append('--'+m)
207 207 lines.append(indent(help, flatten=True))
208 208 lines.append('')
209 209 print '\n'.join(lines)
210 210
211 211 def print_subcommands(self):
212 212 """print the subcommand part of the help"""
213 213 if not self.subcommands:
214 214 return
215 215
216 216 lines = ["Subcommands"]
217 217 lines.append('-'*len(lines[0]))
218 218 for subc, (cls,help) in self.subcommands.iteritems():
219 219 lines.append("%s : %s"%(subc, cls))
220 220 if help:
221 221 lines.append(indent(help, flatten=True))
222 222 lines.append('')
223 223 print '\n'.join(lines)
224 224
225 225 def print_help(self, classes=False):
226 226 """Print the help for each Configurable class in self.classes.
227 227
228 228 If classes=False (the default), only flags and aliases are printed
229 229 """
230 230 self.print_subcommands()
231 231 self.print_flag_help()
232 232 self.print_alias_help()
233 233
234 234 if classes:
235 235 if self.classes:
236 236 print "Class parameters"
237 237 print "----------------"
238 238 print self.keyvalue_description
239 239 print
240 240
241 241 for cls in self.classes:
242 242 cls.class_print_help()
243 243 print
244 244 else:
245 245 print "To see all available configurables, use `--help-all`"
246 246 print
247 247
248 248 def print_description(self):
249 249 """Print the application description."""
250 250 print self.description
251 251 print
252 252
253 253 def print_version(self):
254 254 """Print the version string."""
255 255 print self.version
256 256
257 257 def update_config(self, config):
258 258 """Fire the traits events when the config is updated."""
259 259 # Save a copy of the current config.
260 260 newconfig = deepcopy(self.config)
261 261 # Merge the new config into the current one.
262 262 newconfig._merge(config)
263 263 # Save the combined config as self.config, which triggers the traits
264 264 # events.
265 265 self.config = newconfig
266 266
267 267 def initialize_subcommand(self, subc, argv=None):
268 268 """Initialize a subcommand with argv"""
269 269 subapp,help = self.subcommands.get(subc, (None,None))
270 270 if subapp is None:
271 271 self.print_description()
272 272 print "No such subcommand: %r"%subc
273 273 print
274 274 self.print_subcommands()
275 275 self.exit(1)
276 276
277 277 if isinstance(subapp, basestring):
278 278 subapp = import_item(subapp)
279 279
280 280 # instantiate
281 281 self.subapp = subapp()
282 282 # and initialize subapp
283 283 self.subapp.initialize(argv)
284 284
285 285 def parse_command_line(self, argv=None):
286 286 """Parse the command line arguments."""
287 287 argv = sys.argv[1:] if argv is None else argv
288 288
289 289 if self.subcommands and len(argv) > 0:
290 290 # we have subcommands, and one may have been specified
291 291 subc, subargv = argv[0], argv[1:]
292 292 if re.match(r'^\w(\-?\w)*$', subc):
293 293 # it's a subcommand, and *not* a flag or class parameter
294 294 return self.initialize_subcommand(subc, subargv)
295 295
296 296 if '-h' in argv or '--help' in argv or '--help-all' in argv:
297 297 self.print_description()
298 298 self.print_help('--help-all' in argv)
299 299 self.exit(0)
300 300
301 301 if '--version' in argv:
302 302 self.print_version()
303 303 self.exit(0)
304 304
305 305 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
306 306 flags=self.flags)
307 307 try:
308 308 config = loader.load_config()
309 309 except ArgumentError as e:
310 310 self.log.fatal(str(e))
311 311 self.print_description()
312 312 self.print_help()
313 313 self.exit(1)
314 314 self.update_config(config)
315 315
316 316 def load_config_file(self, filename, path=None):
317 317 """Load a .py based config file by filename and path."""
318 318 loader = PyFileConfigLoader(filename, path=path)
319 319 config = loader.load_config()
320 320 self.update_config(config)
321 321
322 322 def exit(self, exit_status=0):
323 323 self.log.debug("Exiting application: %s" % self.name)
324 324 sys.exit(exit_status)
325 325
326 326 #-----------------------------------------------------------------------------
327 327 # utility functions, for convenience
328 328 #-----------------------------------------------------------------------------
329 329
330 330 def boolean_flag(name, configurable, set_help='', unset_help=''):
331 331 """helper for building basic --trait, --no-trait flags
332 332
333 333 Parameters
334 334 ----------
335 335
336 336 name : str
337 337 The name of the flag.
338 338 configurable : str
339 339 The 'Class.trait' string of the trait to be set/unset with the flag
340 340 set_help : unicode
341 341 help string for --name flag
342 342 unset_help : unicode
343 343 help string for --no-name flag
344 344
345 345 Returns
346 346 -------
347 347
348 348 cfg : dict
349 349 A dict with two keys: 'name', and 'no-name', for setting and unsetting
350 350 the trait, respectively.
351 351 """
352 352 # default helpstrings
353 353 set_help = set_help or "set %s=True"%configurable
354 354 unset_help = unset_help or "set %s=False"%configurable
355 355
356 356 cls,trait = configurable.split('.')
357 357
358 setter = Config()
359 setter[cls][trait] = True
360 unsetter = Config()
361 unsetter[cls][trait] = False
358 setter = {cls : {trait : True}}
359 unsetter = {cls : {trait : False}}
362 360 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
@@ -1,504 +1,506 b''
1 1 """A simple configuration system.
2 2
3 3 Authors
4 4 -------
5 5 * Brian Granger
6 6 * Fernando Perez
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2008-2009 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 import __builtin__
21 21 import re
22 22 import sys
23 23
24 24 from IPython.external import argparse
25 25 from IPython.utils.path import filefind
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Exceptions
29 29 #-----------------------------------------------------------------------------
30 30
31 31
32 32 class ConfigError(Exception):
33 33 pass
34 34
35 35
36 36 class ConfigLoaderError(ConfigError):
37 37 pass
38 38
39 39 class ArgumentError(ConfigLoaderError):
40 40 pass
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # Argparse fix
44 44 #-----------------------------------------------------------------------------
45 45
46 46 # Unfortunately argparse by default prints help messages to stderr instead of
47 47 # stdout. This makes it annoying to capture long help screens at the command
48 48 # line, since one must know how to pipe stderr, which many users don't know how
49 49 # to do. So we override the print_help method with one that defaults to
50 50 # stdout and use our class instead.
51 51
52 52 class ArgumentParser(argparse.ArgumentParser):
53 53 """Simple argparse subclass that prints help to stdout by default."""
54 54
55 55 def print_help(self, file=None):
56 56 if file is None:
57 57 file = sys.stdout
58 58 return super(ArgumentParser, self).print_help(file)
59 59
60 60 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Config class for holding config information
64 64 #-----------------------------------------------------------------------------
65 65
66 66
67 67 class Config(dict):
68 68 """An attribute based dict that can do smart merges."""
69 69
70 70 def __init__(self, *args, **kwds):
71 71 dict.__init__(self, *args, **kwds)
72 72 # This sets self.__dict__ = self, but it has to be done this way
73 73 # because we are also overriding __setattr__.
74 74 dict.__setattr__(self, '__dict__', self)
75 75
76 76 def _merge(self, other):
77 77 to_update = {}
78 78 for k, v in other.iteritems():
79 79 if not self.has_key(k):
80 80 to_update[k] = v
81 81 else: # I have this key
82 82 if isinstance(v, Config):
83 83 # Recursively merge common sub Configs
84 84 self[k]._merge(v)
85 85 else:
86 86 # Plain updates for non-Configs
87 87 to_update[k] = v
88 88
89 89 self.update(to_update)
90 90
91 91 def _is_section_key(self, key):
92 92 if key[0].upper()==key[0] and not key.startswith('_'):
93 93 return True
94 94 else:
95 95 return False
96 96
97 97 def __contains__(self, key):
98 98 if self._is_section_key(key):
99 99 return True
100 100 else:
101 101 return super(Config, self).__contains__(key)
102 102 # .has_key is deprecated for dictionaries.
103 103 has_key = __contains__
104 104
105 105 def _has_section(self, key):
106 106 if self._is_section_key(key):
107 107 if super(Config, self).__contains__(key):
108 108 return True
109 109 return False
110 110
111 111 def copy(self):
112 112 return type(self)(dict.copy(self))
113 113
114 114 def __copy__(self):
115 115 return self.copy()
116 116
117 117 def __deepcopy__(self, memo):
118 118 import copy
119 119 return type(self)(copy.deepcopy(self.items()))
120 120
121 121 def __getitem__(self, key):
122 122 # We cannot use directly self._is_section_key, because it triggers
123 123 # infinite recursion on top of PyPy. Instead, we manually fish the
124 124 # bound method.
125 125 is_section_key = self.__class__._is_section_key.__get__(self)
126 126
127 127 # Because we use this for an exec namespace, we need to delegate
128 128 # the lookup of names in __builtin__ to itself. This means
129 129 # that you can't have section or attribute names that are
130 130 # builtins.
131 131 try:
132 132 return getattr(__builtin__, key)
133 133 except AttributeError:
134 134 pass
135 135 if is_section_key(key):
136 136 try:
137 137 return dict.__getitem__(self, key)
138 138 except KeyError:
139 139 c = Config()
140 140 dict.__setitem__(self, key, c)
141 141 return c
142 142 else:
143 143 return dict.__getitem__(self, key)
144 144
145 145 def __setitem__(self, key, value):
146 146 # Don't allow names in __builtin__ to be modified.
147 147 if hasattr(__builtin__, key):
148 148 raise ConfigError('Config variable names cannot have the same name '
149 149 'as a Python builtin: %s' % key)
150 150 if self._is_section_key(key):
151 151 if not isinstance(value, Config):
152 152 raise ValueError('values whose keys begin with an uppercase '
153 153 'char must be Config instances: %r, %r' % (key, value))
154 154 else:
155 155 dict.__setitem__(self, key, value)
156 156
157 157 def __getattr__(self, key):
158 158 try:
159 159 return self.__getitem__(key)
160 160 except KeyError, e:
161 161 raise AttributeError(e)
162 162
163 163 def __setattr__(self, key, value):
164 164 try:
165 165 self.__setitem__(key, value)
166 166 except KeyError, e:
167 167 raise AttributeError(e)
168 168
169 169 def __delattr__(self, key):
170 170 try:
171 171 dict.__delitem__(self, key)
172 172 except KeyError, e:
173 173 raise AttributeError(e)
174 174
175 175
176 176 #-----------------------------------------------------------------------------
177 177 # Config loading classes
178 178 #-----------------------------------------------------------------------------
179 179
180 180
181 181 class ConfigLoader(object):
182 182 """A object for loading configurations from just about anywhere.
183 183
184 184 The resulting configuration is packaged as a :class:`Struct`.
185 185
186 186 Notes
187 187 -----
188 188 A :class:`ConfigLoader` does one thing: load a config from a source
189 189 (file, command line arguments) and returns the data as a :class:`Struct`.
190 190 There are lots of things that :class:`ConfigLoader` does not do. It does
191 191 not implement complex logic for finding config files. It does not handle
192 192 default values or merge multiple configs. These things need to be
193 193 handled elsewhere.
194 194 """
195 195
196 196 def __init__(self):
197 197 """A base class for config loaders.
198 198
199 199 Examples
200 200 --------
201 201
202 202 >>> cl = ConfigLoader()
203 203 >>> config = cl.load_config()
204 204 >>> config
205 205 {}
206 206 """
207 207 self.clear()
208 208
209 209 def clear(self):
210 210 self.config = Config()
211 211
212 212 def load_config(self):
213 213 """Load a config from somewhere, return a :class:`Config` instance.
214 214
215 215 Usually, this will cause self.config to be set and then returned.
216 216 However, in most cases, :meth:`ConfigLoader.clear` should be called
217 217 to erase any previous state.
218 218 """
219 219 self.clear()
220 220 return self.config
221 221
222 222
223 223 class FileConfigLoader(ConfigLoader):
224 224 """A base class for file based configurations.
225 225
226 226 As we add more file based config loaders, the common logic should go
227 227 here.
228 228 """
229 229 pass
230 230
231 231
232 232 class PyFileConfigLoader(FileConfigLoader):
233 233 """A config loader for pure python files.
234 234
235 235 This calls execfile on a plain python file and looks for attributes
236 236 that are all caps. These attribute are added to the config Struct.
237 237 """
238 238
239 239 def __init__(self, filename, path=None):
240 240 """Build a config loader for a filename and path.
241 241
242 242 Parameters
243 243 ----------
244 244 filename : str
245 245 The file name of the config file.
246 246 path : str, list, tuple
247 247 The path to search for the config file on, or a sequence of
248 248 paths to try in order.
249 249 """
250 250 super(PyFileConfigLoader, self).__init__()
251 251 self.filename = filename
252 252 self.path = path
253 253 self.full_filename = ''
254 254 self.data = None
255 255
256 256 def load_config(self):
257 257 """Load the config from a file and return it as a Struct."""
258 258 self.clear()
259 259 self._find_file()
260 260 self._read_file_as_dict()
261 261 self._convert_to_config()
262 262 return self.config
263 263
264 264 def _find_file(self):
265 265 """Try to find the file by searching the paths."""
266 266 self.full_filename = filefind(self.filename, self.path)
267 267
268 268 def _read_file_as_dict(self):
269 269 """Load the config file into self.config, with recursive loading."""
270 270 # This closure is made available in the namespace that is used
271 271 # to exec the config file. This allows users to call
272 272 # load_subconfig('myconfig.py') to load config files recursively.
273 273 # It needs to be a closure because it has references to self.path
274 274 # and self.config. The sub-config is loaded with the same path
275 275 # as the parent, but it uses an empty config which is then merged
276 276 # with the parents.
277 277 def load_subconfig(fname):
278 278 loader = PyFileConfigLoader(fname, self.path)
279 279 try:
280 280 sub_config = loader.load_config()
281 281 except IOError:
282 282 # Pass silently if the sub config is not there. This happens
283 283 # when a user us using a profile, but not the default config.
284 284 pass
285 285 else:
286 286 self.config._merge(sub_config)
287 287
288 288 # Again, this needs to be a closure and should be used in config
289 289 # files to get the config being loaded.
290 290 def get_config():
291 291 return self.config
292 292
293 293 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
294 294 fs_encoding = sys.getfilesystemencoding() or 'ascii'
295 295 conf_filename = self.full_filename.encode(fs_encoding)
296 296 execfile(conf_filename, namespace)
297 297
298 298 def _convert_to_config(self):
299 299 if self.data is None:
300 300 ConfigLoaderError('self.data does not exist')
301 301
302 302
303 303 class CommandLineConfigLoader(ConfigLoader):
304 304 """A config loader for command line arguments.
305 305
306 306 As we add more command line based loaders, the common logic should go
307 307 here.
308 308 """
309 309
310 310 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.+')
311 311 flag_pattern = re.compile(r'\-\-\w+(\-\w)*')
312 312
313 313 class KeyValueConfigLoader(CommandLineConfigLoader):
314 314 """A config loader that loads key value pairs from the command line.
315 315
316 316 This allows command line options to be gives in the following form::
317 317
318 318 ipython Global.profile="foo" InteractiveShell.autocall=False
319 319 """
320 320
321 321 def __init__(self, argv=None, aliases=None, flags=None):
322 322 """Create a key value pair config loader.
323 323
324 324 Parameters
325 325 ----------
326 326 argv : list
327 327 A list that has the form of sys.argv[1:] which has unicode
328 328 elements of the form u"key=value". If this is None (default),
329 329 then sys.argv[1:] will be used.
330 330 aliases : dict
331 331 A dict of aliases for configurable traits.
332 332 Keys are the short aliases, Values are the resolved trait.
333 333 Of the form: `{'alias' : 'Configurable.trait'}`
334 334 flags : dict
335 335 A dict of flags, keyed by str name. Vaues can be Config objects,
336 336 dicts, or "key=value" strings. If Config or dict, when the flag
337 337 is triggered, The flag is loaded as `self.config.update(m)`.
338 338
339 339 Returns
340 340 -------
341 341 config : Config
342 342 The resulting Config object.
343 343
344 344 Examples
345 345 --------
346 346
347 347 >>> from IPython.config.loader import KeyValueConfigLoader
348 348 >>> cl = KeyValueConfigLoader()
349 349 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
350 350 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
351 351 """
352 352 if argv is None:
353 353 argv = sys.argv[1:]
354 354 self.argv = argv
355 355 self.aliases = aliases or {}
356 356 self.flags = flags or {}
357 357
358 358 def load_config(self, argv=None, aliases=None, flags=None):
359 359 """Parse the configuration and generate the Config object.
360 360
361 361 Parameters
362 362 ----------
363 363 argv : list, optional
364 364 A list that has the form of sys.argv[1:] which has unicode
365 365 elements of the form u"key=value". If this is None (default),
366 366 then self.argv will be used.
367 367 aliases : dict
368 368 A dict of aliases for configurable traits.
369 369 Keys are the short aliases, Values are the resolved trait.
370 370 Of the form: `{'alias' : 'Configurable.trait'}`
371 371 flags : dict
372 372 A dict of flags, keyed by str name. Values can be Config objects
373 373 or dicts. When the flag is triggered, The config is loaded as
374 374 `self.config.update(cfg)`.
375 375 """
376 376 from IPython.config.configurable import Configurable
377 377
378 378 self.clear()
379 379 if argv is None:
380 380 argv = self.argv
381 381 if aliases is None:
382 382 aliases = self.aliases
383 383 if flags is None:
384 384 flags = self.flags
385 385
386 386 for item in argv:
387 387 if kv_pattern.match(item):
388 388 lhs,rhs = item.split('=',1)
389 389 # Substitute longnames for aliases.
390 390 if lhs in aliases:
391 391 lhs = aliases[lhs]
392 392 exec_str = 'self.config.' + lhs + '=' + rhs
393 393 try:
394 394 # Try to see if regular Python syntax will work. This
395 395 # won't handle strings as the quote marks are removed
396 396 # by the system shell.
397 397 exec exec_str in locals(), globals()
398 398 except (NameError, SyntaxError):
399 399 # This case happens if the rhs is a string but without
400 400 # the quote marks. We add the quote marks and see if
401 401 # it succeeds. If it still fails, we let it raise.
402 402 exec_str = 'self.config.' + lhs + '="' + rhs + '"'
403 403 exec exec_str in locals(), globals()
404 404 elif flag_pattern.match(item):
405 405 # trim leading '--'
406 406 m = item[2:]
407 407 cfg,_ = flags.get(m, (None,None))
408 408 if cfg is None:
409 409 raise ArgumentError("Unrecognized flag: %r"%item)
410 410 elif isinstance(cfg, (dict, Config)):
411 # update self.config with Config:
412 self.config.update(cfg)
411 # don't clobber whole config sections, update
412 # each section from config:
413 for sec,c in cfg.iteritems():
414 self.config[sec].update(c)
413 415 else:
414 416 raise ValueError("Invalid flag: %r"%flag)
415 417 else:
416 418 raise ArgumentError("Invalid argument: %r"%item)
417 419 return self.config
418 420
419 421 class ArgParseConfigLoader(CommandLineConfigLoader):
420 422 """A loader that uses the argparse module to load from the command line."""
421 423
422 424 def __init__(self, argv=None, *parser_args, **parser_kw):
423 425 """Create a config loader for use with argparse.
424 426
425 427 Parameters
426 428 ----------
427 429
428 430 argv : optional, list
429 431 If given, used to read command-line arguments from, otherwise
430 432 sys.argv[1:] is used.
431 433
432 434 parser_args : tuple
433 435 A tuple of positional arguments that will be passed to the
434 436 constructor of :class:`argparse.ArgumentParser`.
435 437
436 438 parser_kw : dict
437 439 A tuple of keyword arguments that will be passed to the
438 440 constructor of :class:`argparse.ArgumentParser`.
439 441
440 442 Returns
441 443 -------
442 444 config : Config
443 445 The resulting Config object.
444 446 """
445 447 super(CommandLineConfigLoader, self).__init__()
446 448 if argv == None:
447 449 argv = sys.argv[1:]
448 450 self.argv = argv
449 451 self.parser_args = parser_args
450 452 self.version = parser_kw.pop("version", None)
451 453 kwargs = dict(argument_default=argparse.SUPPRESS)
452 454 kwargs.update(parser_kw)
453 455 self.parser_kw = kwargs
454 456
455 457 def load_config(self, argv=None):
456 458 """Parse command line arguments and return as a Config object.
457 459
458 460 Parameters
459 461 ----------
460 462
461 463 args : optional, list
462 464 If given, a list with the structure of sys.argv[1:] to parse
463 465 arguments from. If not given, the instance's self.argv attribute
464 466 (given at construction time) is used."""
465 467 self.clear()
466 468 if argv is None:
467 469 argv = self.argv
468 470 self._create_parser()
469 471 self._parse_args(argv)
470 472 self._convert_to_config()
471 473 return self.config
472 474
473 475 def get_extra_args(self):
474 476 if hasattr(self, 'extra_args'):
475 477 return self.extra_args
476 478 else:
477 479 return []
478 480
479 481 def _create_parser(self):
480 482 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
481 483 self._add_arguments()
482 484
483 485 def _add_arguments(self):
484 486 raise NotImplementedError("subclasses must implement _add_arguments")
485 487
486 488 def _parse_args(self, args):
487 489 """self.parser->self.parsed_data"""
488 490 # decode sys.argv to support unicode command-line options
489 491 uargs = []
490 492 for a in args:
491 493 if isinstance(a, str):
492 494 # don't decode if we already got unicode
493 495 a = a.decode(sys.stdin.encoding or
494 496 sys.getdefaultencoding())
495 497 uargs.append(a)
496 498 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
497 499
498 500 def _convert_to_config(self):
499 501 """self.parsed_data->self.config"""
500 502 for k, v in vars(self.parsed_data).iteritems():
501 503 exec_str = 'self.config.' + k + '= v'
502 504 exec exec_str in locals(), globals()
503 505
504 506
@@ -1,105 +1,129 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 b = Int(0, config=True, help="The integer b.")
45 46 enabled = Bool(True, config=True, help="Enable bar.")
46 47
47 48
48 49 class MyApp(Application):
49 50
50 51 name = Unicode(u'myapp')
51 52 running = Bool(False, config=True,
52 53 help="Is the app running?")
53 54 classes = List([Bar, Foo])
54 55 config_file = Unicode(u'', config=True,
55 56 help="Load this config file")
56 57
57 58 aliases = Dict(dict(i='Foo.i',j='Foo.j',name='Foo.name',
58 59 enabled='Bar.enabled', log_level='MyApp.log_level'))
59 60
60 61 flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"),
61 62 disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False")))
62 63
63 64 def init_foo(self):
64 65 self.foo = Foo(config=self.config)
65 66
66 67 def init_bar(self):
67 68 self.bar = Bar(config=self.config)
68 69
69 70
70 71 class TestApplication(TestCase):
71 72
72 73 def test_basic(self):
73 74 app = MyApp()
74 75 self.assertEquals(app.name, u'myapp')
75 76 self.assertEquals(app.running, False)
76 77 self.assertEquals(app.classes, [MyApp,Bar,Foo])
77 78 self.assertEquals(app.config_file, u'')
78 79
79 80 def test_config(self):
80 81 app = MyApp()
81 82 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=50"])
82 83 config = app.config
83 84 self.assertEquals(config.Foo.i, 10)
84 85 self.assertEquals(config.Foo.j, 10)
85 86 self.assertEquals(config.Bar.enabled, False)
86 87 self.assertEquals(config.MyApp.log_level,50)
87 88
88 89 def test_config_propagation(self):
89 90 app = MyApp()
90 91 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=50"])
91 92 app.init_foo()
92 93 app.init_bar()
93 94 self.assertEquals(app.foo.i, 10)
94 95 self.assertEquals(app.foo.j, 10)
95 96 self.assertEquals(app.bar.enabled, False)
96 97
97 def test_alias(self):
98 def test_flags(self):
98 99 app = MyApp()
99 100 app.parse_command_line(["--disable"])
100 101 app.init_bar()
101 102 self.assertEquals(app.bar.enabled, False)
102 103 app.parse_command_line(["--enable"])
103 104 app.init_bar()
104 105 self.assertEquals(app.bar.enabled, True)
105 106
107 def test_aliases(self):
108 app = MyApp()
109 app.parse_command_line(["i=5", "j=10"])
110 app.init_foo()
111 self.assertEquals(app.foo.i, 5)
112 app.init_foo()
113 self.assertEquals(app.foo.j, 10)
114
115 def test_flag_clobber(self):
116 """test that setting flags doesn't clobber existing settings"""
117 app = MyApp()
118 app.parse_command_line(["Bar.b=5", "--disable"])
119 print app.config
120 app.init_bar()
121 self.assertEquals(app.bar.enabled, False)
122 self.assertEquals(app.bar.b, 5)
123 app.parse_command_line(["--enable", "Bar.b=10"])
124 print app.config
125 app.init_bar()
126 self.assertEquals(app.bar.enabled, True)
127 self.assertEquals(app.bar.b, 10)
128
129
@@ -1,435 +1,435 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for IPython.
4 4
5 5 All top-level applications should use the classes in this module for
6 6 handling configuration and creating componenets.
7 7
8 8 The job of an :class:`Application` is to create the master configuration
9 9 object and then create the configurable objects, passing the config to them.
10 10
11 11 Authors:
12 12
13 13 * Brian Granger
14 14 * Fernando Perez
15 15
16 16 Notes
17 17 -----
18 18 """
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Copyright (C) 2008-2009 The IPython Development Team
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #-----------------------------------------------------------------------------
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Imports
29 29 #-----------------------------------------------------------------------------
30 30
31 31 import logging
32 32 import os
33 33 import shutil
34 34 import sys
35 35
36 36 from IPython.config.application import Application
37 37 from IPython.config.configurable import Configurable
38 38 from IPython.config.loader import Config
39 39 from IPython.core import release, crashhandler
40 40 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, expand_path
41 41 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Classes and functions
45 45 #-----------------------------------------------------------------------------
46 46
47 47
48 48 #-----------------------------------------------------------------------------
49 49 # Module errors
50 50 #-----------------------------------------------------------------------------
51 51
52 52 class ProfileDirError(Exception):
53 53 pass
54 54
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Class for managing profile directories
58 58 #-----------------------------------------------------------------------------
59 59
60 60 class ProfileDir(Configurable):
61 61 """An object to manage the profile directory and its resources.
62 62
63 63 The profile directory is used by all IPython applications, to manage
64 64 configuration, logging and security.
65 65
66 66 This object knows how to find, create and manage these directories. This
67 67 should be used by any code that wants to handle profiles.
68 68 """
69 69
70 70 security_dir_name = Unicode('security')
71 71 log_dir_name = Unicode('log')
72 72 pid_dir_name = Unicode('pid')
73 73 security_dir = Unicode(u'')
74 74 log_dir = Unicode(u'')
75 75 pid_dir = Unicode(u'')
76 76
77 77 location = Unicode(u'', config=True,
78 78 help="""Set the profile location directly. This overrides the logic used by the
79 79 `profile` option.""",
80 80 )
81 81
82 82 _location_isset = Bool(False) # flag for detecting multiply set location
83 83
84 84 def _location_changed(self, name, old, new):
85 85 if self._location_isset:
86 86 raise RuntimeError("Cannot set profile location more than once.")
87 87 self._location_isset = True
88 88 if not os.path.isdir(new):
89 89 os.makedirs(new)
90 90
91 91 # ensure config files exist:
92 92 self.security_dir = os.path.join(new, self.security_dir_name)
93 93 self.log_dir = os.path.join(new, self.log_dir_name)
94 94 self.pid_dir = os.path.join(new, self.pid_dir_name)
95 95 self.check_dirs()
96 96
97 97 def _log_dir_changed(self, name, old, new):
98 98 self.check_log_dir()
99 99
100 100 def check_log_dir(self):
101 101 if not os.path.isdir(self.log_dir):
102 102 os.mkdir(self.log_dir)
103 103
104 104 def _security_dir_changed(self, name, old, new):
105 105 self.check_security_dir()
106 106
107 107 def check_security_dir(self):
108 108 if not os.path.isdir(self.security_dir):
109 109 os.mkdir(self.security_dir, 0700)
110 110 else:
111 111 os.chmod(self.security_dir, 0700)
112 112
113 113 def _pid_dir_changed(self, name, old, new):
114 114 self.check_pid_dir()
115 115
116 116 def check_pid_dir(self):
117 117 if not os.path.isdir(self.pid_dir):
118 118 os.mkdir(self.pid_dir, 0700)
119 119 else:
120 120 os.chmod(self.pid_dir, 0700)
121 121
122 122 def check_dirs(self):
123 123 self.check_security_dir()
124 124 self.check_log_dir()
125 125 self.check_pid_dir()
126 126
127 127 def copy_config_file(self, config_file, path=None, overwrite=False):
128 128 """Copy a default config file into the active profile directory.
129 129
130 130 Default configuration files are kept in :mod:`IPython.config.default`.
131 131 This function moves these from that location to the working profile
132 132 directory.
133 133 """
134 134 dst = os.path.join(self.location, config_file)
135 135 if os.path.isfile(dst) and not overwrite:
136 136 return
137 137 if path is None:
138 138 path = os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
139 139 src = os.path.join(path, config_file)
140 140 shutil.copy(src, dst)
141 141
142 142 @classmethod
143 143 def create_profile_dir(cls, profile_dir, config=None):
144 144 """Create a new profile directory given a full path.
145 145
146 146 Parameters
147 147 ----------
148 148 profile_dir : str
149 149 The full path to the profile directory. If it does exist, it will
150 150 be used. If not, it will be created.
151 151 """
152 152 return cls(location=profile_dir, config=config)
153 153
154 154 @classmethod
155 155 def create_profile_dir_by_name(cls, path, name=u'default', config=None):
156 156 """Create a profile dir by profile name and path.
157 157
158 158 Parameters
159 159 ----------
160 160 path : unicode
161 161 The path (directory) to put the profile directory in.
162 162 name : unicode
163 163 The name of the profile. The name of the profile directory will
164 164 be "profile_<profile>".
165 165 """
166 166 if not os.path.isdir(path):
167 167 raise ProfileDirError('Directory not found: %s' % path)
168 168 profile_dir = os.path.join(path, u'profile_' + name)
169 169 return cls(location=profile_dir, config=config)
170 170
171 171 @classmethod
172 172 def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
173 173 """Find an existing profile dir by profile name, return its ProfileDir.
174 174
175 175 This searches through a sequence of paths for a profile dir. If it
176 176 is not found, a :class:`ProfileDirError` exception will be raised.
177 177
178 178 The search path algorithm is:
179 179 1. ``os.getcwd()``
180 180 2. ``ipython_dir``
181 181 3. The directories found in the ":" separated
182 182 :env:`IPCLUSTER_DIR_PATH` environment variable.
183 183
184 184 Parameters
185 185 ----------
186 186 ipython_dir : unicode or str
187 187 The IPython directory to use.
188 188 name : unicode or str
189 189 The name of the profile. The name of the profile directory
190 190 will be "profile_<profile>".
191 191 """
192 192 dirname = u'profile_' + name
193 193 profile_dir_paths = os.environ.get('IPYTHON_PROFILE_PATH','')
194 194 if profile_dir_paths:
195 195 profile_dir_paths = profile_dir_paths.split(os.pathsep)
196 196 else:
197 197 profile_dir_paths = []
198 198 paths = [os.getcwd(), ipython_dir] + profile_dir_paths
199 199 for p in paths:
200 200 profile_dir = os.path.join(p, dirname)
201 201 if os.path.isdir(profile_dir):
202 202 return cls(location=profile_dir, config=config)
203 203 else:
204 204 raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
205 205
206 206 @classmethod
207 207 def find_profile_dir(cls, profile_dir, config=None):
208 208 """Find/create a profile dir and return its ProfileDir.
209 209
210 210 This will create the profile directory if it doesn't exist.
211 211
212 212 Parameters
213 213 ----------
214 214 profile_dir : unicode or str
215 215 The path of the profile directory. This is expanded using
216 216 :func:`IPython.utils.genutils.expand_path`.
217 217 """
218 218 profile_dir = expand_path(profile_dir)
219 219 if not os.path.isdir(profile_dir):
220 220 raise ProfileDirError('Profile directory not found: %s' % profile_dir)
221 221 return cls(location=profile_dir, config=config)
222 222
223 223
224 224 #-----------------------------------------------------------------------------
225 225 # Base Application Class
226 226 #-----------------------------------------------------------------------------
227 227
228 228 # aliases and flags
229 229
230 230 base_aliases = dict(
231 231 profile='BaseIPythonApplication.profile',
232 232 ipython_dir='BaseIPythonApplication.ipython_dir',
233 233 )
234 234
235 235 base_flags = dict(
236 debug = ({'Application' : Config({'log_level' : logging.DEBUG})},
236 debug = ({'Application' : {'log_level' : logging.DEBUG}},
237 237 "set log level to logging.DEBUG (maximize logging output)"),
238 quiet = ({'Application' : Config({'log_level' : logging.CRITICAL})},
238 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
239 239 "set log level to logging.CRITICAL (minimize logging output)"),
240 init = ({'BaseIPythonApplication' : Config({
240 init = ({'BaseIPythonApplication' : {
241 241 'copy_config_files' : True,
242 'auto_create' : True})
242 'auto_create' : True}
243 243 }, "Initialize profile with default config files")
244 244 )
245 245
246 246
247 247 class BaseIPythonApplication(Application):
248 248
249 249 name = Unicode(u'ipython')
250 250 description = Unicode(u'IPython: an enhanced interactive Python shell.')
251 251 version = Unicode(release.version)
252 252
253 253 aliases = Dict(base_aliases)
254 254 flags = Dict(base_flags)
255 255
256 256 # Track whether the config_file has changed,
257 257 # because some logic happens only if we aren't using the default.
258 258 config_file_specified = Bool(False)
259 259
260 260 config_file_name = Unicode(u'ipython_config.py')
261 261 def _config_file_name_changed(self, name, old, new):
262 262 if new != old:
263 263 self.config_file_specified = True
264 264
265 265 # The directory that contains IPython's builtin profiles.
266 266 builtin_profile_dir = Unicode(
267 267 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
268 268 )
269 269
270 270 config_file_paths = List(Unicode)
271 271 def _config_file_paths_default(self):
272 272 return [os.getcwdu()]
273 273
274 274 profile = Unicode(u'default', config=True,
275 275 help="""The IPython profile to use."""
276 276 )
277 277 def _profile_changed(self, name, old, new):
278 278 self.builtin_profile_dir = os.path.join(
279 279 get_ipython_package_dir(), u'config', u'profile', new
280 280 )
281 281
282 282
283 283 ipython_dir = Unicode(get_ipython_dir(), config=True,
284 284 help="""
285 285 The name of the IPython directory. This directory is used for logging
286 286 configuration (through profiles), history storage, etc. The default
287 287 is usually $HOME/.ipython. This options can also be specified through
288 288 the environment variable IPYTHON_DIR.
289 289 """
290 290 )
291 291
292 292 overwrite = Bool(False, config=True,
293 293 help="""Whether to overwrite existing config files when copying""")
294 294 auto_create = Bool(False, config=True,
295 295 help="""Whether to create profile dir if it doesn't exist""")
296 296
297 297 config_files = List(Unicode)
298 298 def _config_files_default(self):
299 299 return [u'ipython_config.py']
300 300
301 301 copy_config_files = Bool(False, config=True,
302 302 help="""Whether to copy the default config files into the profile dir.""")
303 303
304 304 # The class to use as the crash handler.
305 305 crash_handler_class = Type(crashhandler.CrashHandler)
306 306
307 307 def __init__(self, **kwargs):
308 308 super(BaseIPythonApplication, self).__init__(**kwargs)
309 309 # ensure even default IPYTHON_DIR exists
310 310 if not os.path.exists(self.ipython_dir):
311 311 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
312 312
313 313 #-------------------------------------------------------------------------
314 314 # Various stages of Application creation
315 315 #-------------------------------------------------------------------------
316 316
317 317 def init_crash_handler(self):
318 318 """Create a crash handler, typically setting sys.excepthook to it."""
319 319 self.crash_handler = self.crash_handler_class(self)
320 320 sys.excepthook = self.crash_handler
321 321
322 322 def _ipython_dir_changed(self, name, old, new):
323 323 if old in sys.path:
324 324 sys.path.remove(old)
325 325 sys.path.append(os.path.abspath(new))
326 326 if not os.path.isdir(new):
327 327 os.makedirs(new, mode=0777)
328 328 readme = os.path.join(new, 'README')
329 329 if not os.path.exists(readme):
330 330 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
331 331 shutil.copy(os.path.join(path, 'README'), readme)
332 332 self.log.debug("IPYTHON_DIR set to: %s" % new)
333 333
334 334 def load_config_file(self, suppress_errors=True):
335 335 """Load the config file.
336 336
337 337 By default, errors in loading config are handled, and a warning
338 338 printed on screen. For testing, the suppress_errors option is set
339 339 to False, so errors will make tests fail.
340 340 """
341 341 self.log.debug("Attempting to load config file: %s" %
342 342 self.config_file_name)
343 343 try:
344 344 Application.load_config_file(
345 345 self,
346 346 self.config_file_name,
347 347 path=self.config_file_paths
348 348 )
349 349 except IOError:
350 350 # Only warn if the default config file was NOT being used.
351 351 if self.config_file_specified:
352 352 self.log.warn("Config file not found, skipping: %s" %
353 353 self.config_file_name)
354 354 except:
355 355 # For testing purposes.
356 356 if not suppress_errors:
357 357 raise
358 358 self.log.warn("Error loading config file: %s" %
359 359 self.config_file_name, exc_info=True)
360 360
361 361 def init_profile_dir(self):
362 362 """initialize the profile dir"""
363 363 try:
364 364 # location explicitly specified:
365 365 location = self.config.ProfileDir.location
366 366 except AttributeError:
367 367 # location not specified, find by profile name
368 368 try:
369 369 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
370 370 except ProfileDirError:
371 371 # not found, maybe create it (always create default profile)
372 372 if self.auto_create or self.profile=='default':
373 373 try:
374 374 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
375 375 except ProfileDirError:
376 376 self.log.fatal("Could not create profile: %r"%self.profile)
377 377 self.exit(1)
378 378 else:
379 379 self.log.info("Created profile dir: %r"%p.location)
380 380 else:
381 381 self.log.fatal("Profile %r not found."%self.profile)
382 382 self.exit(1)
383 383 else:
384 384 self.log.info("Using existing profile dir: %r"%p.location)
385 385 else:
386 386 # location is fully specified
387 387 try:
388 388 p = ProfileDir.find_profile_dir(location, self.config)
389 389 except ProfileDirError:
390 390 # not found, maybe create it
391 391 if self.auto_create:
392 392 try:
393 393 p = ProfileDir.create_profile_dir(location, self.config)
394 394 except ProfileDirError:
395 395 self.log.fatal("Could not create profile directory: %r"%location)
396 396 self.exit(1)
397 397 else:
398 398 self.log.info("Creating new profile dir: %r"%location)
399 399 else:
400 400 self.log.fatal("Profile directory %r not found."%location)
401 401 self.exit(1)
402 402 else:
403 403 self.log.info("Using existing profile dir: %r"%location)
404 404
405 405 self.profile_dir = p
406 406 self.config_file_paths.append(p.location)
407 407
408 408 def init_config_files(self):
409 409 """[optionally] copy default config files into profile dir."""
410 410 # copy config files
411 411 if self.copy_config_files:
412 412 path = self.builtin_profile_dir
413 413 src = self.profile
414 414 if not os.path.exists(path):
415 415 # use default if new profile doesn't have a preset
416 416 path = None
417 417 src = 'default'
418 418
419 419 self.log.debug("Staging %s config files into %r [overwrite=%s]"%(
420 420 src, self.profile_dir.location, self.overwrite)
421 421 )
422 422
423 423 for cfg in self.config_files:
424 424 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
425 425
426 426 def initialize(self, argv=None):
427 427 self.init_crash_handler()
428 428 self.parse_command_line(argv)
429 429 cl_config = self.config
430 430 self.init_profile_dir()
431 431 self.init_config_files()
432 432 self.load_config_file()
433 433 # enforce cl-opts override configfile opts:
434 434 self.update_config(cl_config)
435 435
General Comments 0
You need to be logged in to leave comments. Login now