##// END OF EJS Templates
Fix KeyError if old ipython_config.py is present.
Thomas Kluyver -
Show More
@@ -1,450 +1,450
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19 from hashlib import md5
20 20
21 21 import IPython
22 22 from IPython.utils import warn
23 23 from IPython.utils.process import system
24 24 from IPython.utils.importstring import import_item
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Code
28 28 #-----------------------------------------------------------------------------
29 29
30 30 fs_encoding = sys.getfilesystemencoding()
31 31
32 32 def _cast_unicode(s, enc=None):
33 33 """Turn 8-bit strings into unicode."""
34 34 if isinstance(s, bytes):
35 35 enc = enc or sys.getdefaultencoding()
36 36 return s.decode(enc)
37 37 return s
38 38
39 39
40 40 def _get_long_path_name(path):
41 41 """Dummy no-op."""
42 42 return path
43 43
44 44 if sys.platform == 'win32':
45 45 def _get_long_path_name(path):
46 46 """Get a long path name (expand ~) on Windows using ctypes.
47 47
48 48 Examples
49 49 --------
50 50
51 51 >>> get_long_path_name('c:\\docume~1')
52 52 u'c:\\\\Documents and Settings'
53 53
54 54 """
55 55 try:
56 56 import ctypes
57 57 except ImportError:
58 58 raise ImportError('you need to have ctypes installed for this to work')
59 59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
60 60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
61 61 ctypes.c_uint ]
62 62
63 63 buf = ctypes.create_unicode_buffer(260)
64 64 rv = _GetLongPathName(path, buf, 260)
65 65 if rv == 0 or rv > 260:
66 66 return path
67 67 else:
68 68 return buf.value
69 69
70 70
71 71 def get_long_path_name(path):
72 72 """Expand a path into its long form.
73 73
74 74 On Windows this expands any ~ in the paths. On other platforms, it is
75 75 a null operation.
76 76 """
77 77 return _get_long_path_name(path)
78 78
79 79
80 80 def get_py_filename(name):
81 81 """Return a valid python filename in the current directory.
82 82
83 83 If the given name is not a file, it adds '.py' and searches again.
84 84 Raises IOError with an informative message if the file isn't found."""
85 85
86 86 name = os.path.expanduser(name)
87 87 if not os.path.isfile(name) and not name.endswith('.py'):
88 88 name += '.py'
89 89 if os.path.isfile(name):
90 90 return name
91 91 else:
92 92 raise IOError,'File `%s` not found.' % name
93 93
94 94
95 95 def filefind(filename, path_dirs=None):
96 96 """Find a file by looking through a sequence of paths.
97 97
98 98 This iterates through a sequence of paths looking for a file and returns
99 99 the full, absolute path of the first occurence of the file. If no set of
100 100 path dirs is given, the filename is tested as is, after running through
101 101 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
102 102
103 103 filefind('myfile.txt')
104 104
105 105 will find the file in the current working dir, but::
106 106
107 107 filefind('~/myfile.txt')
108 108
109 109 Will find the file in the users home directory. This function does not
110 110 automatically try any paths, such as the cwd or the user's home directory.
111 111
112 112 Parameters
113 113 ----------
114 114 filename : str
115 115 The filename to look for.
116 116 path_dirs : str, None or sequence of str
117 117 The sequence of paths to look for the file in. If None, the filename
118 118 need to be absolute or be in the cwd. If a string, the string is
119 119 put into a sequence and the searched. If a sequence, walk through
120 120 each element and join with ``filename``, calling :func:`expandvars`
121 121 and :func:`expanduser` before testing for existence.
122 122
123 123 Returns
124 124 -------
125 125 Raises :exc:`IOError` or returns absolute path to file.
126 126 """
127 127
128 128 # If paths are quoted, abspath gets confused, strip them...
129 129 filename = filename.strip('"').strip("'")
130 130 # If the input is an absolute path, just check it exists
131 131 if os.path.isabs(filename) and os.path.isfile(filename):
132 132 return filename
133 133
134 134 if path_dirs is None:
135 135 path_dirs = ("",)
136 136 elif isinstance(path_dirs, basestring):
137 137 path_dirs = (path_dirs,)
138 138
139 139 for path in path_dirs:
140 140 if path == '.': path = os.getcwd()
141 141 testname = expand_path(os.path.join(path, filename))
142 142 if os.path.isfile(testname):
143 143 return os.path.abspath(testname)
144 144
145 145 raise IOError("File %r does not exist in any of the search paths: %r" %
146 146 (filename, path_dirs) )
147 147
148 148
149 149 class HomeDirError(Exception):
150 150 pass
151 151
152 152
153 153 def get_home_dir():
154 154 """Return the closest possible equivalent to a 'home' directory.
155 155
156 156 * On POSIX, we try $HOME.
157 157 * On Windows we try:
158 158 - %HOMESHARE%
159 159 - %HOMEDRIVE\%HOMEPATH%
160 160 - %USERPROFILE%
161 161 - Registry hack for My Documents
162 162 - %HOME%: rare, but some people with unix-like setups may have defined it
163 163 * On Dos C:\
164 164
165 165 Currently only Posix and NT are implemented, a HomeDirError exception is
166 166 raised for all other OSes.
167 167 """
168 168
169 169 isdir = os.path.isdir
170 170 env = os.environ
171 171
172 172 # first, check py2exe distribution root directory for _ipython.
173 173 # This overrides all. Normally does not exist.
174 174
175 175 if hasattr(sys, "frozen"): #Is frozen by py2exe
176 176 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
177 177 root, rest = IPython.__file__.lower().split('library.zip')
178 178 else:
179 179 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
180 180 root=os.path.abspath(root).rstrip('\\')
181 181 if isdir(os.path.join(root, '_ipython')):
182 182 os.environ["IPYKITROOT"] = root
183 183 return _cast_unicode(root, fs_encoding)
184 184
185 185 if os.name == 'posix':
186 186 # Linux, Unix, AIX, OS X
187 187 try:
188 188 homedir = env['HOME']
189 189 except KeyError:
190 190 # Last-ditch attempt at finding a suitable $HOME, on systems where
191 191 # it may not be defined in the environment but the system shell
192 192 # still knows it - reported once as:
193 193 # https://github.com/ipython/ipython/issues/154
194 194 from subprocess import Popen, PIPE
195 195 homedir = Popen('echo $HOME', shell=True,
196 196 stdout=PIPE).communicate()[0].strip()
197 197 if homedir:
198 198 return _cast_unicode(homedir, fs_encoding)
199 199 else:
200 200 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
201 201 else:
202 202 return _cast_unicode(homedir, fs_encoding)
203 203 elif os.name == 'nt':
204 204 # Now for win9x, XP, Vista, 7?
205 205 # For some strange reason all of these return 'nt' for os.name.
206 206 # First look for a network home directory. This will return the UNC
207 207 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
208 208 # is needed when running IPython on cluster where all paths have to
209 209 # be UNC.
210 210 try:
211 211 homedir = env['HOMESHARE']
212 212 except KeyError:
213 213 pass
214 214 else:
215 215 if isdir(homedir):
216 216 return _cast_unicode(homedir, fs_encoding)
217 217
218 218 # Now look for a local home directory
219 219 try:
220 220 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
221 221 except KeyError:
222 222 pass
223 223 else:
224 224 if isdir(homedir):
225 225 return _cast_unicode(homedir, fs_encoding)
226 226
227 227 # Now the users profile directory
228 228 try:
229 229 homedir = os.path.join(env['USERPROFILE'])
230 230 except KeyError:
231 231 pass
232 232 else:
233 233 if isdir(homedir):
234 234 return _cast_unicode(homedir, fs_encoding)
235 235
236 236 # Use the registry to get the 'My Documents' folder.
237 237 try:
238 238 import _winreg as wreg
239 239 key = wreg.OpenKey(
240 240 wreg.HKEY_CURRENT_USER,
241 241 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
242 242 )
243 243 homedir = wreg.QueryValueEx(key,'Personal')[0]
244 244 key.Close()
245 245 except:
246 246 pass
247 247 else:
248 248 if isdir(homedir):
249 249 return _cast_unicode(homedir, fs_encoding)
250 250
251 251 # A user with a lot of unix tools in win32 may have defined $HOME.
252 252 # Try this as a last ditch option.
253 253 try:
254 254 homedir = env['HOME']
255 255 except KeyError:
256 256 pass
257 257 else:
258 258 if isdir(homedir):
259 259 return _cast_unicode(homedir, fs_encoding)
260 260
261 261 # If all else fails, raise HomeDirError
262 262 raise HomeDirError('No valid home directory could be found')
263 263 elif os.name == 'dos':
264 264 # Desperate, may do absurd things in classic MacOS. May work under DOS.
265 265 return u'C:\\'
266 266 else:
267 267 raise HomeDirError('No valid home directory could be found for your OS')
268 268
269 269 def get_xdg_dir():
270 270 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
271 271
272 272 This is only for posix (Linux,Unix,OS X, etc) systems.
273 273 """
274 274
275 275 isdir = os.path.isdir
276 276 env = os.environ
277 277
278 278 if os.name == 'posix':
279 279 # Linux, Unix, AIX, OS X
280 280 # use ~/.config if not set OR empty
281 281 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
282 282 if xdg and isdir(xdg):
283 283 return _cast_unicode(xdg, fs_encoding)
284 284
285 285 return None
286 286
287 287
288 288 def get_ipython_dir():
289 289 """Get the IPython directory for this platform and user.
290 290
291 291 This uses the logic in `get_home_dir` to find the home directory
292 292 and the adds .ipython to the end of the path.
293 293 """
294 294
295 295 env = os.environ
296 296 pjoin = os.path.join
297 297 exists = os.path.exists
298 298
299 299 ipdir_def = '.ipython'
300 300 xdg_def = 'ipython'
301 301
302 302 home_dir = get_home_dir()
303 303 xdg_dir = get_xdg_dir()
304 304 # import pdb; pdb.set_trace() # dbg
305 305 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
306 306 if ipdir is None:
307 307 # not set explicitly, use XDG_CONFIG_HOME or HOME
308 308 home_ipdir = pjoin(home_dir, ipdir_def)
309 309 if xdg_dir:
310 310 # use XDG, as long as the user isn't already
311 311 # using $HOME/.ipython and *not* XDG/ipython
312 312
313 313 xdg_ipdir = pjoin(xdg_dir, xdg_def)
314 314
315 315 if exists(xdg_ipdir) or not exists(home_ipdir):
316 316 ipdir = xdg_ipdir
317 317
318 318 if ipdir is None:
319 319 # not using XDG
320 320 ipdir = home_ipdir
321 321
322 322 ipdir = os.path.normpath(os.path.expanduser(ipdir))
323 323
324 324 return _cast_unicode(ipdir, fs_encoding)
325 325
326 326
327 327 def get_ipython_package_dir():
328 328 """Get the base directory where IPython itself is installed."""
329 329 ipdir = os.path.dirname(IPython.__file__)
330 330 return _cast_unicode(ipdir, fs_encoding)
331 331
332 332
333 333 def get_ipython_module_path(module_str):
334 334 """Find the path to an IPython module in this version of IPython.
335 335
336 336 This will always find the version of the module that is in this importable
337 337 IPython package. This will always return the path to the ``.py``
338 338 version of the module.
339 339 """
340 340 if module_str == 'IPython':
341 341 return os.path.join(get_ipython_package_dir(), '__init__.py')
342 342 mod = import_item(module_str)
343 343 the_path = mod.__file__.replace('.pyc', '.py')
344 344 the_path = the_path.replace('.pyo', '.py')
345 345 return _cast_unicode(the_path, fs_encoding)
346 346
347 347
348 348 def expand_path(s):
349 349 """Expand $VARS and ~names in a string, like a shell
350 350
351 351 :Examples:
352 352
353 353 In [2]: os.environ['FOO']='test'
354 354
355 355 In [3]: expand_path('variable FOO is $FOO')
356 356 Out[3]: 'variable FOO is test'
357 357 """
358 358 # This is a pretty subtle hack. When expand user is given a UNC path
359 359 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
360 360 # the $ to get (\\server\share\%username%). I think it considered $
361 361 # alone an empty var. But, we need the $ to remains there (it indicates
362 362 # a hidden share).
363 363 if os.name=='nt':
364 364 s = s.replace('$\\', 'IPYTHON_TEMP')
365 365 s = os.path.expandvars(os.path.expanduser(s))
366 366 if os.name=='nt':
367 367 s = s.replace('IPYTHON_TEMP', '$\\')
368 368 return s
369 369
370 370
371 371 def target_outdated(target,deps):
372 372 """Determine whether a target is out of date.
373 373
374 374 target_outdated(target,deps) -> 1/0
375 375
376 376 deps: list of filenames which MUST exist.
377 377 target: single filename which may or may not exist.
378 378
379 379 If target doesn't exist or is older than any file listed in deps, return
380 380 true, otherwise return false.
381 381 """
382 382 try:
383 383 target_time = os.path.getmtime(target)
384 384 except os.error:
385 385 return 1
386 386 for dep in deps:
387 387 dep_time = os.path.getmtime(dep)
388 388 if dep_time > target_time:
389 389 #print "For target",target,"Dep failed:",dep # dbg
390 390 #print "times (dep,tar):",dep_time,target_time # dbg
391 391 return 1
392 392 return 0
393 393
394 394
395 395 def target_update(target,deps,cmd):
396 396 """Update a target with a given command given a list of dependencies.
397 397
398 398 target_update(target,deps,cmd) -> runs cmd if target is outdated.
399 399
400 400 This is just a wrapper around target_outdated() which calls the given
401 401 command if target is outdated."""
402 402
403 403 if target_outdated(target,deps):
404 404 system(cmd)
405 405
406 406 def filehash(path):
407 407 """Make an MD5 hash of a file, ignoring any differences in line
408 408 ending characters."""
409 409 with open(path, "rU") as f:
410 410 return md5(f.read()).hexdigest()
411 411
412 412 # If the config is unmodified from the default, we'll just delete it.
413 413 # These are consistent for 0.10.x, thankfully. We're not going to worry about
414 414 # older versions.
415 415 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
416 416 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
417 417
418 418 def check_for_old_config(ipython_dir=None):
419 419 """Check for old config files, and present a warning if they exist.
420 420
421 421 A link to the docs of the new config is included in the message.
422 422
423 423 This should mitigate confusion with the transition to the new
424 424 config system in 0.11.
425 425 """
426 426 if ipython_dir is None:
427 427 ipython_dir = get_ipython_dir()
428 428
429 429 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
430 430 warned = False
431 431 for cfg in old_configs:
432 432 f = os.path.join(ipython_dir, cfg)
433 433 if os.path.exists(f):
434 if filehash(f) == old_config_md5[cfg]:
434 if filehash(f) == old_config_md5.get(cfg, ''):
435 435 os.unlink(f)
436 436 warn.info("Removed unmodified old IPython config file %r"%f)
437 437 else:
438 438 warned = True
439 439 warn.warn("Found old IPython config file %r (modified by user)"%f)
440 440
441 441 if warned:
442 442 warn.warn("""
443 443 The IPython configuration system has changed as of 0.11, and these files
444 444 will be ignored. See http://ipython.github.com/ipython-doc/dev/config for
445 445 details on the new config system. To start configuring IPython, do
446 446 `ipython profile create`, and edit `ipython_config.py` in
447 447 <ipython_dir>/profile_default, adding
448 448 `c.InteractiveShellApp.ignore_old_config=True`""")
449 449
450 450
General Comments 0
You need to be logged in to leave comments. Login now