"""
import sys, exceptions, os, stat, tempfile, time, binascii
+from collections import namedtuple
+from errno import ENOENT
+
+if sys.platform == "win32":
+ from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_ulonglong, \
+ create_unicode_buffer, get_last_error
+ from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR, LPVOID, HANDLE
from twisted.python import log
except ImportError:
pass
-def abspath_expanduser_unicode(path, base=None):
+def abspath_expanduser_unicode(path, base=None, long_path=True):
"""
Return the absolute version of a path. If 'base' is given and 'path' is relative,
the path will be expanded relative to 'base'.
'path' must be a Unicode string. 'base', if given, must be a Unicode string
corresponding to an absolute path as returned by a previous call to
abspath_expanduser_unicode.
+ On Windows, the result will be a long path unless long_path is given as False.
"""
if not isinstance(path, unicode):
raise AssertionError("paths must be Unicode strings")
- if base is not None:
+ if base is not None and long_path:
precondition_abspath(base)
path = expanduser(path)
# there is always at least one Unicode path component.
path = os.path.normpath(path)
- if sys.platform == "win32":
+ if sys.platform == "win32" and long_path:
path = to_windows_long_path(path)
return path
have_GetDiskFreeSpaceExW = False
if sys.platform == "win32":
- from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong, create_unicode_buffer, \
- get_last_error
- from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR
-
# <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx>
GetEnvironmentVariableW = WINFUNCTYPE(
- DWORD,
- LPCWSTR, LPWSTR, DWORD,
+ DWORD, LPCWSTR, LPWSTR, DWORD,
use_last_error=True
- )(("GetEnvironmentVariableW", windll.kernel32))
+ )(("GetEnvironmentVariableW", windll.kernel32))
try:
# <http://msdn.microsoft.com/en-us/library/aa383742%28v=VS.85%29.aspx>
# <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx>
GetDiskFreeSpaceExW = WINFUNCTYPE(
- BOOL,
- LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
+ BOOL, LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
use_last_error=True
- )(("GetDiskFreeSpaceExW", windll.kernel32))
+ )(("GetDiskFreeSpaceExW", windll.kernel32))
have_GetDiskFreeSpaceExW = True
except Exception:
err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND:
return None
- raise OSError("Windows error %d attempting to read size of environment variable %r"
- % (err, name))
+ raise OSError("WinError: %s\n attempting to read size of environment variable %r"
+ % (WinError(err), name))
if n == 1:
# Avoid an ambiguity between a zero-length string and an error in the return value of the
# call to GetEnvironmentVariableW below.
err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND:
return None
- raise OSError("Windows error %d attempting to read environment variable %r"
- % (err, name))
+ raise OSError("WinError: %s\n attempting to read environment variable %r"
+ % (WinError(err), name))
if retval >= n:
raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r"
% (retval, n, name))
byref(n_total),
byref(n_free_for_root))
if retval == 0:
- raise OSError("Windows error %d attempting to get disk statistics for %r"
- % (get_last_error(), whichdir))
+ raise OSError("WinError: %s\n attempting to get disk statistics for %r"
+ % (WinError(get_last_error()), whichdir))
free_for_nonroot = n_free_for_nonroot.value
total = n_total.value
free_for_root = n_free_for_root.value
except EnvironmentError:
log.msg("OS call to get disk statistics failed")
return 0
+
+
+if sys.platform == "win32":
+ # <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx>
+ CreateFileW = WINFUNCTYPE(HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE) \
+ (("CreateFileW", windll.kernel32))
+
+ GENERIC_WRITE = 0x40000000
+ FILE_SHARE_READ = 0x00000001
+ FILE_SHARE_WRITE = 0x00000002
+ OPEN_EXISTING = 3
+ INVALID_HANDLE_VALUE = 0xFFFFFFFF
+
+ # <http://msdn.microsoft.com/en-us/library/aa364439%28v=vs.85%29.aspx>
+ FlushFileBuffers = WINFUNCTYPE(BOOL, HANDLE)(("FlushFileBuffers", windll.kernel32))
+
+ # <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx>
+ CloseHandle = WINFUNCTYPE(BOOL, HANDLE)(("CloseHandle", windll.kernel32))
+
+ # <http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/>
+ def flush_volume(path):
+ drive = os.path.splitdrive(os.path.realpath(path))[0]
+
+ hVolume = CreateFileW(u"\\\\.\\" + drive,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ None,
+ OPEN_EXISTING,
+ 0,
+ None
+ )
+ if hVolume == INVALID_HANDLE_VALUE:
+ raise WinError()
+
+ if FlushFileBuffers(hVolume) == 0:
+ raise WinError()
+
+ CloseHandle(hVolume)
+else:
+ def flush_volume(path):
+ # use sync()?
+ pass
+
+
+class ConflictError(Exception):
+ pass
+
+class UnableToUnlinkReplacementError(Exception):
+ pass
+
+def reraise(wrapper):
+ _, exc, tb = sys.exc_info()
+ wrapper_exc = wrapper("%s: %s" % (exc.__class__.__name__, exc))
+ raise wrapper_exc.__class__, wrapper_exc, tb
+
+if sys.platform == "win32":
+ # <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512%28v=vs.85%29.aspx>
+ ReplaceFileW = WINFUNCTYPE(
+ BOOL,
+ LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID,
+ use_last_error=True
+ )(("ReplaceFileW", windll.kernel32))
+
+ REPLACEFILE_IGNORE_MERGE_ERRORS = 0x00000002
+
+ def rename_no_overwrite(source_path, dest_path):
+ os.rename(source_path, dest_path)
+
+ def replace_file(replaced_path, replacement_path, backup_path):
+ precondition_abspath(replaced_path)
+ precondition_abspath(replacement_path)
+ precondition_abspath(backup_path)
+
+ r = ReplaceFileW(replaced_path, replacement_path, backup_path,
+ REPLACEFILE_IGNORE_MERGE_ERRORS, None, None)
+ if r == 0:
+ # The UnableToUnlinkReplacementError case does not happen on Windows;
+ # all errors should be treated as signalling a conflict.
+ err = get_last_error()
+ raise ConflictError("WinError: %s" % (WinError(err)))
+else:
+ def rename_no_overwrite(source_path, dest_path):
+ # link will fail with EEXIST if there is already something at dest_path.
+ os.link(source_path, dest_path)
+ try:
+ os.unlink(source_path)
+ except EnvironmentError:
+ reraise(UnableToUnlinkReplacementError)
+
+ def replace_file(replaced_path, replacement_path, backup_path):
+ precondition_abspath(replaced_path)
+ precondition_abspath(replacement_path)
+ precondition_abspath(backup_path)
+
+ if not os.path.exists(replacement_path):
+ raise ConflictError("Replacement file not found: %r" % (replacement_path,))
+
+ try:
+ os.rename(replaced_path, backup_path)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ try:
+ rename_no_overwrite(replacement_path, replaced_path)
+ except EnvironmentError:
+ reraise(ConflictError)
+
+PathInfo = namedtuple('PathInfo', 'isdir isfile islink exists size mtime ctime')
+
+def get_pathinfo(path_u, now=None):
+ try:
+ statinfo = os.lstat(path_u)
+ mode = statinfo.st_mode
+ return PathInfo(isdir =stat.S_ISDIR(mode),
+ isfile=stat.S_ISREG(mode),
+ islink=stat.S_ISLNK(mode),
+ exists=True,
+ size =statinfo.st_size,
+ mtime =statinfo.st_mtime,
+ ctime =statinfo.st_ctime,
+ )
+ except OSError as e:
+ if e.errno == ENOENT:
+ if now is None:
+ now = time.time()
+ return PathInfo(isdir =False,
+ isfile=False,
+ islink=False,
+ exists=False,
+ size =None,
+ mtime =now,
+ ctime =now,
+ )
+ raise