##// END OF EJS Templates
don't crash apps on TraitErrors when parsing command-line
MinRK -
Show More
@@ -1,393 +1,393 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 Unicode, List, Int, Enum, Dict, Instance
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 52
53 53 Flags *always* begin with '--', never just one '-'.
54 54 """.strip() # trim newlines of front and back
55 55
56 56 alias_description = """
57 57 These are commonly set parameters, given abbreviated aliases for convenience.
58 58 They are set in the same `name=value` way as class parameters, where
59 59 <name> is replaced by the real parameter for which it is an alias.
60 60 """.strip() # trim newlines of front and back
61 61
62 62 keyvalue_description = """
63 63 Parameters are set from command-line arguments of the form:
64 64 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
65 65 This line is evaluated in Python, so simple expressions are allowed, e.g.
66 66 `C.a='range(3)'` For setting C.a=[0,1,2]
67 67 """.strip() # trim newlines of front and back
68 68
69 69 #-----------------------------------------------------------------------------
70 70 # Application class
71 71 #-----------------------------------------------------------------------------
72 72
73 73
74 74 class ApplicationError(Exception):
75 75 pass
76 76
77 77
78 78 class Application(SingletonConfigurable):
79 79 """A singleton application with full configuration support."""
80 80
81 81 # The name of the application, will usually match the name of the command
82 82 # line application
83 83 name = Unicode(u'application')
84 84
85 85 # The description of the application that is printed at the beginning
86 86 # of the help.
87 87 description = Unicode(u'This is an application.')
88 88 # default section descriptions
89 89 flag_description = Unicode(flag_description)
90 90 alias_description = Unicode(alias_description)
91 91 keyvalue_description = Unicode(keyvalue_description)
92 92
93 93
94 94 # A sequence of Configurable subclasses whose config=True attributes will
95 95 # be exposed at the command line.
96 96 classes = List([])
97 97
98 98 # The version string of this application.
99 99 version = Unicode(u'0.0')
100 100
101 101 # The log level for the application
102 102 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
103 103 default_value=logging.WARN,
104 104 config=True,
105 105 help="Set the log level by value or name.")
106 106 def _log_level_changed(self, name, old, new):
107 107 """Adjust the log level when log_level is set."""
108 108 if isinstance(new, basestring):
109 109 new = getattr(logging, new)
110 110 self.log_level = new
111 111 self.log.setLevel(new)
112 112
113 113 # the alias map for configurables
114 114 aliases = Dict(dict(log_level='Application.log_level'))
115 115
116 116 # flags for loading Configurables or store_const style flags
117 117 # flags are loaded from this dict by '--key' flags
118 118 # this must be a dict of two-tuples, the first element being the Config/dict
119 119 # and the second being the help string for the flag
120 120 flags = Dict()
121 121 def _flags_changed(self, name, old, new):
122 122 """ensure flags dict is valid"""
123 123 for key,value in new.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
128 128
129 129 # subcommands for launching other applications
130 130 # if this is not empty, this will be a parent Application
131 131 # this must be a dict of two-tuples,
132 132 # the first element being the application class/import string
133 133 # and the second being the help string for the subcommand
134 134 subcommands = Dict()
135 135 # parse_command_line will initialize a subapp, if requested
136 136 subapp = Instance('IPython.config.application.Application', allow_none=True)
137 137
138 138 # extra command-line arguments that don't set config values
139 139 extra_args = List(Unicode)
140 140
141 141
142 142 def __init__(self, **kwargs):
143 143 SingletonConfigurable.__init__(self, **kwargs)
144 144 # Add my class to self.classes so my attributes appear in command line
145 145 # options.
146 146 self.classes.insert(0, self.__class__)
147 147
148 148 self.init_logging()
149 149
150 150 def _config_changed(self, name, old, new):
151 151 SingletonConfigurable._config_changed(self, name, old, new)
152 152 self.log.debug('Config changed:')
153 153 self.log.debug(repr(new))
154 154
155 155 def init_logging(self):
156 156 """Start logging for this application.
157 157
158 158 The default is to log to stdout using a StreaHandler. The log level
159 159 starts at loggin.WARN, but this can be adjusted by setting the
160 160 ``log_level`` attribute.
161 161 """
162 162 self.log = logging.getLogger(self.__class__.__name__)
163 163 self.log.setLevel(self.log_level)
164 164 self._log_handler = logging.StreamHandler()
165 165 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
166 166 self._log_handler.setFormatter(self._log_formatter)
167 167 self.log.addHandler(self._log_handler)
168 168
169 169 def initialize(self, argv=None):
170 170 """Do the basic steps to configure me.
171 171
172 172 Override in subclasses.
173 173 """
174 174 self.parse_command_line(argv)
175 175
176 176
177 177 def start(self):
178 178 """Start the app mainloop.
179 179
180 180 Override in subclasses.
181 181 """
182 182 if self.subapp is not None:
183 183 return self.subapp.start()
184 184
185 185 def print_alias_help(self):
186 186 """Print the alias part of the help."""
187 187 if not self.aliases:
188 188 return
189 189
190 190 lines = ['Aliases']
191 191 lines.append('-'*len(lines[0]))
192 192 lines.append('')
193 193 for p in wrap_paragraphs(self.alias_description):
194 194 lines.append(p)
195 195 lines.append('')
196 196
197 197 classdict = {}
198 198 for cls in self.classes:
199 199 # include all parents (up to, but excluding Configurable) in available names
200 200 for c in cls.mro()[:-3]:
201 201 classdict[c.__name__] = c
202 202
203 203 for alias, longname in self.aliases.iteritems():
204 204 classname, traitname = longname.split('.',1)
205 205 cls = classdict[classname]
206 206
207 207 trait = cls.class_traits(config=True)[traitname]
208 208 help = cls.class_get_trait_help(trait)
209 209 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
210 210 lines.append(help)
211 211 lines.append('')
212 212 print '\n'.join(lines)
213 213
214 214 def print_flag_help(self):
215 215 """Print the flag part of the help."""
216 216 if not self.flags:
217 217 return
218 218
219 219 lines = ['Flags']
220 220 lines.append('-'*len(lines[0]))
221 221 lines.append('')
222 222 for p in wrap_paragraphs(self.flag_description):
223 223 lines.append(p)
224 224 lines.append('')
225 225
226 226 for m, (cfg,help) in self.flags.iteritems():
227 227 lines.append('--'+m)
228 228 lines.append(indent(dedent(help.strip())))
229 229 lines.append('')
230 230 print '\n'.join(lines)
231 231
232 232 def print_subcommands(self):
233 233 """Print the subcommand part of the help."""
234 234 if not self.subcommands:
235 235 return
236 236
237 237 lines = ["Subcommands"]
238 238 lines.append('-'*len(lines[0]))
239 239 for subc, (cls,help) in self.subcommands.iteritems():
240 240 lines.append("%s : %s"%(subc, cls))
241 241 if help:
242 242 lines.append(indent(dedent(help.strip())))
243 243 lines.append('')
244 244 print '\n'.join(lines)
245 245
246 246 def print_help(self, classes=False):
247 247 """Print the help for each Configurable class in self.classes.
248 248
249 249 If classes=False (the default), only flags and aliases are printed.
250 250 """
251 251 self.print_subcommands()
252 252 self.print_flag_help()
253 253 self.print_alias_help()
254 254
255 255 if classes:
256 256 if self.classes:
257 257 print "Class parameters"
258 258 print "----------------"
259 259 print
260 260 for p in wrap_paragraphs(self.keyvalue_description):
261 261 print p
262 262 print
263 263
264 264 for cls in self.classes:
265 265 cls.class_print_help()
266 266 print
267 267 else:
268 268 print "To see all available configurables, use `--help-all`"
269 269 print
270 270
271 271 def print_description(self):
272 272 """Print the application description."""
273 273 for p in wrap_paragraphs(self.description):
274 274 print p
275 275 print
276 276
277 277 def print_version(self):
278 278 """Print the version string."""
279 279 print self.version
280 280
281 281 def update_config(self, config):
282 282 """Fire the traits events when the config is updated."""
283 283 # Save a copy of the current config.
284 284 newconfig = deepcopy(self.config)
285 285 # Merge the new config into the current one.
286 286 newconfig._merge(config)
287 287 # Save the combined config as self.config, which triggers the traits
288 288 # events.
289 289 self.config = newconfig
290 290
291 291 def initialize_subcommand(self, subc, argv=None):
292 292 """Initialize a subcommand with argv."""
293 293 subapp,help = self.subcommands.get(subc)
294 294
295 295 if isinstance(subapp, basestring):
296 296 subapp = import_item(subapp)
297 297
298 298 # clear existing instances
299 299 self.__class__.clear_instance()
300 300 # instantiate
301 301 self.subapp = subapp.instance()
302 302 # and initialize subapp
303 303 self.subapp.initialize(argv)
304 304
305 305 def parse_command_line(self, argv=None):
306 306 """Parse the command line arguments."""
307 307 argv = sys.argv[1:] if argv is None else argv
308 308
309 309 if self.subcommands and len(argv) > 0:
310 310 # we have subcommands, and one may have been specified
311 311 subc, subargv = argv[0], argv[1:]
312 312 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
313 313 # it's a subcommand, and *not* a flag or class parameter
314 314 return self.initialize_subcommand(subc, subargv)
315 315
316 316 if '-h' in argv or '--help' in argv or '--help-all' in argv:
317 317 self.print_description()
318 318 self.print_help('--help-all' in argv)
319 319 self.exit(0)
320 320
321 321 if '--version' in argv:
322 322 self.print_version()
323 323 self.exit(0)
324 324
325 325 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
326 326 flags=self.flags)
327 327 try:
328 328 config = loader.load_config()
329 except ArgumentError as e:
330 self.log.fatal(str(e))
329 self.update_config(config)
330 except (TraitError, ArgumentError) as e:
331 331 self.print_description()
332 332 self.print_help()
333 self.log.fatal(str(e))
333 334 self.exit(1)
334 self.update_config(config)
335 335 # store unparsed args in extra_args
336 336 self.extra_args = loader.extra_args
337 337
338 338 def load_config_file(self, filename, path=None):
339 339 """Load a .py based config file by filename and path."""
340 340 loader = PyFileConfigLoader(filename, path=path)
341 341 config = loader.load_config()
342 342 self.update_config(config)
343 343
344 344 def generate_config_file(self):
345 345 """generate default config file from Configurables"""
346 346 lines = ["# Configuration file for %s."%self.name]
347 347 lines.append('')
348 348 lines.append('c = get_config()')
349 349 lines.append('')
350 350 for cls in self.classes:
351 351 lines.append(cls.class_config_section())
352 352 return '\n'.join(lines)
353 353
354 354 def exit(self, exit_status=0):
355 355 self.log.debug("Exiting application: %s" % self.name)
356 356 sys.exit(exit_status)
357 357
358 358 #-----------------------------------------------------------------------------
359 359 # utility functions, for convenience
360 360 #-----------------------------------------------------------------------------
361 361
362 362 def boolean_flag(name, configurable, set_help='', unset_help=''):
363 363 """Helper for building basic --trait, --no-trait flags.
364 364
365 365 Parameters
366 366 ----------
367 367
368 368 name : str
369 369 The name of the flag.
370 370 configurable : str
371 371 The 'Class.trait' string of the trait to be set/unset with the flag
372 372 set_help : unicode
373 373 help string for --name flag
374 374 unset_help : unicode
375 375 help string for --no-name flag
376 376
377 377 Returns
378 378 -------
379 379
380 380 cfg : dict
381 381 A dict with two keys: 'name', and 'no-name', for setting and unsetting
382 382 the trait, respectively.
383 383 """
384 384 # default helpstrings
385 385 set_help = set_help or "set %s=True"%configurable
386 386 unset_help = unset_help or "set %s=False"%configurable
387 387
388 388 cls,trait = configurable.split('.')
389 389
390 390 setter = {cls : {trait : True}}
391 391 unsetter = {cls : {trait : False}}
392 392 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
393 393
@@ -1,291 +1,292 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 * Min RK
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Copyright (C) 2008-2011 The IPython Development Team
21 21 #
22 22 # Distributed under the terms of the BSD License. The full license is in
23 23 # the file COPYING, distributed as part of this software.
24 24 #-----------------------------------------------------------------------------
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Imports
28 28 #-----------------------------------------------------------------------------
29 29
30 30 import logging
31 31 import os
32 32 import shutil
33 33 import sys
34 34
35 35 from IPython.config.application import Application
36 36 from IPython.config.configurable import Configurable
37 37 from IPython.config.loader import Config
38 38 from IPython.core import release, crashhandler
39 39 from IPython.core.profiledir import ProfileDir, ProfileDirError
40 40 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
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 # Base Application Class
50 50 #-----------------------------------------------------------------------------
51 51
52 52 # aliases and flags
53 53
54 54 base_aliases = dict(
55 55 profile='BaseIPythonApplication.profile',
56 56 ipython_dir='BaseIPythonApplication.ipython_dir',
57 57 log_level='Application.log_level',
58 58 )
59 59
60 60 base_flags = dict(
61 61 debug = ({'Application' : {'log_level' : logging.DEBUG}},
62 62 "set log level to logging.DEBUG (maximize logging output)"),
63 63 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
64 64 "set log level to logging.CRITICAL (minimize logging output)"),
65 65 init = ({'BaseIPythonApplication' : {
66 66 'copy_config_files' : True,
67 67 'auto_create' : True}
68 68 }, "Initialize profile with default config files")
69 69 )
70 70
71 71
72 72 class BaseIPythonApplication(Application):
73 73
74 74 name = Unicode(u'ipython')
75 75 description = Unicode(u'IPython: an enhanced interactive Python shell.')
76 76 version = Unicode(release.version)
77 77
78 78 aliases = Dict(base_aliases)
79 79 flags = Dict(base_flags)
80 80
81 81 # Track whether the config_file has changed,
82 82 # because some logic happens only if we aren't using the default.
83 83 config_file_specified = Bool(False)
84 84
85 85 config_file_name = Unicode(u'ipython_config.py')
86 86 def _config_file_name_default(self):
87 87 return self.name.replace('-','_') + u'_config.py'
88 88 def _config_file_name_changed(self, name, old, new):
89 89 if new != old:
90 90 self.config_file_specified = True
91 91
92 92 # The directory that contains IPython's builtin profiles.
93 93 builtin_profile_dir = Unicode(
94 94 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
95 95 )
96 96
97 97 config_file_paths = List(Unicode)
98 98 def _config_file_paths_default(self):
99 99 return [os.getcwdu()]
100 100
101 101 profile = Unicode(u'default', config=True,
102 102 help="""The IPython profile to use."""
103 103 )
104 104 def _profile_changed(self, name, old, new):
105 105 self.builtin_profile_dir = os.path.join(
106 106 get_ipython_package_dir(), u'config', u'profile', new
107 107 )
108 108
109 109 ipython_dir = Unicode(get_ipython_dir(), config=True,
110 110 help="""
111 111 The name of the IPython directory. This directory is used for logging
112 112 configuration (through profiles), history storage, etc. The default
113 113 is usually $HOME/.ipython. This options can also be specified through
114 114 the environment variable IPYTHON_DIR.
115 115 """
116 116 )
117 117
118 118 overwrite = Bool(False, config=True,
119 119 help="""Whether to overwrite existing config files when copying""")
120 120 auto_create = Bool(False, config=True,
121 121 help="""Whether to create profile dir if it doesn't exist""")
122 122
123 123 config_files = List(Unicode)
124 124 def _config_files_default(self):
125 125 return [u'ipython_config.py']
126 126
127 127 copy_config_files = Bool(False, config=True,
128 128 help="""Whether to install the default config files into the profile dir.
129 129 If a new profile is being created, and IPython contains config files for that
130 130 profile, then they will be staged into the new directory. Otherwise,
131 131 default config files will be automatically generated.
132 132 """)
133 133
134 134 # The class to use as the crash handler.
135 135 crash_handler_class = Type(crashhandler.CrashHandler)
136 136
137 137 def __init__(self, **kwargs):
138 138 super(BaseIPythonApplication, self).__init__(**kwargs)
139 139 # ensure even default IPYTHON_DIR exists
140 140 if not os.path.exists(self.ipython_dir):
141 141 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
142 142
143 143 #-------------------------------------------------------------------------
144 144 # Various stages of Application creation
145 145 #-------------------------------------------------------------------------
146 146
147 147 def init_crash_handler(self):
148 148 """Create a crash handler, typically setting sys.excepthook to it."""
149 149 self.crash_handler = self.crash_handler_class(self)
150 150 sys.excepthook = self.crash_handler
151 151
152 152 def _ipython_dir_changed(self, name, old, new):
153 153 if old in sys.path:
154 154 sys.path.remove(old)
155 155 sys.path.append(os.path.abspath(new))
156 156 if not os.path.isdir(new):
157 157 os.makedirs(new, mode=0777)
158 158 readme = os.path.join(new, 'README')
159 159 if not os.path.exists(readme):
160 160 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
161 161 shutil.copy(os.path.join(path, 'README'), readme)
162 162 self.log.debug("IPYTHON_DIR set to: %s" % new)
163 163
164 164 def load_config_file(self, suppress_errors=True):
165 165 """Load the config file.
166 166
167 167 By default, errors in loading config are handled, and a warning
168 168 printed on screen. For testing, the suppress_errors option is set
169 169 to False, so errors will make tests fail.
170 170 """
171 171 base_config = 'ipython_config.py'
172 172 self.log.debug("Attempting to load config file: %s" %
173 173 base_config)
174 174 try:
175 175 Application.load_config_file(
176 176 self,
177 177 base_config,
178 178 path=self.config_file_paths
179 179 )
180 180 except IOError:
181 181 # ignore errors loading parent
182 182 pass
183 183 if self.config_file_name == base_config:
184 184 # don't load secondary config
185 185 return
186 186 self.log.debug("Attempting to load config file: %s" %
187 187 self.config_file_name)
188 188 try:
189 189 Application.load_config_file(
190 190 self,
191 191 self.config_file_name,
192 192 path=self.config_file_paths
193 193 )
194 194 except IOError:
195 195 # Only warn if the default config file was NOT being used.
196 196 if self.config_file_specified:
197 197 self.log.warn("Config file not found, skipping: %s" %
198 198 self.config_file_name)
199 199 except:
200 200 # For testing purposes.
201 201 if not suppress_errors:
202 202 raise
203 203 self.log.warn("Error loading config file: %s" %
204 204 self.config_file_name, exc_info=True)
205 205
206 206 def init_profile_dir(self):
207 207 """initialize the profile dir"""
208 208 try:
209 209 # location explicitly specified:
210 210 location = self.config.ProfileDir.location
211 211 except AttributeError:
212 212 # location not specified, find by profile name
213 213 try:
214 214 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
215 215 except ProfileDirError:
216 216 # not found, maybe create it (always create default profile)
217 217 if self.auto_create or self.profile=='default':
218 218 try:
219 219 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
220 220 except ProfileDirError:
221 221 self.log.fatal("Could not create profile: %r"%self.profile)
222 222 self.exit(1)
223 223 else:
224 224 self.log.info("Created profile dir: %r"%p.location)
225 225 else:
226 226 self.log.fatal("Profile %r not found."%self.profile)
227 227 self.exit(1)
228 228 else:
229 229 self.log.info("Using existing profile dir: %r"%p.location)
230 230 else:
231 231 # location is fully specified
232 232 try:
233 233 p = ProfileDir.find_profile_dir(location, self.config)
234 234 except ProfileDirError:
235 235 # not found, maybe create it
236 236 if self.auto_create:
237 237 try:
238 238 p = ProfileDir.create_profile_dir(location, self.config)
239 239 except ProfileDirError:
240 240 self.log.fatal("Could not create profile directory: %r"%location)
241 241 self.exit(1)
242 242 else:
243 243 self.log.info("Creating new profile dir: %r"%location)
244 244 else:
245 245 self.log.fatal("Profile directory %r not found."%location)
246 246 self.exit(1)
247 247 else:
248 248 self.log.info("Using existing profile dir: %r"%location)
249 249
250 250 self.profile_dir = p
251 251 self.config_file_paths.append(p.location)
252 252
253 253 def init_config_files(self):
254 254 """[optionally] copy default config files into profile dir."""
255 255 # copy config files
256 256 if self.copy_config_files:
257 257 path = self.builtin_profile_dir
258 258 src = self.profile
259 259
260 260 cfg = self.config_file_name
261 261 if path and os.path.exists(os.path.join(path, cfg)):
262 262 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
263 263 cfg, src, self.profile_dir.location, self.overwrite)
264 264 )
265 265 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
266 266 else:
267 267 self.stage_default_config_file()
268 268
269 269 def stage_default_config_file(self):
270 270 """auto generate default config file, and stage it into the profile."""
271 271 s = self.generate_config_file()
272 272 fname = os.path.join(self.profile_dir.location, self.config_file_name)
273 273 if self.overwrite or not os.path.exists(fname):
274 274 self.log.warn("Generating default config file: %r"%(fname))
275 275 with open(fname, 'w') as f:
276 276 f.write(s)
277 277
278 278
279 279 def initialize(self, argv=None):
280 self.init_crash_handler()
280 # don't hook up crash handler before parsing command-line
281 281 self.parse_command_line(argv)
282 self.init_crash_handler()
282 283 if self.subapp is not None:
283 284 # stop here if subapp is taking over
284 285 return
285 286 cl_config = self.config
286 287 self.init_profile_dir()
287 288 self.init_config_files()
288 289 self.load_config_file()
289 290 # enforce cl-opts override configfile opts:
290 291 self.update_config(cl_config)
291 292
General Comments 0
You need to be logged in to leave comments. Login now