Back to index

moin  1.9.0~rc2
Functions | Variables
MoinMoin.util.filesys Namespace Reference

Functions

def chmod
 Misc Helpers.
def rename
def rename_no_overwrite
def touch
def access_denied_decorator
def fuid
def copystat
def copytree
def lock
def unlock
def realPathCase
def dcdisable
def dclistdir
def dcreset

Variables

tuple logging = log.getLogger(__name__)
 rename_overwrite = rename
tuple stat = access_denied_decorator(os.stat)
tuple mkdir = access_denied_decorator(os.mkdir)
tuple rmdir = access_denied_decorator(os.rmdir)
int LOCK_EX = 0
int DCENABLED = 1

Function Documentation

Due to unknown reasons, some os.* functions on Win32 sometimes fail
    with Access Denied (although access should be possible).
    Just retrying it a bit later works and this is what we do.

Definition at line 138 of file filesys.py.

00138 
00139 def access_denied_decorator(fn):
00140     """ Due to unknown reasons, some os.* functions on Win32 sometimes fail
00141         with Access Denied (although access should be possible).
00142         Just retrying it a bit later works and this is what we do.
00143     """
00144     if sys.platform == 'win32':
00145         def wrapper(*args, **kwargs):
00146             max_retries = 42
00147             retry = 0
00148             while True:
00149                 try:
00150                     return fn(*args, **kwargs)
00151                 except OSError, err:
00152                     retry += 1
00153                     if retry > max_retries:
00154                         raise
00155                     if err.errno == errno.EACCES:
00156                         logging.warning('%s(%r, %r) -> access denied. retrying...' % (fn.__name__, args, kwargs))
00157                         time.sleep(0.01)
00158                         continue
00159                     raise
00160         return wrapper
00161     else:
00162         return fn

def MoinMoin.util.filesys.chmod (   name,
  mode,
  catchexception = True 
)

Misc Helpers.

change mode of some file/dir on platforms that support it.
    usually you don't need this because we use os.umask() when importing
    request.py

Definition at line 20 of file filesys.py.

00020 
00021 def chmod(name, mode, catchexception=True):
00022     """ change mode of some file/dir on platforms that support it.
00023         usually you don't need this because we use os.umask() when importing
00024         request.py
00025     """
00026     try:
00027         os.chmod(name, mode)
00028     except OSError:
00029         if not catchexception:
00030             raise
00031 

Here is the caller graph for this function:

def MoinMoin.util.filesys.copystat (   src,
  dst 
)
Copy stat bits from src to dst

This should be used when shutil.copystat would be used on directories
on win32 because win32 does not support utime() for directories.

According to the official docs written by Microsoft, it returns ENOACCES if the
supplied filename is a directory. Looks like a trainee implemented the function.

Definition at line 228 of file filesys.py.

00228 
00229 def copystat(src, dst):
00230     """Copy stat bits from src to dst
00231 
00232     This should be used when shutil.copystat would be used on directories
00233     on win32 because win32 does not support utime() for directories.
00234 
00235     According to the official docs written by Microsoft, it returns ENOACCES if the
00236     supplied filename is a directory. Looks like a trainee implemented the function.
00237     """
00238     if sys.platform == 'win32' and S_ISDIR(os.stat(dst)[ST_MODE]):
00239         if os.name == 'nt':
00240             st = os.stat(src)
00241             mode = S_IMODE(st[ST_MODE])
00242             if hasattr(os, 'chmod'):
00243                 os.chmod(dst, mode) # KEEP THIS ONE!
00244         #else: pass # we are on Win9x,ME - no chmod here
00245     else:
00246         shutil.copystat(src, dst)
00247 

Here is the caller graph for this function:

def MoinMoin.util.filesys.copytree (   src,
  dst,
  symlinks = False 
)
Recursively copy a directory tree using copy2().

The destination directory must not already exist.
If exception(s) occur, an Error is raised with a list of reasons.

If the optional symlinks flag is true, symbolic links in the
source tree result in symbolic links in the destination tree; if
it is false, the contents of the files pointed to by symbolic
links are copied.

In contrary to shutil.copytree, this version also copies directory
stats, not only file stats.

Definition at line 248 of file filesys.py.

00248 
00249 def copytree(src, dst, symlinks=False):
00250     """Recursively copy a directory tree using copy2().
00251 
00252     The destination directory must not already exist.
00253     If exception(s) occur, an Error is raised with a list of reasons.
00254 
00255     If the optional symlinks flag is true, symbolic links in the
00256     source tree result in symbolic links in the destination tree; if
00257     it is false, the contents of the files pointed to by symbolic
00258     links are copied.
00259 
00260     In contrary to shutil.copytree, this version also copies directory
00261     stats, not only file stats.
00262 
00263     """
00264     names = os.listdir(src)
00265     os.mkdir(dst)
00266     copystat(src, dst)
00267     errors = []
00268     for name in names:
00269         srcname = os.path.join(src, name)
00270         dstname = os.path.join(dst, name)
00271         try:
00272             if symlinks and os.path.islink(srcname):
00273                 linkto = os.readlink(srcname)
00274                 os.symlink(linkto, dstname)
00275             elif os.path.isdir(srcname):
00276                 copytree(srcname, dstname, symlinks)
00277             else:
00278                 shutil.copy2(srcname, dstname)
00279             # XXX What about devices, sockets etc.?
00280         except (IOError, os.error), why:
00281             errors.append((srcname, dstname, why))
00282     if errors:
00283         raise EnvironmentError, errors
00284 
00285 # Code could come from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65203
00286 
# we currently do not support locking

Here is the call graph for this function:

Definition at line 329 of file filesys.py.

00329 
00330 def dcdisable():
00331     global DCENABLED
00332     DCENABLED = 0

Definition at line 335 of file filesys.py.

00335 
00336 def dclistdir(path):
00337     if sys.platform == 'win32' or not DCENABLED:
00338         return os.listdir(path)
00339     else:
00340         return dircache.listdir(path)

Definition at line 341 of file filesys.py.

00341 
00342 def dcreset():
00343     if sys.platform == 'win32' or not DCENABLED:
00344         return
00345     else:
00346         return dircache.reset()
def MoinMoin.util.filesys.fuid (   filename,
  max_staleness = 3600 
)
return a unique id for a file

    Using just the file's mtime to determine if the file has changed is
    not reliable - if file updates happen faster than the file system's
    mtime granularity, then the modification is not detectable because
    the mtime is still the same.

    This function tries to improve by using not only the mtime, but also
    other metadata values like file size and inode to improve reliability.

    For the calculation of this value, we of course only want to use data
    that we can get rather fast, thus we use file metadata, not file data
    (file content).

    Note: depending on the operating system capabilities and the way the
          file update is done, this function might return the same value
          even if the file has changed. It should be better than just
          using file's mtime though.
          max_staleness tries to avoid the worst for these cases.

    @param filename: file name of the file to look at
    @param max_staleness: if a file is older than that, we may consider
                          it stale and return a different uid - this is a
                          dirty trick to work around changes never being
                          detected. Default is 3600 seconds, use None to
                          disable this trickery. See below for more details.
    @return: an object that changes value if the file changed,
             None is returned if there were problems accessing the file

Definition at line 168 of file filesys.py.

00168 
00169 def fuid(filename, max_staleness=3600):
00170     """ return a unique id for a file
00171 
00172         Using just the file's mtime to determine if the file has changed is
00173         not reliable - if file updates happen faster than the file system's
00174         mtime granularity, then the modification is not detectable because
00175         the mtime is still the same.
00176 
00177         This function tries to improve by using not only the mtime, but also
00178         other metadata values like file size and inode to improve reliability.
00179 
00180         For the calculation of this value, we of course only want to use data
00181         that we can get rather fast, thus we use file metadata, not file data
00182         (file content).
00183 
00184         Note: depending on the operating system capabilities and the way the
00185               file update is done, this function might return the same value
00186               even if the file has changed. It should be better than just
00187               using file's mtime though.
00188               max_staleness tries to avoid the worst for these cases.
00189 
00190         @param filename: file name of the file to look at
00191         @param max_staleness: if a file is older than that, we may consider
00192                               it stale and return a different uid - this is a
00193                               dirty trick to work around changes never being
00194                               detected. Default is 3600 seconds, use None to
00195                               disable this trickery. See below for more details.
00196         @return: an object that changes value if the file changed,
00197                  None is returned if there were problems accessing the file
00198     """
00199     try:
00200         st = os.stat(filename)
00201     except (IOError, OSError):
00202         uid = None  # for permanent errors on stat() this does not change, but
00203                     # having a changing value would be pointless because if we
00204                     # can't even stat the file, it is unlikely we can read it.
00205     else:
00206         fake_mtime = int(st.st_mtime)
00207         if not st.st_ino and max_staleness:
00208             # st_ino being 0 likely means that we run on a platform not
00209             # supporting it (e.g. win32) - thus we likely need this dirty
00210             # trick
00211             now = int(time.time())
00212             if now >= st.st_mtime + max_staleness:
00213                 # keep same fake_mtime for each max_staleness interval
00214                 fake_mtime = int(now / max_staleness) * max_staleness
00215         uid = (st.st_mtime,  # might have a rather rough granularity, e.g. 2s
00216                              # on FAT, 1s on ext3 and might not change on fast
00217                              # updates
00218                st.st_ino,  # inode number (will change if the update is done
00219                            # by e.g. renaming a temp file to the real file).
00220                            # not supported on win32 (0 ever)
00221                st.st_size,  # likely to change on many updates, but not
00222                             # sufficient alone
00223                fake_mtime,  # trick to workaround file system / platform
00224                             # limitations causing permanent trouble
00225               )
00226     return uid
00227 

def MoinMoin.util.filesys.lock (   file,
  flags 
)

Definition at line 289 of file filesys.py.

00289 
00290 def lock(file, flags):
00291     raise NotImplementedError

Return the real case of path e.g. PageName for pagename

HFS and HFS+ file systems, are case preserving but case
insensitive. You can't have 'file' and 'File' in the same
directory, but you can get the real name of 'file'.

@param path: string
@rtype: string
@return the real case of path or None

Definition at line 302 of file filesys.py.

00302 
00303     def realPathCase(path):
00304         """ Return the real case of path e.g. PageName for pagename
00305 
00306         HFS and HFS+ file systems, are case preserving but case
00307         insensitive. You can't have 'file' and 'File' in the same
00308         directory, but you can get the real name of 'file'.
00309 
00310         @param path: string
00311         @rtype: string
00312         @return the real case of path or None
00313         """
00314         try:
00315             from Carbon import File
00316             try:
00317                 return File.FSRef(path).as_pathname()
00318             except File.Error:
00319                 return None
00320         except ImportError:
00321             return None
00322 
00323 else:

def MoinMoin.util.filesys.rename (   oldname,
  newname 
)
Multiplatform rename

Needed because win32 rename is not POSIX compliant, and does not
remove target file if it exists.

Problem: this "rename" is not atomic any more on win32.

FIXME: What about rename locking? we can have a lock file in the
page directory, named: PageName.lock, and lock this file before we
rename, then unlock when finished.

Definition at line 32 of file filesys.py.

00032 
00033 def rename(oldname, newname):
00034     """ Multiplatform rename
00035 
00036     Needed because win32 rename is not POSIX compliant, and does not
00037     remove target file if it exists.
00038 
00039     Problem: this "rename" is not atomic any more on win32.
00040 
00041     FIXME: What about rename locking? we can have a lock file in the
00042     page directory, named: PageName.lock, and lock this file before we
00043     rename, then unlock when finished.
00044     """
00045     if os.name == 'nt':
00046         # Windows "rename" taken from Mercurial's util.py. Thanks!
00047         try:
00048             os.rename(oldname, newname)
00049         except OSError, err:
00050             # On windows, rename to existing file is not allowed, so we
00051             # must delete destination first. But if a file is open, unlink
00052             # schedules it for delete but does not delete it. Rename
00053             # happens immediately even for open files, so we rename
00054             # destination to a temporary name, then delete that. Then
00055             # rename is safe to do.
00056             # The temporary name is chosen at random to avoid the situation
00057             # where a file is left lying around from a previous aborted run.
00058             # The usual race condition this introduces can't be avoided as
00059             # we need the name to rename into, and not the file itself. Due
00060             # to the nature of the operation however, any races will at worst
00061             # lead to the rename failing and the current operation aborting.
00062 
00063             if err.errno != errno.EEXIST:
00064                 raise
00065 
00066             def tempname(prefix):
00067                 for tries in xrange(10):
00068                     temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
00069                     if not os.path.exists(temp):
00070                         return temp
00071                 raise IOError, (errno.EEXIST, "No usable temporary filename found")
00072 
00073             temp = tempname(newname)
00074             os.rename(newname, temp)
00075             os.unlink(temp)
00076             os.rename(oldname, newname)
00077     else:
00078         # POSIX: just do it :)
00079         os.rename(oldname, newname)

Here is the caller graph for this function:

def MoinMoin.util.filesys.rename_no_overwrite (   oldname,
  newname,
  delete_old = False 
)
Multiplatform rename

This kind of rename is doing things differently: it fails if newname
already exists. This is the usual thing on win32, but not on posix.

If delete_old is True, oldname is removed in any case (even if the
rename did not succeed).

Definition at line 82 of file filesys.py.

00082 
00083 def rename_no_overwrite(oldname, newname, delete_old=False):
00084     """ Multiplatform rename
00085 
00086     This kind of rename is doing things differently: it fails if newname
00087     already exists. This is the usual thing on win32, but not on posix.
00088 
00089     If delete_old is True, oldname is removed in any case (even if the
00090     rename did not succeed).
00091     """
00092     if os.name == 'nt':
00093         try:
00094             try:
00095                 os.rename(oldname, newname)
00096                 success = True
00097             except:
00098                 success = False
00099                 raise
00100         finally:
00101             if not success and delete_old:
00102                 os.unlink(oldname)
00103     else:
00104         try:
00105             try:
00106                 os.link(oldname, newname)
00107                 success = True
00108             except:
00109                 success = False
00110                 raise
00111         finally:
00112             if success or delete_old:
00113                 os.unlink(oldname)
00114 

Definition at line 115 of file filesys.py.

00115 
00116 def touch(name):
00117     if sys.platform == 'win32':
00118         import win32file, win32con, pywintypes
00119 
00120         access = win32file.GENERIC_WRITE
00121         share = (win32file.FILE_SHARE_DELETE |
00122                  win32file.FILE_SHARE_READ |
00123                  win32file.FILE_SHARE_WRITE)
00124         create = win32file.OPEN_EXISTING
00125         mtime = time.gmtime()
00126         handle = win32file.CreateFile(name, access, share, None, create,
00127                                       win32file.FILE_ATTRIBUTE_NORMAL |
00128                                       win32con.FILE_FLAG_BACKUP_SEMANTICS,
00129                                       None)
00130         try:
00131             newTime = pywintypes.Time(mtime)
00132             win32file.SetFileTime(handle, newTime, newTime, newTime)
00133         finally:
00134             win32file.CloseHandle(handle)
00135     else:
00136         os.utime(name, None)
00137 

Definition at line 292 of file filesys.py.

00292 
00293 def unlock(file):
00294     raise NotImplementedError
00295 
00296 
00297 # ----------------------------------------------------------------------
00298 # Get real case of path name on case insensitive file systems
00299 # TODO: nt version?


Variable Documentation

Definition at line 328 of file filesys.py.

Definition at line 287 of file filesys.py.

Definition at line 14 of file filesys.py.

Definition at line 164 of file filesys.py.

Definition at line 80 of file filesys.py.

Definition at line 165 of file filesys.py.

Definition at line 163 of file filesys.py.