##// END OF EJS Templates
remove special-case in get_home_dir for frozen dists...
MinRK -
Show More
@@ -1,526 +1,512 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 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 import tempfile
20 20 import warnings
21 21 from hashlib import md5
22 22 import glob
23 23
24 24 import IPython
25 25 from IPython.testing.skipdoctest import skip_doctest
26 26 from IPython.utils.process import system
27 27 from IPython.utils.importstring import import_item
28 28 from IPython.utils import py3compat
29 29 #-----------------------------------------------------------------------------
30 30 # Code
31 31 #-----------------------------------------------------------------------------
32 32
33 33 fs_encoding = sys.getfilesystemencoding()
34 34
35 35 def _get_long_path_name(path):
36 36 """Dummy no-op."""
37 37 return path
38 38
39 39 def _writable_dir(path):
40 40 """Whether `path` is a directory, to which the user has write access."""
41 41 return os.path.isdir(path) and os.access(path, os.W_OK)
42 42
43 43 if sys.platform == 'win32':
44 44 @skip_doctest
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 unquote_filename(name, win32=(sys.platform=='win32')):
81 81 """ On Windows, remove leading and trailing quotes from filenames.
82 82 """
83 83 if win32:
84 84 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
85 85 name = name[1:-1]
86 86 return name
87 87
88 88
89 89 def get_py_filename(name, force_win32=None):
90 90 """Return a valid python filename in the current directory.
91 91
92 92 If the given name is not a file, it adds '.py' and searches again.
93 93 Raises IOError with an informative message if the file isn't found.
94 94
95 95 On Windows, apply Windows semantics to the filename. In particular, remove
96 96 any quoting that has been applied to it. This option can be forced for
97 97 testing purposes.
98 98 """
99 99
100 100 name = os.path.expanduser(name)
101 101 if force_win32 is None:
102 102 win32 = (sys.platform == 'win32')
103 103 else:
104 104 win32 = force_win32
105 105 name = unquote_filename(name, win32=win32)
106 106 if not os.path.isfile(name) and not name.endswith('.py'):
107 107 name += '.py'
108 108 if os.path.isfile(name):
109 109 return name
110 110 else:
111 111 raise IOError('File `%r` not found.' % name)
112 112
113 113
114 114 def filefind(filename, path_dirs=None):
115 115 """Find a file by looking through a sequence of paths.
116 116
117 117 This iterates through a sequence of paths looking for a file and returns
118 118 the full, absolute path of the first occurence of the file. If no set of
119 119 path dirs is given, the filename is tested as is, after running through
120 120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
121 121
122 122 filefind('myfile.txt')
123 123
124 124 will find the file in the current working dir, but::
125 125
126 126 filefind('~/myfile.txt')
127 127
128 128 Will find the file in the users home directory. This function does not
129 129 automatically try any paths, such as the cwd or the user's home directory.
130 130
131 131 Parameters
132 132 ----------
133 133 filename : str
134 134 The filename to look for.
135 135 path_dirs : str, None or sequence of str
136 136 The sequence of paths to look for the file in. If None, the filename
137 137 need to be absolute or be in the cwd. If a string, the string is
138 138 put into a sequence and the searched. If a sequence, walk through
139 139 each element and join with ``filename``, calling :func:`expandvars`
140 140 and :func:`expanduser` before testing for existence.
141 141
142 142 Returns
143 143 -------
144 144 Raises :exc:`IOError` or returns absolute path to file.
145 145 """
146 146
147 147 # If paths are quoted, abspath gets confused, strip them...
148 148 filename = filename.strip('"').strip("'")
149 149 # If the input is an absolute path, just check it exists
150 150 if os.path.isabs(filename) and os.path.isfile(filename):
151 151 return filename
152 152
153 153 if path_dirs is None:
154 154 path_dirs = ("",)
155 155 elif isinstance(path_dirs, basestring):
156 156 path_dirs = (path_dirs,)
157 157
158 158 for path in path_dirs:
159 159 if path == '.': path = os.getcwdu()
160 160 testname = expand_path(os.path.join(path, filename))
161 161 if os.path.isfile(testname):
162 162 return os.path.abspath(testname)
163 163
164 164 raise IOError("File %r does not exist in any of the search paths: %r" %
165 165 (filename, path_dirs) )
166 166
167 167
168 168 class HomeDirError(Exception):
169 169 pass
170 170
171 171
172 172 def get_home_dir(require_writable=False):
173 173 """Return the 'home' directory, as a unicode string.
174 174
175 * First, check for frozen env in case of py2exe
176 * Otherwise, defer to os.path.expanduser('~')
175 Uses os.path.expanduser('~'), and checks for writability.
177 176
178 177 See stdlib docs for how this is determined.
179 178 $HOME is first priority on *ALL* platforms.
180 179
181 180 Parameters
182 181 ----------
183 182
184 183 require_writable : bool [default: False]
185 184 if True:
186 185 guarantees the return value is a writable directory, otherwise
187 186 raises HomeDirError
188 187 if False:
189 188 The path is resolved, but it is not guaranteed to exist or be writable.
190 189 """
191 190
192 # first, check py2exe distribution root directory for _ipython.
193 # This overrides all. Normally does not exist.
194
195 if hasattr(sys, "frozen"): #Is frozen by py2exe
196 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
197 root, rest = IPython.__file__.lower().split('library.zip')
198 else:
199 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
200 root=os.path.abspath(root).rstrip('\\')
201 if _writable_dir(os.path.join(root, '_ipython')):
202 os.environ["IPYKITROOT"] = root
203 return py3compat.cast_unicode(root, fs_encoding)
204
205 191 homedir = os.path.expanduser('~')
206 192 # Next line will make things work even when /home/ is a symlink to
207 193 # /usr/home as it is on FreeBSD, for example
208 194 homedir = os.path.realpath(homedir)
209 195
210 196 if not _writable_dir(homedir) and os.name == 'nt':
211 197 # expanduser failed, use the registry to get the 'My Documents' folder.
212 198 try:
213 199 import _winreg as wreg
214 200 key = wreg.OpenKey(
215 201 wreg.HKEY_CURRENT_USER,
216 202 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
217 203 )
218 204 homedir = wreg.QueryValueEx(key,'Personal')[0]
219 205 key.Close()
220 206 except:
221 207 pass
222 208
223 209 if (not require_writable) or _writable_dir(homedir):
224 210 return py3compat.cast_unicode(homedir, fs_encoding)
225 211 else:
226 212 raise HomeDirError('%s is not a writable dir, '
227 213 'set $HOME environment variable to override' % homedir)
228 214
229 215 def get_xdg_dir():
230 216 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
231 217
232 218 This is only for non-OS X posix (Linux,Unix,etc.) systems.
233 219 """
234 220
235 221 env = os.environ
236 222
237 223 if os.name == 'posix' and sys.platform != 'darwin':
238 224 # Linux, Unix, AIX, etc.
239 225 # use ~/.config if empty OR not set
240 226 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
241 227 if xdg and _writable_dir(xdg):
242 228 return py3compat.cast_unicode(xdg, fs_encoding)
243 229
244 230 return None
245 231
246 232
247 233 def get_xdg_cache_dir():
248 234 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
249 235
250 236 This is only for non-OS X posix (Linux,Unix,etc.) systems.
251 237 """
252 238
253 239 env = os.environ
254 240
255 241 if os.name == 'posix' and sys.platform != 'darwin':
256 242 # Linux, Unix, AIX, etc.
257 243 # use ~/.cache if empty OR not set
258 244 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
259 245 if xdg and _writable_dir(xdg):
260 246 return py3compat.cast_unicode(xdg, fs_encoding)
261 247
262 248 return None
263 249
264 250
265 251 def get_ipython_dir():
266 252 """Get the IPython directory for this platform and user.
267 253
268 254 This uses the logic in `get_home_dir` to find the home directory
269 255 and then adds .ipython to the end of the path.
270 256 """
271 257
272 258 env = os.environ
273 259 pjoin = os.path.join
274 260
275 261
276 262 ipdir_def = '.ipython'
277 263 xdg_def = 'ipython'
278 264
279 265 home_dir = get_home_dir()
280 266 xdg_dir = get_xdg_dir()
281 267
282 268 # import pdb; pdb.set_trace() # dbg
283 269 if 'IPYTHON_DIR' in env:
284 270 warnings.warn('The environment variable IPYTHON_DIR is deprecated. '
285 271 'Please use IPYTHONDIR instead.')
286 272 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
287 273 if ipdir is None:
288 274 # not set explicitly, use XDG_CONFIG_HOME or HOME
289 275 home_ipdir = pjoin(home_dir, ipdir_def)
290 276 if xdg_dir:
291 277 # use XDG, as long as the user isn't already
292 278 # using $HOME/.ipython and *not* XDG/ipython
293 279
294 280 xdg_ipdir = pjoin(xdg_dir, xdg_def)
295 281
296 282 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
297 283 ipdir = xdg_ipdir
298 284
299 285 if ipdir is None:
300 286 # not using XDG
301 287 ipdir = home_ipdir
302 288
303 289 ipdir = os.path.normpath(os.path.expanduser(ipdir))
304 290
305 291 if os.path.exists(ipdir) and not _writable_dir(ipdir):
306 292 # ipdir exists, but is not writable
307 293 warnings.warn("IPython dir '%s' is not a writable location,"
308 294 " using a temp directory."%ipdir)
309 295 ipdir = tempfile.mkdtemp()
310 296 elif not os.path.exists(ipdir):
311 297 parent = os.path.dirname(ipdir)
312 298 if not _writable_dir(parent):
313 299 # ipdir does not exist and parent isn't writable
314 300 warnings.warn("IPython parent '%s' is not a writable location,"
315 301 " using a temp directory."%parent)
316 302 ipdir = tempfile.mkdtemp()
317 303
318 304 return py3compat.cast_unicode(ipdir, fs_encoding)
319 305
320 306
321 307 def get_ipython_cache_dir():
322 308 """Get the cache directory it is created if it does not exist."""
323 309 xdgdir = get_xdg_cache_dir()
324 310 if xdgdir is None:
325 311 return get_ipython_dir()
326 312 ipdir = os.path.join(xdgdir, "ipython")
327 313 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
328 314 os.makedirs(ipdir)
329 315 elif not _writable_dir(xdgdir):
330 316 return get_ipython_dir()
331 317
332 318 return py3compat.cast_unicode(ipdir, fs_encoding)
333 319
334 320
335 321 def get_ipython_package_dir():
336 322 """Get the base directory where IPython itself is installed."""
337 323 ipdir = os.path.dirname(IPython.__file__)
338 324 return py3compat.cast_unicode(ipdir, fs_encoding)
339 325
340 326
341 327 def get_ipython_module_path(module_str):
342 328 """Find the path to an IPython module in this version of IPython.
343 329
344 330 This will always find the version of the module that is in this importable
345 331 IPython package. This will always return the path to the ``.py``
346 332 version of the module.
347 333 """
348 334 if module_str == 'IPython':
349 335 return os.path.join(get_ipython_package_dir(), '__init__.py')
350 336 mod = import_item(module_str)
351 337 the_path = mod.__file__.replace('.pyc', '.py')
352 338 the_path = the_path.replace('.pyo', '.py')
353 339 return py3compat.cast_unicode(the_path, fs_encoding)
354 340
355 341 def locate_profile(profile='default'):
356 342 """Find the path to the folder associated with a given profile.
357 343
358 344 I.e. find $IPYTHONDIR/profile_whatever.
359 345 """
360 346 from IPython.core.profiledir import ProfileDir, ProfileDirError
361 347 try:
362 348 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
363 349 except ProfileDirError:
364 350 # IOError makes more sense when people are expecting a path
365 351 raise IOError("Couldn't find profile %r" % profile)
366 352 return pd.location
367 353
368 354 def expand_path(s):
369 355 """Expand $VARS and ~names in a string, like a shell
370 356
371 357 :Examples:
372 358
373 359 In [2]: os.environ['FOO']='test'
374 360
375 361 In [3]: expand_path('variable FOO is $FOO')
376 362 Out[3]: 'variable FOO is test'
377 363 """
378 364 # This is a pretty subtle hack. When expand user is given a UNC path
379 365 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
380 366 # the $ to get (\\server\share\%username%). I think it considered $
381 367 # alone an empty var. But, we need the $ to remains there (it indicates
382 368 # a hidden share).
383 369 if os.name=='nt':
384 370 s = s.replace('$\\', 'IPYTHON_TEMP')
385 371 s = os.path.expandvars(os.path.expanduser(s))
386 372 if os.name=='nt':
387 373 s = s.replace('IPYTHON_TEMP', '$\\')
388 374 return s
389 375
390 376
391 377 def unescape_glob(string):
392 378 """Unescape glob pattern in `string`."""
393 379 def unescape(s):
394 380 for pattern in '*[]!?':
395 381 s = s.replace(r'\{0}'.format(pattern), pattern)
396 382 return s
397 383 return '\\'.join(map(unescape, string.split('\\\\')))
398 384
399 385
400 386 def shellglob(args):
401 387 """
402 388 Do glob expansion for each element in `args` and return a flattened list.
403 389
404 390 Unmatched glob pattern will remain as-is in the returned list.
405 391
406 392 """
407 393 expanded = []
408 394 # Do not unescape backslash in Windows as it is interpreted as
409 395 # path separator:
410 396 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
411 397 for a in args:
412 398 expanded.extend(glob.glob(a) or [unescape(a)])
413 399 return expanded
414 400
415 401
416 402 def target_outdated(target,deps):
417 403 """Determine whether a target is out of date.
418 404
419 405 target_outdated(target,deps) -> 1/0
420 406
421 407 deps: list of filenames which MUST exist.
422 408 target: single filename which may or may not exist.
423 409
424 410 If target doesn't exist or is older than any file listed in deps, return
425 411 true, otherwise return false.
426 412 """
427 413 try:
428 414 target_time = os.path.getmtime(target)
429 415 except os.error:
430 416 return 1
431 417 for dep in deps:
432 418 dep_time = os.path.getmtime(dep)
433 419 if dep_time > target_time:
434 420 #print "For target",target,"Dep failed:",dep # dbg
435 421 #print "times (dep,tar):",dep_time,target_time # dbg
436 422 return 1
437 423 return 0
438 424
439 425
440 426 def target_update(target,deps,cmd):
441 427 """Update a target with a given command given a list of dependencies.
442 428
443 429 target_update(target,deps,cmd) -> runs cmd if target is outdated.
444 430
445 431 This is just a wrapper around target_outdated() which calls the given
446 432 command if target is outdated."""
447 433
448 434 if target_outdated(target,deps):
449 435 system(cmd)
450 436
451 437 def filehash(path):
452 438 """Make an MD5 hash of a file, ignoring any differences in line
453 439 ending characters."""
454 440 with open(path, "rU") as f:
455 441 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
456 442
457 443 # If the config is unmodified from the default, we'll just delete it.
458 444 # These are consistent for 0.10.x, thankfully. We're not going to worry about
459 445 # older versions.
460 446 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
461 447 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
462 448
463 449 def check_for_old_config(ipython_dir=None):
464 450 """Check for old config files, and present a warning if they exist.
465 451
466 452 A link to the docs of the new config is included in the message.
467 453
468 454 This should mitigate confusion with the transition to the new
469 455 config system in 0.11.
470 456 """
471 457 if ipython_dir is None:
472 458 ipython_dir = get_ipython_dir()
473 459
474 460 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
475 461 warned = False
476 462 for cfg in old_configs:
477 463 f = os.path.join(ipython_dir, cfg)
478 464 if os.path.exists(f):
479 465 if filehash(f) == old_config_md5.get(cfg, ''):
480 466 os.unlink(f)
481 467 else:
482 468 warnings.warn("Found old IPython config file %r (modified by user)"%f)
483 469 warned = True
484 470
485 471 if warned:
486 472 warnings.warn("""
487 473 The IPython configuration system has changed as of 0.11, and these files will
488 474 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
489 475 of the new config system.
490 476 To start configuring IPython, do `ipython profile create`, and edit
491 477 `ipython_config.py` in <ipython_dir>/profile_default.
492 478 If you need to leave the old config files in place for an older version of
493 479 IPython and want to suppress this warning message, set
494 480 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
495 481
496 482 def get_security_file(filename, profile='default'):
497 483 """Return the absolute path of a security file given by filename and profile
498 484
499 485 This allows users and developers to find security files without
500 486 knowledge of the IPython directory structure. The search path
501 487 will be ['.', profile.security_dir]
502 488
503 489 Parameters
504 490 ----------
505 491
506 492 filename : str
507 493 The file to be found. If it is passed as an absolute path, it will
508 494 simply be returned.
509 495 profile : str [default: 'default']
510 496 The name of the profile to search. Leaving this unspecified
511 497 The file to be found. If it is passed as an absolute path, fname will
512 498 simply be returned.
513 499
514 500 Returns
515 501 -------
516 502 Raises :exc:`IOError` if file not found or returns absolute path to file.
517 503 """
518 504 # import here, because profiledir also imports from utils.path
519 505 from IPython.core.profiledir import ProfileDir
520 506 try:
521 507 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
522 508 except Exception:
523 509 # will raise ProfileDirError if no such profile
524 510 raise IOError("Profile %r not found")
525 511 return filefind(filename, ['.', pd.security_dir])
526 512
General Comments 0
You need to be logged in to leave comments. Login now