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