33
# begin copy of werkzeug.posixemulation from werkzeug 0.6.1(pre) repo
35
werkzeug.posixemulation
36
~~~~~~~~~~~~~~~~~~~~~~~
38
Provides a POSIX emulation for some features that are relevant to
39
web applications. The main purpose is to simplify support for
40
systems such as Windows NT that are not 100% POSIX compatible.
42
Currently this only implements a :func:`rename` function that
43
follows POSIX semantics. Eg: if the target file already exists it
44
will be replaced without asking.
46
This module was introduced in 0.6.1 and is not a public interface.
47
It might become one in later versions of Werkzeug.
49
:copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
50
:license: BSD, see LICENSE for more details.
57
can_rename_open_file = False
59
_rename = lambda src, dst: False
60
_rename_atomic = lambda src, dst: False
64
_GetLastError = ctypes.windll.kernel32.GetLastError
66
def _LogLastError(fn):
68
logging.debug("%s returned: %r" % (fn, err))
70
# 6800 = The function attempted to use a name that is reserved
71
# for use by another transaction.
73
_MOVEFILE_REPLACE_EXISTING = 0x1
74
_MOVEFILE_WRITE_THROUGH = 0x8
75
_MoveFileEx = ctypes.windll.kernel32.MoveFileExW
77
def _rename(src, dst):
78
if not isinstance(src, unicode):
79
src = unicode(src, sys.getfilesystemencoding())
80
if not isinstance(dst, unicode):
81
dst = unicode(dst, sys.getfilesystemencoding())
82
if _rename_atomic(src, dst):
84
logging.debug("PSEUDO-atomic rename %r %r" % (src, dst))
87
while not ret and retry < 100:
88
ret = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
89
_MOVEFILE_WRITE_THROUGH)
91
_LogLastError("MoveFileExW")
94
logging.debug("retry %d after sleeping" % retry)
97
# this stuff only exists in windows >= vista / windows server 2008
98
_CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
99
_CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
100
_MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
101
_CloseHandle = ctypes.windll.kernel32.CloseHandle
102
can_rename_open_file = True
104
def _rename_atomic(src, dst):
105
ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
107
_LogLastError("CreateTransaction")
110
logging.debug("atomic rename %r %r" % (src, dst))
113
while not ret and retry < 100:
114
ret = _MoveFileTransacted(src, dst, None, None,
115
_MOVEFILE_REPLACE_EXISTING |
116
_MOVEFILE_WRITE_THROUGH, ta)
118
_LogLastError("MoveFileTransacted")
121
logging.debug("retry %d after sleeping" % retry)
123
ret = _CommitTransaction(ta)
125
_LogLastError("CommitTransaction")
128
ret = _CloseHandle(ta)
130
_LogLastError("CloseHandle")
134
def rename(src, dst):
135
# Try atomic or pseudo-atomic rename
136
if _rename(src, dst):
138
# Fall back to "move away and replace"
139
logging.debug("NON-atomic rename %r %r" % (src, dst))
143
if e.errno != errno.EEXIST:
145
old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
153
#logging.debug("atomic os.rename (POSIX)")
155
can_rename_open_file = True
157
# end copy of werkzeug.posixemulation
159
rename_overwrite = rename
161
def rename_no_overwrite(oldname, newname, delete_old=False):
28
def rename(oldname, newname):
162
29
""" Multiplatform rename
164
This kind of rename is doing things differently: it fails if newname
165
already exists. This is the usual thing on win32, but not on posix.
167
If delete_old is True, oldname is removed in any case (even if the
168
rename did not succeed).
31
Needed because win32 rename is not POSIX compliant, and does not
32
remove target file if it exists.
34
Problem: this "rename" is not atomic any more on win32.
36
FIXME: What about rename locking? we can have a lock file in the
37
page directory, named: PageName.lock, and lock this file before we
38
rename, then unlock when finished.
170
40
if os.name == 'nt':
173
os.rename(oldname, newname)
179
if not success and delete_old:
184
os.link(oldname, newname)
190
if success or delete_old:
41
if os.path.isfile(newname):
45
pass # let os.rename give us the error (if any)
46
os.rename(oldname, newname)
195
49
if sys.platform == 'win32':
214
68
os.utime(name, None)
217
def access_denied_decorator(fn):
218
""" Due to unknown reasons, some os.* functions on Win32 sometimes fail
219
with Access Denied (although access should be possible).
220
Just retrying it a bit later works and this is what we do.
222
if sys.platform == 'win32':
223
def wrapper(*args, **kwargs):
228
return fn(*args, **kwargs)
231
if retry > max_retries:
233
if err.errno == errno.EACCES:
234
logging.warning('%s(%r, %r) -> access denied. retrying...' % (fn.__name__, args, kwargs))
242
stat = access_denied_decorator(os.stat)
243
mkdir = access_denied_decorator(os.mkdir)
244
rmdir = access_denied_decorator(os.rmdir)
247
70
def fuid(filename, max_staleness=3600):
248
71
""" return a unique id for a file
393
214
from Carbon import File
395
return File.FSRef(path).as_pathname()
398
215
except ImportError:
218
return File.FSRef(path).as_pathname()
403
224
def realPathCase(path):
406
# dircache stuff seems to be broken on win32 (at least for FAT32, maybe NTFS).
407
# dircache stuff is also broken on POSIX if updates happen too fast (< 1s).
408
DCENABLED = 0 # set to 0 to completely disable dircache usage
410
# Note: usage of the dc* functions below is deprecated, they'll get removed soon.
411
dc_deprecated = "dircache function calls (dcdisable,dclistdir,dcreset) are deprecated, please fix caller"
227
# dircache stuff seems to be broken on win32 (at least for FAT32, maybe NTFS)
228
DCENABLED = 1 # set to 0 to disable dirchache usage
414
warnings.warn(dc_deprecated, DeprecationWarning, stacklevel=2)
420
235
def dclistdir(path):
421
warnings.warn(dc_deprecated, DeprecationWarning, stacklevel=2)
422
236
if sys.platform == 'win32' or not DCENABLED:
423
237
return os.listdir(path)
425
239
return dircache.listdir(path)
428
warnings.warn(dc_deprecated, DeprecationWarning, stacklevel=2)
429
242
if sys.platform == 'win32' or not DCENABLED:
432
245
return dircache.reset()