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