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