~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/util/filesys.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
"""
3
3
    MoinMoin - File System Utilities
4
4
 
5
 
    @copyright: 2002 Juergen Hermann <jh@web.de>,
6
 
                2006-2008 MoinMoin:ThomasWaldmann
 
5
    @copyright: 2002 Juergen Hermann <jh@web.de>
7
6
    @license: GNU GPL, see COPYING for details.
8
7
"""
9
8
 
10
 
import sys, os, shutil, time, errno
 
9
import sys, os, shutil, time
11
10
from stat import S_ISDIR, ST_MODE, S_IMODE
12
 
import warnings
13
 
 
14
 
from MoinMoin import log
15
 
logging = log.getLogger(__name__)
16
11
 
17
12
#############################################################################
18
13
### Misc Helpers
30
25
            raise
31
26
 
32
27
 
33
 
# begin copy of werkzeug.posixemulation from werkzeug 0.6.1(pre) repo
34
 
r"""
35
 
    werkzeug.posixemulation
36
 
    ~~~~~~~~~~~~~~~~~~~~~~~
37
 
 
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.
41
 
 
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.
45
 
 
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.
48
 
 
49
 
    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
50
 
    :license: BSD, see LICENSE for more details.
51
 
"""
52
 
import os
53
 
import errno
54
 
import random
55
 
 
56
 
 
57
 
can_rename_open_file = False
58
 
if os.name == 'nt':
59
 
    _rename = lambda src, dst: False
60
 
    _rename_atomic = lambda src, dst: False
61
 
 
62
 
    try:
63
 
        import ctypes
64
 
        _GetLastError = ctypes.windll.kernel32.GetLastError
65
 
 
66
 
        def _LogLastError(fn):
67
 
            err = _GetLastError()
68
 
            logging.debug("%s returned: %r" % (fn, err))
69
 
            # 5 = Access Denied
70
 
            # 6800 = The function attempted to use a name that is reserved
71
 
            #        for use by another transaction.
72
 
 
73
 
        _MOVEFILE_REPLACE_EXISTING = 0x1
74
 
        _MOVEFILE_WRITE_THROUGH = 0x8
75
 
        _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
76
 
 
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):
83
 
                return True
84
 
            logging.debug("PSEUDO-atomic rename %r %r" % (src, dst))
85
 
            retry = 0
86
 
            ret = 0
87
 
            while not ret and retry < 100:
88
 
                ret = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
89
 
                                            _MOVEFILE_WRITE_THROUGH)
90
 
                if ret == 0:
91
 
                    _LogLastError("MoveFileExW")
92
 
                    time.sleep(0.001)
93
 
                    retry += 1
94
 
                    logging.debug("retry %d after sleeping" % retry)
95
 
            return ret
96
 
 
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
103
 
 
104
 
        def _rename_atomic(src, dst):
105
 
            ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
106
 
            if ta == -1:
107
 
                _LogLastError("CreateTransaction")
108
 
                return False
109
 
            try:
110
 
                logging.debug("atomic rename %r %r" % (src, dst))
111
 
                retry = 0
112
 
                ret = 0
113
 
                while not ret and retry < 100:
114
 
                    ret = _MoveFileTransacted(src, dst, None, None,
115
 
                                               _MOVEFILE_REPLACE_EXISTING |
116
 
                                               _MOVEFILE_WRITE_THROUGH, ta)
117
 
                    if ret == 0:
118
 
                        _LogLastError("MoveFileTransacted")
119
 
                        time.sleep(0.001)
120
 
                        retry += 1
121
 
                        logging.debug("retry %d after sleeping" % retry)
122
 
                    else:
123
 
                        ret = _CommitTransaction(ta)
124
 
                        if ret == 0:
125
 
                            _LogLastError("CommitTransaction")
126
 
                return ret
127
 
            finally:
128
 
                ret = _CloseHandle(ta)
129
 
                if ret == 0:
130
 
                    _LogLastError("CloseHandle")
131
 
    except Exception:
132
 
        pass
133
 
 
134
 
    def rename(src, dst):
135
 
        # Try atomic or pseudo-atomic rename
136
 
        if _rename(src, dst):
137
 
            return
138
 
        # Fall back to "move away and replace"
139
 
        logging.debug("NON-atomic rename %r %r" % (src, dst))
140
 
        try:
141
 
            os.rename(src, dst)
142
 
        except OSError, e:
143
 
            if e.errno != errno.EEXIST:
144
 
                raise
145
 
            old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
146
 
            os.rename(dst, old)
147
 
            os.rename(src, dst)
148
 
            try:
149
 
                os.unlink(old)
150
 
            except Exception:
151
 
                pass
152
 
else:
153
 
    #logging.debug("atomic os.rename (POSIX)")
154
 
    rename = os.rename
155
 
    can_rename_open_file = True
156
 
 
157
 
# end copy of werkzeug.posixemulation
158
 
 
159
 
rename_overwrite = rename
160
 
 
161
 
def rename_no_overwrite(oldname, newname, delete_old=False):
 
28
def rename(oldname, newname):
162
29
    """ Multiplatform rename
163
30
 
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.
166
 
 
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.
 
33
 
 
34
    Problem: this "rename" is not atomic any more on win32.
 
35
 
 
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.
169
39
    """
170
40
    if os.name == 'nt':
171
 
        try:
172
 
            try:
173
 
                os.rename(oldname, newname)
174
 
                success = True
175
 
            except:
176
 
                success = False
177
 
                raise
178
 
        finally:
179
 
            if not success and delete_old:
180
 
                os.unlink(oldname)
181
 
    else:
182
 
        try:
183
 
            try:
184
 
                os.link(oldname, newname)
185
 
                success = True
186
 
            except:
187
 
                success = False
188
 
                raise
189
 
        finally:
190
 
            if success or delete_old:
191
 
                os.unlink(oldname)
192
 
 
 
41
        if os.path.isfile(newname):
 
42
            try:
 
43
                os.remove(newname)
 
44
            except OSError:
 
45
                pass # let os.rename give us the error (if any)
 
46
    os.rename(oldname, newname)
193
47
 
194
48
def touch(name):
195
49
    if sys.platform == 'win32':
213
67
    else:
214
68
        os.utime(name, None)
215
69
 
216
 
 
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.
221
 
    """
222
 
    if sys.platform == 'win32':
223
 
        def wrapper(*args, **kwargs):
224
 
            max_retries = 42
225
 
            retry = 0
226
 
            while True:
227
 
                try:
228
 
                    return fn(*args, **kwargs)
229
 
                except OSError, err:
230
 
                    retry += 1
231
 
                    if retry > max_retries:
232
 
                        raise
233
 
                    if err.errno == errno.EACCES:
234
 
                        logging.warning('%s(%r, %r) -> access denied. retrying...' % (fn.__name__, args, kwargs))
235
 
                        time.sleep(0.01)
236
 
                        continue
237
 
                    raise
238
 
        return wrapper
239
 
    else:
240
 
        return fn
241
 
 
242
 
stat = access_denied_decorator(os.stat)
243
 
mkdir = access_denied_decorator(os.mkdir)
244
 
rmdir = access_denied_decorator(os.rmdir)
245
 
 
246
 
 
247
70
def fuid(filename, max_staleness=3600):
248
71
    """ return a unique id for a file
249
72
 
288
111
            # trick
289
112
            now = int(time.time())
290
113
            if now >= st.st_mtime + max_staleness:
291
 
                # keep same fake_mtime for each max_staleness interval
292
 
                fake_mtime = int(now / max_staleness) * max_staleness
 
114
                fake_mtime = now
293
115
        uid = (st.st_mtime,  # might have a rather rough granularity, e.g. 2s
294
 
                             # on FAT, 1s on ext3 and might not change on fast
295
 
                             # updates
 
116
                             # on FAT and might not change on fast updates
296
117
               st.st_ino,  # inode number (will change if the update is done
297
118
                           # by e.g. renaming a temp file to the real file).
298
119
                           # not supported on win32 (0 ever)
391
212
        """
392
213
        try:
393
214
            from Carbon import File
394
 
            try:
395
 
                return File.FSRef(path).as_pathname()
396
 
            except File.Error:
397
 
                return None
398
215
        except ImportError:
399
216
            return None
 
217
        try:
 
218
            return File.FSRef(path).as_pathname()
 
219
        except File.Error:
 
220
            return None
400
221
 
401
222
else:
402
223
 
403
224
    def realPathCase(path):
404
225
        return None
405
226
 
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
409
 
 
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"
412
 
 
 
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
413
229
def dcdisable():
414
 
    warnings.warn(dc_deprecated, DeprecationWarning, stacklevel=2)
415
230
    global DCENABLED
416
231
    DCENABLED = 0
417
232
 
418
233
import dircache
419
234
 
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)
424
238
    else:
425
239
        return dircache.listdir(path)
426
240
 
427
241
def dcreset():
428
 
    warnings.warn(dc_deprecated, DeprecationWarning, stacklevel=2)
429
242
    if sys.platform == 'win32' or not DCENABLED:
430
243
        return
431
244
    else:
432
245
        return dircache.reset()
433