30
from copy import deepcopy
32
from IPython.utils.genutils import get_ipython_dir, filefind
35
from IPython.core import release, crashhandler
36
from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
33
37
from IPython.config.loader import (
34
38
PyFileConfigLoader,
35
39
ArgParseConfigLoader,
40
43
#-----------------------------------------------------------------------------
41
44
# Classes and functions
42
45
#-----------------------------------------------------------------------------
45
class IPythonArgParseConfigLoader(ArgParseConfigLoader):
46
"""Default command line options for IPython based applications."""
48
def _add_other_arguments(self):
49
self.parser.add_argument('-ipythondir',dest='Global.ipythondir',type=str,
50
help='Set to override default location of Global.ipythondir.',
51
default=NoConfigDefault,
52
metavar='Global.ipythondir')
53
self.parser.add_argument('-p','-profile',dest='Global.profile',type=str,
54
help='The string name of the ipython profile to be used.',
55
default=NoConfigDefault,
56
metavar='Global.profile')
57
self.parser.add_argument('-log_level',dest="Global.log_level",type=int,
58
help='Set the log level (0,10,20,30,40,50). Default is 30.',
59
default=NoConfigDefault)
60
self.parser.add_argument('-config_file',dest='Global.config_file',type=str,
61
help='Set the config file name to override default.',
62
default=NoConfigDefault,
63
metavar='Global.config_file')
66
47
class ApplicationError(Exception):
52
(('--ipython-dir', ), dict(
53
dest='Global.ipython_dir',type=unicode,
55
"""Set to override default location of the IPython directory
56
IPYTHON_DIR, stored as Global.ipython_dir. This can also be specified
57
through the environment variable IPYTHON_DIR.""",
58
metavar='Global.ipython_dir') ),
59
(('-p', '--profile',), dict(
60
dest='Global.profile',type=unicode,
62
"""The string name of the ipython profile to be used. Assume that your
63
config file is ipython_config-<name>.py (looks in current dir first,
64
then in IPYTHON_DIR). This is a quick way to keep and load multiple
65
config files for different tasks, especially if include your basic one
66
in your more specialized ones. You can keep a basic
67
IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
68
include this one and load extra things for particular tasks.""",
69
metavar='Global.profile') ),
70
(('--log-level',), dict(
71
dest="Global.log_level",type=int,
72
help='Set the log level (0,10,20,30,40,50). Default is 30.',
73
metavar='Global.log_level')),
74
(('--config-file',), dict(
75
dest='Global.config_file',type=unicode,
77
"""Set the config file name to override default. Normally IPython
78
loads ipython_config.py (from current directory) or
79
IPYTHON_DIR/ipython_config.py. If the loading of your config file
80
fails, IPython starts with a bare bones configuration (no modules
82
metavar='Global.config_file')),
70
85
class Application(object):
71
"""Load a config, construct an app and run it.
86
"""Load a config, construct components and set them running.
88
The configuration of an application can be done via four different Config
89
objects, which are loaded and ultimately merged into a single one used from
90
that point on by the app. These are:
92
1. default_config: internal defaults, implemented in code.
93
2. file_config: read from the filesystem.
94
3. command_line_config: read from the system's command line flags.
95
4. constructor_config: passed parametrically to the constructor.
97
During initialization, 3 is actually read before 2, since at the
98
command-line one may override the location of the file to be read. But the
99
above is the order in which the merge is made.
101
There is a final config object can be created and passed to the
102
constructor: override_config. If it exists, this completely overrides the
103
configs 2-4 above (the default is still used to ensure that all needed
104
fields at least are created). This makes it easier to create
105
parametrically (e.g. in testing or sphinx plugins) objects with a known
106
configuration, that are unaffected by whatever arguments may be present in
107
sys.argv or files in the user's various directories.
74
config_file_name = 'ipython_config.py'
111
description = 'IPython: an enhanced interactive Python shell.'
112
#: usage message printed by argparse. If None, auto-generate
114
config_file_name = u'ipython_config.py'
115
#: Track the default and actual separately because some messages are
116
#: only printed if we aren't using the default.
117
default_config_file_name = config_file_name
118
default_log_level = logging.WARN
119
#: Set by --profile option
121
#: User's ipython directory, typically ~/.ipython/
123
#: internal defaults, implemented in code.
124
default_config = None
125
#: read from the filesystem
127
#: read from the system's command line flags
128
command_line_config = None
129
#: passed parametrically to the constructor.
130
constructor_config = None
131
#: final override, if given supercedes file/command/constructor configs
132
override_config = None
133
#: A reference to the argv to be used (typically ends up being sys.argv[1:])
135
#: Default command line arguments. Subclasses should create a new tuple
136
#: that *includes* these.
137
cl_arguments = app_cl_args
139
#: extra arguments computed by the command-line loader
146
# Class choices for things that will be instantiated at runtime.
147
_CrashHandler = crashhandler.CrashHandler
149
def __init__(self, argv=None, constructor_config=None, override_config=None):
150
self.argv = sys.argv[1:] if argv is None else argv
151
self.constructor_config = constructor_config
152
self.override_config = override_config
78
153
self.init_logger()
79
self.default_config_file_name = self.config_file_name
81
155
def init_logger(self):
82
156
self.log = logging.getLogger(self.__class__.__name__)
83
157
# This is used as the default until the command line arguments are read.
84
self.log.setLevel(logging.WARN)
158
self.log.setLevel(self.default_log_level)
85
159
self._log_handler = logging.StreamHandler()
86
160
self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
87
161
self._log_handler.setFormatter(self._log_formatter)
96
170
log_level = property(_get_log_level, _set_log_level)
172
def initialize(self):
173
"""Initialize the application.
175
Loads all configuration information and sets all application state, but
176
does not start any relevant processing (typically some kind of event
179
Once this method has been called, the application is flagged as
180
initialized and the method becomes a no-op."""
182
if self._initialized:
185
# The first part is protected with an 'attempt' wrapper, that will log
186
# failures with the basic system traceback machinery. Once our crash
187
# handler is in place, we can let any subsequent exception propagate,
188
# as our handler will log it with much better detail than the default.
189
self.attempt(self.create_crash_handler)
191
# Configuration phase
192
# Default config (internally hardwired in application code)
193
self.create_default_config()
194
self.log_default_config()
195
self.set_default_config_log_level()
197
if self.override_config is None:
198
# Command-line config
199
self.pre_load_command_line_config()
200
self.load_command_line_config()
201
self.set_command_line_config_log_level()
202
self.post_load_command_line_config()
203
self.log_command_line_config()
205
# Find resources needed for filesystem access, using information from
207
self.find_ipython_dir()
208
self.find_resources()
209
self.find_config_file_name()
210
self.find_config_file_paths()
212
if self.override_config is None:
214
self.pre_load_file_config()
215
self.load_file_config()
216
self.set_file_config_log_level()
217
self.post_load_file_config()
218
self.log_file_config()
220
# Merge all config objects into a single one the app can then use
222
self.log_master_config()
227
self.post_construct()
229
# Done, flag as such and
230
self._initialized = True
99
233
"""Start the application."""
100
self.attempt(self.create_default_config)
101
self.attempt(self.pre_load_command_line_config)
102
self.attempt(self.load_command_line_config, action='abort')
103
self.attempt(self.post_load_command_line_config)
104
self.attempt(self.find_ipythondir)
105
self.attempt(self.find_config_file_name)
106
self.attempt(self.find_config_file_paths)
107
self.attempt(self.pre_load_file_config)
108
self.attempt(self.load_file_config)
109
self.attempt(self.post_load_file_config)
110
self.attempt(self.merge_configs)
111
self.attempt(self.pre_construct)
112
self.attempt(self.construct)
113
self.attempt(self.post_construct)
114
self.attempt(self.start_app)
116
237
#-------------------------------------------------------------------------
117
238
# Various stages of Application creation
118
239
#-------------------------------------------------------------------------
241
def create_crash_handler(self):
242
"""Create a crash handler, typically setting sys.excepthook to it."""
243
self.crash_handler = self._CrashHandler(self, self.name)
244
sys.excepthook = self.crash_handler
120
246
def create_default_config(self):
121
247
"""Create defaults that can't be set elsewhere.
126
252
we set them here. The Global section is for variables like this that
127
253
don't belong to a particular component.
129
self.default_config = Config()
130
self.default_config.Global.ipythondir = get_ipython_dir()
256
c.Global.ipython_dir = get_ipython_dir()
257
c.Global.log_level = self.log_level
258
self.default_config = c
260
def log_default_config(self):
131
261
self.log.debug('Default config loaded:')
132
262
self.log.debug(repr(self.default_config))
264
def set_default_config_log_level(self):
266
self.log_level = self.default_config.Global.log_level
267
except AttributeError:
268
# Fallback to the default_log_level class attribute
134
271
def create_command_line_config(self):
135
272
"""Create and return a command line config loader."""
136
return IPythonArgParseConfigLoader(description=self.name)
273
return ArgParseConfigLoader(self.argv, self.cl_arguments,
274
description=self.description,
275
version=release.version,
138
279
def pre_load_command_line_config(self):
139
280
"""Do actions just before loading the command line config."""
142
283
def load_command_line_config(self):
143
"""Load the command line config.
145
This method also sets ``self.debug``.
284
"""Load the command line config."""
148
285
loader = self.create_command_line_config()
149
286
self.command_line_config = loader.load_config()
150
287
self.extra_args = loader.get_extra_args()
289
def set_command_line_config_log_level(self):
153
291
self.log_level = self.command_line_config.Global.log_level
154
292
except AttributeError:
155
pass # Use existing value which is set in Application.init_logger.
295
def post_load_command_line_config(self):
296
"""Do actions just after loading the command line config."""
299
def log_command_line_config(self):
156
300
self.log.debug("Command line config loaded:")
157
301
self.log.debug(repr(self.command_line_config))
159
def post_load_command_line_config(self):
160
"""Do actions just after loading the command line config."""
163
def find_ipythondir(self):
303
def find_ipython_dir(self):
164
304
"""Set the IPython directory.
166
This sets ``self.ipythondir``, but the actual value that is passed
167
to the application is kept in either ``self.default_config`` or
168
``self.command_line_config``. This also added ``self.ipythondir`` to
169
``sys.path`` so config files there can be references by other config
306
This sets ``self.ipython_dir``, but the actual value that is passed to
307
the application is kept in either ``self.default_config`` or
308
``self.command_line_config``. This also adds ``self.ipython_dir`` to
309
``sys.path`` so config files there can be referenced by other config
174
self.ipythondir = self.command_line_config.Global.ipythondir
314
self.ipython_dir = self.command_line_config.Global.ipython_dir
175
315
except AttributeError:
176
self.ipythondir = self.default_config.Global.ipythondir
177
sys.path.append(os.path.abspath(self.ipythondir))
178
if not os.path.isdir(self.ipythondir):
179
os.makedirs(self.ipythondir, mode = 0777)
180
self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
316
self.ipython_dir = self.default_config.Global.ipython_dir
317
sys.path.append(os.path.abspath(self.ipython_dir))
318
if not os.path.isdir(self.ipython_dir):
319
os.makedirs(self.ipython_dir, mode=0777)
320
self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
322
def find_resources(self):
323
"""Find other resources that need to be in place.
325
Things like cluster directories need to be in place to find the
326
config file. These happen right after the IPython directory has
182
331
def find_config_file_name(self):
183
332
"""Find the config file name for this application.
185
If a profile has been set at the command line, this will resolve
186
it. The search paths for the config file are set in
187
:meth:`find_config_file_paths` and then passed to the config file
188
loader where they are resolved to an absolute path.
334
This must set ``self.config_file_name`` to the filename of the
335
config file to use (just the filename). The search paths for the
336
config file are set in :meth:`find_config_file_paths` and then passed
337
to the config file loader where they are resolved to an absolute path.
339
If a profile has been set at the command line, this will resolve it.
248
408
"""Do actions after the config file is loaded."""
411
def log_file_config(self):
412
if hasattr(self.file_config.Global, 'config_file'):
413
self.log.debug("Config file loaded: %s" %
414
self.file_config.Global.config_file)
415
self.log.debug(repr(self.file_config))
251
417
def merge_configs(self):
252
418
"""Merge the default, command line and file config objects."""
253
419
config = Config()
254
420
config._merge(self.default_config)
255
config._merge(self.file_config)
256
config._merge(self.command_line_config)
421
if self.override_config is None:
422
config._merge(self.file_config)
423
config._merge(self.command_line_config)
424
if self.constructor_config is not None:
425
config._merge(self.constructor_config)
427
config._merge(self.override_config)
428
# XXX fperez - propose to Brian we rename master_config to simply
429
# config, I think this is going to be heavily used in examples and
430
# application code and the name is shorter/easier to find/remember.
431
# For now, just alias it...
257
432
self.master_config = config
435
def log_master_config(self):
258
436
self.log.debug("Master config created:")
259
437
self.log.debug(repr(self.master_config))