##// END OF EJS Templates
remove nag screen delay...
Paul Ivanov -
Show More
@@ -1,411 +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}`... continue "
206 "in 5 sec. Press Ctrl-C to quit now.".format(sub=subc))
207 try:
208 time.sleep(5)
209 except KeyboardInterrupt:
210 sys.exit(1)
205 self.log.warning("You likely want to use `jupyter {sub}`... in the"
206 "future".format(sub=subc))
211 207 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
212 208
213 209 def init_crash_handler(self):
214 210 """Create a crash handler, typically setting sys.excepthook to it."""
215 211 self.crash_handler = self.crash_handler_class(self)
216 212 sys.excepthook = self.excepthook
217 213 def unset_crashhandler():
218 214 sys.excepthook = sys.__excepthook__
219 215 atexit.register(unset_crashhandler)
220 216
221 217 def excepthook(self, etype, evalue, tb):
222 218 """this is sys.excepthook after init_crashhandler
223 219
224 220 set self.verbose_crash=True to use our full crashhandler, instead of
225 221 a regular traceback with a short message (crash_handler_lite)
226 222 """
227 223
228 224 if self.verbose_crash:
229 225 return self.crash_handler(etype, evalue, tb)
230 226 else:
231 227 return crashhandler.crash_handler_lite(etype, evalue, tb)
232 228
233 229 def _ipython_dir_changed(self, name, old, new):
234 230 if old is not Undefined:
235 231 str_old = py3compat.cast_bytes_py2(os.path.abspath(old),
236 232 sys.getfilesystemencoding()
237 233 )
238 234 if str_old in sys.path:
239 235 sys.path.remove(str_old)
240 236 str_path = py3compat.cast_bytes_py2(os.path.abspath(new),
241 237 sys.getfilesystemencoding()
242 238 )
243 239 sys.path.append(str_path)
244 240 ensure_dir_exists(new)
245 241 readme = os.path.join(new, 'README')
246 242 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
247 243 if not os.path.exists(readme) and os.path.exists(readme_src):
248 244 shutil.copy(readme_src, readme)
249 245 for d in ('extensions', 'nbextensions'):
250 246 path = os.path.join(new, d)
251 247 try:
252 248 ensure_dir_exists(path)
253 249 except OSError as e:
254 250 # this will not be EEXIST
255 251 self.log.error("couldn't create path %s: %s", path, e)
256 252 self.log.debug("IPYTHONDIR set to: %s" % new)
257 253
258 254 def load_config_file(self, suppress_errors=True):
259 255 """Load the config file.
260 256
261 257 By default, errors in loading config are handled, and a warning
262 258 printed on screen. For testing, the suppress_errors option is set
263 259 to False, so errors will make tests fail.
264 260 """
265 261 self.log.debug("Searching path %s for config files", self.config_file_paths)
266 262 base_config = 'ipython_config.py'
267 263 self.log.debug("Attempting to load config file: %s" %
268 264 base_config)
269 265 try:
270 266 Application.load_config_file(
271 267 self,
272 268 base_config,
273 269 path=self.config_file_paths
274 270 )
275 271 except ConfigFileNotFound:
276 272 # ignore errors loading parent
277 273 self.log.debug("Config file %s not found", base_config)
278 274 pass
279 275
280 276 for config_file_name in self.config_files:
281 277 if not config_file_name or config_file_name == base_config:
282 278 continue
283 279 self.log.debug("Attempting to load config file: %s" %
284 280 self.config_file_name)
285 281 try:
286 282 Application.load_config_file(
287 283 self,
288 284 config_file_name,
289 285 path=self.config_file_paths
290 286 )
291 287 except ConfigFileNotFound:
292 288 # Only warn if the default config file was NOT being used.
293 289 if config_file_name in self.config_file_specified:
294 290 msg = self.log.warning
295 291 else:
296 292 msg = self.log.debug
297 293 msg("Config file not found, skipping: %s", config_file_name)
298 294 except Exception:
299 295 # For testing purposes.
300 296 if not suppress_errors:
301 297 raise
302 298 self.log.warning("Error loading config file: %s" %
303 299 self.config_file_name, exc_info=True)
304 300
305 301 def init_profile_dir(self):
306 302 """initialize the profile dir"""
307 303 self._in_init_profile_dir = True
308 304 if self.profile_dir is not None:
309 305 # already ran
310 306 return
311 307 if 'ProfileDir.location' not in self.config:
312 308 # location not specified, find by profile name
313 309 try:
314 310 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
315 311 except ProfileDirError:
316 312 # not found, maybe create it (always create default profile)
317 313 if self.auto_create or self.profile == 'default':
318 314 try:
319 315 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
320 316 except ProfileDirError:
321 317 self.log.fatal("Could not create profile: %r"%self.profile)
322 318 self.exit(1)
323 319 else:
324 320 self.log.info("Created profile dir: %r"%p.location)
325 321 else:
326 322 self.log.fatal("Profile %r not found."%self.profile)
327 323 self.exit(1)
328 324 else:
329 325 self.log.debug("Using existing profile dir: %r"%p.location)
330 326 else:
331 327 location = self.config.ProfileDir.location
332 328 # location is fully specified
333 329 try:
334 330 p = ProfileDir.find_profile_dir(location, self.config)
335 331 except ProfileDirError:
336 332 # not found, maybe create it
337 333 if self.auto_create:
338 334 try:
339 335 p = ProfileDir.create_profile_dir(location, self.config)
340 336 except ProfileDirError:
341 337 self.log.fatal("Could not create profile directory: %r"%location)
342 338 self.exit(1)
343 339 else:
344 340 self.log.debug("Creating new profile dir: %r"%location)
345 341 else:
346 342 self.log.fatal("Profile directory %r not found."%location)
347 343 self.exit(1)
348 344 else:
349 345 self.log.info("Using existing profile dir: %r"%location)
350 346 # if profile_dir is specified explicitly, set profile name
351 347 dir_name = os.path.basename(p.location)
352 348 if dir_name.startswith('profile_'):
353 349 self.profile = dir_name[8:]
354 350
355 351 self.profile_dir = p
356 352 self.config_file_paths.append(p.location)
357 353 self._in_init_profile_dir = False
358 354
359 355 def init_config_files(self):
360 356 """[optionally] copy default config files into profile dir."""
361 357 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
362 358 # copy config files
363 359 path = self.builtin_profile_dir
364 360 if self.copy_config_files:
365 361 src = self.profile
366 362
367 363 cfg = self.config_file_name
368 364 if path and os.path.exists(os.path.join(path, cfg)):
369 365 self.log.warning("Staging %r from %s into %r [overwrite=%s]"%(
370 366 cfg, src, self.profile_dir.location, self.overwrite)
371 367 )
372 368 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
373 369 else:
374 370 self.stage_default_config_file()
375 371 else:
376 372 # Still stage *bundled* config files, but not generated ones
377 373 # This is necessary for `ipython profile=sympy` to load the profile
378 374 # on the first go
379 375 files = glob.glob(os.path.join(path, '*.py'))
380 376 for fullpath in files:
381 377 cfg = os.path.basename(fullpath)
382 378 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
383 379 # file was copied
384 380 self.log.warning("Staging bundled %s from %s into %r"%(
385 381 cfg, self.profile, self.profile_dir.location)
386 382 )
387 383
388 384
389 385 def stage_default_config_file(self):
390 386 """auto generate default config file, and stage it into the profile."""
391 387 s = self.generate_config_file()
392 388 fname = os.path.join(self.profile_dir.location, self.config_file_name)
393 389 if self.overwrite or not os.path.exists(fname):
394 390 self.log.warning("Generating default config file: %r"%(fname))
395 391 with open(fname, 'w') as f:
396 392 f.write(s)
397 393
398 394 @catch_config_error
399 395 def initialize(self, argv=None):
400 396 # don't hook up crash handler before parsing command-line
401 397 self.parse_command_line(argv)
402 398 self.init_crash_handler()
403 399 if self.subapp is not None:
404 400 # stop here if subapp is taking over
405 401 return
406 402 cl_config = self.config
407 403 self.init_profile_dir()
408 404 self.init_config_files()
409 405 self.load_config_file()
410 406 # enforce cl-opts override configfile opts:
411 407 self.update_config(cl_config)
General Comments 0
You need to be logged in to leave comments. Login now