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