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