##// END OF EJS Templates
include {sys.prefix}/etc/ipython in config path
Min RK -
Show More
@@ -1,453 +1,462 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 configurables.
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
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import atexit
16 16 from copy import deepcopy
17 17 import glob
18 18 import logging
19 19 import os
20 20 import shutil
21 21 import sys
22 22
23 23 from traitlets.config.application import Application, catch_config_error
24 24 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
25 25 from IPython.core import release, crashhandler
26 26 from IPython.core.profiledir import ProfileDir, ProfileDirError
27 27 from IPython.paths import get_ipython_dir, get_ipython_package_dir
28 28 from IPython.utils.path import ensure_dir_exists
29 29 from traitlets import (
30 30 List, Unicode, Type, Bool, Dict, Set, Instance, Undefined,
31 31 default, observe,
32 32 )
33 33
34 34 if os.name == 'nt':
35 35 programdata = os.environ.get('PROGRAMDATA', None)
36 36 if programdata:
37 37 SYSTEM_CONFIG_DIRS = [os.path.join(programdata, 'ipython')]
38 38 else: # PROGRAMDATA is not defined by default on XP.
39 39 SYSTEM_CONFIG_DIRS = []
40 40 else:
41 41 SYSTEM_CONFIG_DIRS = [
42 42 "/usr/local/etc/ipython",
43 43 "/etc/ipython",
44 44 ]
45 45
46
47 ENV_CONFIG_DIRS = []
48 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
49 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
50 # only add ENV_CONFIG if sys.prefix is not already included
51 ENV_CONFIG_DIRS.append(_env_config_dir)
52
53
46 54 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
47 55 if _envvar in {None, ''}:
48 56 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
49 57 else:
50 58 if _envvar.lower() in {'1','true'}:
51 59 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
52 60 elif _envvar.lower() in {'0','false'} :
53 61 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
54 62 else:
55 63 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
56 64
57 65 # aliases and flags
58 66
59 67 base_aliases = {
60 68 'profile-dir' : 'ProfileDir.location',
61 69 'profile' : 'BaseIPythonApplication.profile',
62 70 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
63 71 'log-level' : 'Application.log_level',
64 72 'config' : 'BaseIPythonApplication.extra_config_file',
65 73 }
66 74
67 75 base_flags = dict(
68 76 debug = ({'Application' : {'log_level' : logging.DEBUG}},
69 77 "set log level to logging.DEBUG (maximize logging output)"),
70 78 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
71 79 "set log level to logging.CRITICAL (minimize logging output)"),
72 80 init = ({'BaseIPythonApplication' : {
73 81 'copy_config_files' : True,
74 82 'auto_create' : True}
75 83 }, """Initialize profile with default config files. This is equivalent
76 84 to running `ipython profile create <profile>` prior to startup.
77 85 """)
78 86 )
79 87
80 88 class ProfileAwareConfigLoader(PyFileConfigLoader):
81 89 """A Python file config loader that is aware of IPython profiles."""
82 90 def load_subconfig(self, fname, path=None, profile=None):
83 91 if profile is not None:
84 92 try:
85 93 profile_dir = ProfileDir.find_profile_dir_by_name(
86 94 get_ipython_dir(),
87 95 profile,
88 96 )
89 97 except ProfileDirError:
90 98 return
91 99 path = profile_dir.location
92 100 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
93 101
94 102 class BaseIPythonApplication(Application):
95 103
96 104 name = Unicode(u'ipython')
97 105 description = Unicode(u'IPython: an enhanced interactive Python shell.')
98 106 version = Unicode(release.version)
99 107
100 108 aliases = Dict(base_aliases)
101 109 flags = Dict(base_flags)
102 110 classes = List([ProfileDir])
103 111
104 112 # enable `load_subconfig('cfg.py', profile='name')`
105 113 python_config_loader_class = ProfileAwareConfigLoader
106 114
107 115 # Track whether the config_file has changed,
108 116 # because some logic happens only if we aren't using the default.
109 117 config_file_specified = Set()
110 118
111 119 config_file_name = Unicode()
112 120 @default('config_file_name')
113 121 def _config_file_name_default(self):
114 122 return self.name.replace('-','_') + u'_config.py'
115 123 @observe('config_file_name')
116 124 def _config_file_name_changed(self, change):
117 125 if change['new'] != change['old']:
118 126 self.config_file_specified.add(change['new'])
119 127
120 128 # The directory that contains IPython's builtin profiles.
121 129 builtin_profile_dir = Unicode(
122 130 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
123 131 )
124 132
125 133 config_file_paths = List(Unicode())
126 134 @default('config_file_paths')
127 135 def _config_file_paths_default(self):
128 136 return [os.getcwd()]
129 137
130 138 extra_config_file = Unicode(
131 139 help="""Path to an extra config file to load.
132 140
133 141 If specified, load this config file in addition to any other IPython config.
134 142 """).tag(config=True)
135 143 @observe('extra_config_file')
136 144 def _extra_config_file_changed(self, change):
137 145 old = change['old']
138 146 new = change['new']
139 147 try:
140 148 self.config_files.remove(old)
141 149 except ValueError:
142 150 pass
143 151 self.config_file_specified.add(new)
144 152 self.config_files.append(new)
145 153
146 154 profile = Unicode(u'default',
147 155 help="""The IPython profile to use."""
148 156 ).tag(config=True)
149 157
150 158 @observe('profile')
151 159 def _profile_changed(self, change):
152 160 self.builtin_profile_dir = os.path.join(
153 161 get_ipython_package_dir(), u'config', u'profile', change['new']
154 162 )
155 163
156 164 ipython_dir = Unicode(
157 165 help="""
158 166 The name of the IPython directory. This directory is used for logging
159 167 configuration (through profiles), history storage, etc. The default
160 168 is usually $HOME/.ipython. This option can also be specified through
161 169 the environment variable IPYTHONDIR.
162 170 """
163 171 ).tag(config=True)
164 172 @default('ipython_dir')
165 173 def _ipython_dir_default(self):
166 174 d = get_ipython_dir()
167 175 self._ipython_dir_changed({
168 176 'name': 'ipython_dir',
169 177 'old': d,
170 178 'new': d,
171 179 })
172 180 return d
173 181
174 182 _in_init_profile_dir = False
175 183 profile_dir = Instance(ProfileDir, allow_none=True)
176 184 @default('profile_dir')
177 185 def _profile_dir_default(self):
178 186 # avoid recursion
179 187 if self._in_init_profile_dir:
180 188 return
181 189 # profile_dir requested early, force initialization
182 190 self.init_profile_dir()
183 191 return self.profile_dir
184 192
185 193 overwrite = Bool(False,
186 194 help="""Whether to overwrite existing config files when copying"""
187 195 ).tag(config=True)
188 196 auto_create = Bool(False,
189 197 help="""Whether to create profile dir if it doesn't exist"""
190 198 ).tag(config=True)
191 199
192 200 config_files = List(Unicode())
193 201 @default('config_files')
194 202 def _config_files_default(self):
195 203 return [self.config_file_name]
196 204
197 205 copy_config_files = Bool(False,
198 206 help="""Whether to install the default config files into the profile dir.
199 207 If a new profile is being created, and IPython contains config files for that
200 208 profile, then they will be staged into the new directory. Otherwise,
201 209 default config files will be automatically generated.
202 210 """).tag(config=True)
203 211
204 212 verbose_crash = Bool(False,
205 213 help="""Create a massive crash report when IPython encounters what may be an
206 214 internal error. The default is to append a short message to the
207 215 usual traceback""").tag(config=True)
208 216
209 217 # The class to use as the crash handler.
210 218 crash_handler_class = Type(crashhandler.CrashHandler)
211 219
212 220 @catch_config_error
213 221 def __init__(self, **kwargs):
214 222 super(BaseIPythonApplication, self).__init__(**kwargs)
215 223 # ensure current working directory exists
216 224 try:
217 225 os.getcwd()
218 226 except:
219 227 # exit if cwd doesn't exist
220 228 self.log.error("Current working directory doesn't exist.")
221 229 self.exit(1)
222 230
223 231 #-------------------------------------------------------------------------
224 232 # Various stages of Application creation
225 233 #-------------------------------------------------------------------------
226 234
227 235 deprecated_subcommands = {}
228 236
229 237 def initialize_subcommand(self, subc, argv=None):
230 238 if subc in self.deprecated_subcommands:
231 239 self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed "
232 240 "in future versions.".format(sub=subc))
233 241 self.log.warning("You likely want to use `jupyter {sub}` in the "
234 242 "future".format(sub=subc))
235 243 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
236 244
237 245 def init_crash_handler(self):
238 246 """Create a crash handler, typically setting sys.excepthook to it."""
239 247 self.crash_handler = self.crash_handler_class(self)
240 248 sys.excepthook = self.excepthook
241 249 def unset_crashhandler():
242 250 sys.excepthook = sys.__excepthook__
243 251 atexit.register(unset_crashhandler)
244 252
245 253 def excepthook(self, etype, evalue, tb):
246 254 """this is sys.excepthook after init_crashhandler
247 255
248 256 set self.verbose_crash=True to use our full crashhandler, instead of
249 257 a regular traceback with a short message (crash_handler_lite)
250 258 """
251 259
252 260 if self.verbose_crash:
253 261 return self.crash_handler(etype, evalue, tb)
254 262 else:
255 263 return crashhandler.crash_handler_lite(etype, evalue, tb)
256 264
257 265 @observe('ipython_dir')
258 266 def _ipython_dir_changed(self, change):
259 267 old = change['old']
260 268 new = change['new']
261 269 if old is not Undefined:
262 270 str_old = os.path.abspath(old)
263 271 if str_old in sys.path:
264 272 sys.path.remove(str_old)
265 273 str_path = os.path.abspath(new)
266 274 sys.path.append(str_path)
267 275 ensure_dir_exists(new)
268 276 readme = os.path.join(new, 'README')
269 277 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
270 278 if not os.path.exists(readme) and os.path.exists(readme_src):
271 279 shutil.copy(readme_src, readme)
272 280 for d in ('extensions', 'nbextensions'):
273 281 path = os.path.join(new, d)
274 282 try:
275 283 ensure_dir_exists(path)
276 284 except OSError as e:
277 285 # this will not be EEXIST
278 286 self.log.error("couldn't create path %s: %s", path, e)
279 287 self.log.debug("IPYTHONDIR set to: %s" % new)
280 288
281 289 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
282 290 """Load the config file.
283 291
284 292 By default, errors in loading config are handled, and a warning
285 293 printed on screen. For testing, the suppress_errors option is set
286 294 to False, so errors will make tests fail.
287 295
288 296 `supress_errors` default value is to be `None` in which case the
289 297 behavior default to the one of `traitlets.Application`.
290 298
291 299 The default value can be set :
292 300 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
293 301 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
294 302 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
295 303
296 304 Any other value are invalid, and will make IPython exit with a non-zero return code.
297 305 """
298 306
299 307
300 308 self.log.debug("Searching path %s for config files", self.config_file_paths)
301 309 base_config = 'ipython_config.py'
302 310 self.log.debug("Attempting to load config file: %s" %
303 311 base_config)
304 312 try:
305 313 if suppress_errors is not None:
306 314 old_value = Application.raise_config_file_errors
307 315 Application.raise_config_file_errors = not suppress_errors;
308 316 Application.load_config_file(
309 317 self,
310 318 base_config,
311 319 path=self.config_file_paths
312 320 )
313 321 except ConfigFileNotFound:
314 322 # ignore errors loading parent
315 323 self.log.debug("Config file %s not found", base_config)
316 324 pass
317 325 if suppress_errors is not None:
318 326 Application.raise_config_file_errors = old_value
319 327
320 328 for config_file_name in self.config_files:
321 329 if not config_file_name or config_file_name == base_config:
322 330 continue
323 331 self.log.debug("Attempting to load config file: %s" %
324 332 self.config_file_name)
325 333 try:
326 334 Application.load_config_file(
327 335 self,
328 336 config_file_name,
329 337 path=self.config_file_paths
330 338 )
331 339 except ConfigFileNotFound:
332 340 # Only warn if the default config file was NOT being used.
333 341 if config_file_name in self.config_file_specified:
334 342 msg = self.log.warning
335 343 else:
336 344 msg = self.log.debug
337 345 msg("Config file not found, skipping: %s", config_file_name)
338 346 except Exception:
339 347 # For testing purposes.
340 348 if not suppress_errors:
341 349 raise
342 350 self.log.warning("Error loading config file: %s" %
343 351 self.config_file_name, exc_info=True)
344 352
345 353 def init_profile_dir(self):
346 354 """initialize the profile dir"""
347 355 self._in_init_profile_dir = True
348 356 if self.profile_dir is not None:
349 357 # already ran
350 358 return
351 359 if 'ProfileDir.location' not in self.config:
352 360 # location not specified, find by profile name
353 361 try:
354 362 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
355 363 except ProfileDirError:
356 364 # not found, maybe create it (always create default profile)
357 365 if self.auto_create or self.profile == 'default':
358 366 try:
359 367 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
360 368 except ProfileDirError:
361 369 self.log.fatal("Could not create profile: %r"%self.profile)
362 370 self.exit(1)
363 371 else:
364 372 self.log.info("Created profile dir: %r"%p.location)
365 373 else:
366 374 self.log.fatal("Profile %r not found."%self.profile)
367 375 self.exit(1)
368 376 else:
369 377 self.log.debug("Using existing profile dir: %r"%p.location)
370 378 else:
371 379 location = self.config.ProfileDir.location
372 380 # location is fully specified
373 381 try:
374 382 p = ProfileDir.find_profile_dir(location, self.config)
375 383 except ProfileDirError:
376 384 # not found, maybe create it
377 385 if self.auto_create:
378 386 try:
379 387 p = ProfileDir.create_profile_dir(location, self.config)
380 388 except ProfileDirError:
381 389 self.log.fatal("Could not create profile directory: %r"%location)
382 390 self.exit(1)
383 391 else:
384 392 self.log.debug("Creating new profile dir: %r"%location)
385 393 else:
386 394 self.log.fatal("Profile directory %r not found."%location)
387 395 self.exit(1)
388 396 else:
389 397 self.log.info("Using existing profile dir: %r"%location)
390 398 # if profile_dir is specified explicitly, set profile name
391 399 dir_name = os.path.basename(p.location)
392 400 if dir_name.startswith('profile_'):
393 401 self.profile = dir_name[8:]
394 402
395 403 self.profile_dir = p
396 404 self.config_file_paths.append(p.location)
397 405 self._in_init_profile_dir = False
398 406
399 407 def init_config_files(self):
400 408 """[optionally] copy default config files into profile dir."""
409 self.config_file_paths.extend(ENV_CONFIG_DIRS)
401 410 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
402 411 # copy config files
403 412 path = self.builtin_profile_dir
404 413 if self.copy_config_files:
405 414 src = self.profile
406 415
407 416 cfg = self.config_file_name
408 417 if path and os.path.exists(os.path.join(path, cfg)):
409 418 self.log.warning("Staging %r from %s into %r [overwrite=%s]"%(
410 419 cfg, src, self.profile_dir.location, self.overwrite)
411 420 )
412 421 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
413 422 else:
414 423 self.stage_default_config_file()
415 424 else:
416 425 # Still stage *bundled* config files, but not generated ones
417 426 # This is necessary for `ipython profile=sympy` to load the profile
418 427 # on the first go
419 428 files = glob.glob(os.path.join(path, '*.py'))
420 429 for fullpath in files:
421 430 cfg = os.path.basename(fullpath)
422 431 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
423 432 # file was copied
424 433 self.log.warning("Staging bundled %s from %s into %r"%(
425 434 cfg, self.profile, self.profile_dir.location)
426 435 )
427 436
428 437
429 438 def stage_default_config_file(self):
430 439 """auto generate default config file, and stage it into the profile."""
431 440 s = self.generate_config_file()
432 441 fname = os.path.join(self.profile_dir.location, self.config_file_name)
433 442 if self.overwrite or not os.path.exists(fname):
434 443 self.log.warning("Generating default config file: %r"%(fname))
435 444 with open(fname, 'w') as f:
436 445 f.write(s)
437 446
438 447 @catch_config_error
439 448 def initialize(self, argv=None):
440 449 # don't hook up crash handler before parsing command-line
441 450 self.parse_command_line(argv)
442 451 self.init_crash_handler()
443 452 if self.subapp is not None:
444 453 # stop here if subapp is taking over
445 454 return
446 455 # save a copy of CLI config to re-load after config files
447 456 # so that it has highest priority
448 457 cl_config = deepcopy(self.config)
449 458 self.init_profile_dir()
450 459 self.init_config_files()
451 460 self.load_config_file()
452 461 # enforce cl-opts override configfile opts:
453 462 self.update_config(cl_config)
General Comments 0
You need to be logged in to leave comments. Login now