| Index: third_party/twisted_8_1/twisted/python/filepath.py
|
| diff --git a/third_party/twisted_8_1/twisted/python/filepath.py b/third_party/twisted_8_1/twisted/python/filepath.py
|
| deleted file mode 100644
|
| index 0116833d57e21fc77ea73467ca1e5300ca9d4eef..0000000000000000000000000000000000000000
|
| --- a/third_party/twisted_8_1/twisted/python/filepath.py
|
| +++ /dev/null
|
| @@ -1,675 +0,0 @@
|
| -# -*- test-case-name: twisted.test.test_paths -*-
|
| -# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
|
| -# See LICENSE for details.
|
| -
|
| -"""
|
| -Object-oriented filesystem path representation.
|
| -"""
|
| -
|
| -import os
|
| -import errno
|
| -import hashlib
|
| -import random
|
| -import base64
|
| -
|
| -from os.path import isabs, exists, normpath, abspath, splitext
|
| -from os.path import basename, dirname
|
| -from os.path import join as joinpath
|
| -from os import sep as slash
|
| -from os import listdir, utime, stat
|
| -
|
| -from stat import S_ISREG, S_ISDIR
|
| -
|
| -# Please keep this as light as possible on other Twisted imports; many, many
|
| -# things import this module, and it would be good if it could easily be
|
| -# modified for inclusion in the standard library. --glyph
|
| -
|
| -from twisted.python.runtime import platform
|
| -
|
| -from twisted.python.win32 import ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND
|
| -from twisted.python.win32 import ERROR_INVALID_NAME, ERROR_DIRECTORY
|
| -from twisted.python.win32 import WindowsError
|
| -
|
| -def _stub_islink(path):
|
| - """
|
| - Always return 'false' if the operating system does not support symlinks.
|
| -
|
| - @param path: a path string.
|
| - @type path: L{str}
|
| - @return: false
|
| - """
|
| - return False
|
| -
|
| -
|
| -def _stub_urandom(n):
|
| - """
|
| - Provide random data in versions of Python prior to 2.4. This is an
|
| - effectively compatible replacement for 'os.urandom'.
|
| -
|
| - @type n: L{int}
|
| - @param n: the number of bytes of data to return
|
| - @return: C{n} bytes of random data.
|
| - @rtype: str
|
| - """
|
| - randomData = [random.randrange(256) for n in xrange(n)]
|
| - return ''.join(map(chr, randomData))
|
| -
|
| -
|
| -def _stub_armor(s):
|
| - """
|
| - ASCII-armor for random data. This uses a hex encoding, although we will
|
| - prefer url-safe base64 encoding for features in this module if it is
|
| - available.
|
| - """
|
| - return s.encode('hex')
|
| -
|
| -islink = getattr(os.path, 'islink', _stub_islink)
|
| -randomBytes = getattr(os, 'urandom', _stub_urandom)
|
| -armor = getattr(base64, 'urlsafe_b64encode', _stub_armor)
|
| -
|
| -class InsecurePath(Exception):
|
| - pass
|
| -
|
| -
|
| -class UnlistableError(OSError):
|
| - """
|
| - An exception which is used to distinguish between errors which mean 'this
|
| - is not a directory you can list' and other, more catastrophic errors.
|
| -
|
| - This error will try to look as much like the original error as possible,
|
| - while still being catchable as an independent type.
|
| -
|
| - @ivar originalException: the actual original exception instance, either an
|
| - L{OSError} or a L{WindowsError}.
|
| - """
|
| - def __init__(self, originalException):
|
| - """
|
| - Create an UnlistableError exception.
|
| -
|
| - @param originalException: an instance of OSError.
|
| - """
|
| - self.__dict__.update(originalException.__dict__)
|
| - self.originalException = originalException
|
| -
|
| -
|
| -
|
| -class _WindowsUnlistableError(UnlistableError, WindowsError):
|
| - """
|
| - This exception is raised on Windows, for compatibility with previous
|
| - releases of FilePath where unportable programs may have done "except
|
| - WindowsError:" around a call to children().
|
| -
|
| - It is private because all application code may portably catch
|
| - L{UnlistableError} instead.
|
| - """
|
| -
|
| -
|
| -
|
| -def _secureEnoughString():
|
| - """
|
| - Create a pseudorandom, 16-character string for use in secure filenames.
|
| - """
|
| - return armor(hashlib.sha1(randomBytes(64)).digest())[:16]
|
| -
|
| -class _PathHelper:
|
| - """
|
| - Abstract helper class also used by ZipPath; implements certain utility methods.
|
| - """
|
| -
|
| - def getContent(self):
|
| - return self.open().read()
|
| -
|
| - def children(self):
|
| - """
|
| - List the chilren of this path object.
|
| -
|
| - @raise OSError: If an error occurs while listing the directory. If the
|
| - error is 'serious', meaning that the operation failed due to an access
|
| - violation, exhaustion of some kind of resource (file descriptors or
|
| - memory), OSError or a platform-specific variant will be raised.
|
| -
|
| - @raise UnlistableError: If the inability to list the directory is due
|
| - to this path not existing or not being a directory, the more specific
|
| - OSError subclass L{UnlistableError} is raised instead.
|
| -
|
| - @return: an iterable of all currently-existing children of this object
|
| - accessible with L{_PathHelper.child}.
|
| - """
|
| - try:
|
| - subnames = self.listdir()
|
| - except WindowsError, winErrObj:
|
| - # WindowsError is an OSError subclass, so if not for this clause
|
| - # the OSError clause below would be handling these. Windows error
|
| - # codes aren't the same as POSIX error codes, so we need to handle
|
| - # them differently.
|
| -
|
| - # Under Python 2.5 on Windows, WindowsError has a winerror
|
| - # attribute and an errno attribute. The winerror attribute is
|
| - # bound to the Windows error code while the errno attribute is
|
| - # bound to a translation of that code to a perhaps equivalent POSIX
|
| - # error number.
|
| -
|
| - # Under Python 2.4 on Windows, WindowsError only has an errno
|
| - # attribute. It is bound to the Windows error code.
|
| -
|
| - # For simplicity of code and to keep the number of paths through
|
| - # this suite minimal, we grab the Windows error code under either
|
| - # version.
|
| -
|
| - # Furthermore, attempting to use os.listdir on a non-existent path
|
| - # in Python 2.4 will result in a Windows error code of
|
| - # ERROR_PATH_NOT_FOUND. However, in Python 2.5,
|
| - # ERROR_FILE_NOT_FOUND results instead. -exarkun
|
| - winerror = getattr(winErrObj, 'winerror', winErrObj.errno)
|
| - if winerror not in (ERROR_PATH_NOT_FOUND,
|
| - ERROR_FILE_NOT_FOUND,
|
| - ERROR_INVALID_NAME,
|
| - ERROR_DIRECTORY):
|
| - raise
|
| - raise _WindowsUnlistableError(winErrObj)
|
| - except OSError, ose:
|
| - if ose.errno not in (errno.ENOENT, errno.ENOTDIR):
|
| - # Other possible errors here, according to linux manpages:
|
| - # EACCES, EMIFLE, ENFILE, ENOMEM. None of these seem like the
|
| - # sort of thing which should be handled normally. -glyph
|
| - raise
|
| - raise UnlistableError(ose)
|
| - return map(self.child, subnames)
|
| -
|
| - def walk(self):
|
| - """
|
| - Yield myself, then each of my children, and each of those children's
|
| - children in turn.
|
| -
|
| - @return: a generator yielding FilePath-like objects.
|
| - """
|
| - yield self
|
| - if self.isdir():
|
| - for c in self.children():
|
| - for subc in c.walk():
|
| - yield subc
|
| -
|
| - def sibling(self, path):
|
| - return self.parent().child(path)
|
| -
|
| - def segmentsFrom(self, ancestor):
|
| - """
|
| - Return a list of segments between a child and its ancestor.
|
| -
|
| - For example, in the case of a path X representing /a/b/c/d and a path Y
|
| - representing /a/b, C{Y.segmentsFrom(X)} will return C{['c',
|
| - 'd']}.
|
| -
|
| - @param ancestor: an instance of the same class as self, ostensibly an
|
| - ancestor of self.
|
| -
|
| - @raise: ValueError if the 'ancestor' parameter is not actually an
|
| - ancestor, i.e. a path for /x/y/z is passed as an ancestor for /a/b/c/d.
|
| -
|
| - @return: a list of strs
|
| - """
|
| - # this might be an unnecessarily inefficient implementation but it will
|
| - # work on win32 and for zipfiles; later I will deterimine if the
|
| - # obvious fast implemenation does the right thing too
|
| - f = self
|
| - p = f.parent()
|
| - segments = []
|
| - while f != ancestor and p != f:
|
| - segments[0:0] = [f.basename()]
|
| - f = p
|
| - p = p.parent()
|
| - if f == ancestor and segments:
|
| - return segments
|
| - raise ValueError("%r not parent of %r" % (ancestor, self))
|
| -
|
| -
|
| - # new in 8.0
|
| - def __hash__(self):
|
| - """
|
| - Hash the same as another FilePath with the same path as mine.
|
| - """
|
| - return hash((self.__class__, self.path))
|
| -
|
| -
|
| - # pending deprecation in 8.0
|
| - def getmtime(self):
|
| - """
|
| - Deprecated. Use getModificationTime instead.
|
| - """
|
| - return int(self.getModificationTime())
|
| -
|
| -
|
| - def getatime(self):
|
| - """
|
| - Deprecated. Use getAccessTime instead.
|
| - """
|
| - return int(self.getAccessTime())
|
| -
|
| -
|
| - def getctime(self):
|
| - """
|
| - Deprecated. Use getStatusChangeTime instead.
|
| - """
|
| - return int(self.getStatusChangeTime())
|
| -
|
| -
|
| -
|
| -class FilePath(_PathHelper):
|
| - """
|
| - I am a path on the filesystem that only permits 'downwards' access.
|
| -
|
| - Instantiate me with a pathname (for example,
|
| - FilePath('/home/myuser/public_html')) and I will attempt to only provide
|
| - access to files which reside inside that path. I may be a path to a file,
|
| - a directory, or a file which does not exist.
|
| -
|
| - The correct way to use me is to instantiate me, and then do ALL filesystem
|
| - access through me. In other words, do not import the 'os' module; if you
|
| - need to open a file, call my 'open' method. If you need to list a
|
| - directory, call my 'path' method.
|
| -
|
| - Even if you pass me a relative path, I will convert that to an absolute
|
| - path internally.
|
| -
|
| - Note: although time-related methods do return floating-point results, they
|
| - may still be only second resolution depending on the platform and the last
|
| - value passed to L{os.stat_float_times}. If you want greater-than-second
|
| - precision, call C{os.stat_float_times(True)}, or use Python 2.5.
|
| - Greater-than-second precision is only available in Windows on Python2.5 and
|
| - later.
|
| -
|
| - @type alwaysCreate: C{bool}
|
| - @ivar alwaysCreate: When opening this file, only succeed if the file does not
|
| - already exist.
|
| - """
|
| -
|
| - statinfo = None
|
| - path = None
|
| -
|
| - def __init__(self, path, alwaysCreate=False):
|
| - self.path = abspath(path)
|
| - self.alwaysCreate = alwaysCreate
|
| -
|
| - def __getstate__(self):
|
| - d = self.__dict__.copy()
|
| - if d.has_key('statinfo'):
|
| - del d['statinfo']
|
| - return d
|
| -
|
| - def child(self, path):
|
| - if platform.isWindows() and path.count(":"):
|
| - # Catch paths like C:blah that don't have a slash
|
| - raise InsecurePath("%r contains a colon." % (path,))
|
| - norm = normpath(path)
|
| - if slash in norm:
|
| - raise InsecurePath("%r contains one or more directory separators" % (path,))
|
| - newpath = abspath(joinpath(self.path, norm))
|
| - if not newpath.startswith(self.path):
|
| - raise InsecurePath("%r is not a child of %s" % (newpath, self.path))
|
| - return self.clonePath(newpath)
|
| -
|
| - def preauthChild(self, path):
|
| - """
|
| - Use me if `path' might have slashes in it, but you know they're safe.
|
| -
|
| - (NOT slashes at the beginning. It still needs to be a _child_).
|
| - """
|
| - newpath = abspath(joinpath(self.path, normpath(path)))
|
| - if not newpath.startswith(self.path):
|
| - raise InsecurePath("%s is not a child of %s" % (newpath, self.path))
|
| - return self.clonePath(newpath)
|
| -
|
| - def childSearchPreauth(self, *paths):
|
| - """Return my first existing child with a name in 'paths'.
|
| -
|
| - paths is expected to be a list of *pre-secured* path fragments; in most
|
| - cases this will be specified by a system administrator and not an
|
| - arbitrary user.
|
| -
|
| - If no appropriately-named children exist, this will return None.
|
| - """
|
| - p = self.path
|
| - for child in paths:
|
| - jp = joinpath(p, child)
|
| - if exists(jp):
|
| - return self.clonePath(jp)
|
| -
|
| - def siblingExtensionSearch(self, *exts):
|
| - """Attempt to return a path with my name, given multiple possible
|
| - extensions.
|
| -
|
| - Each extension in exts will be tested and the first path which exists
|
| - will be returned. If no path exists, None will be returned. If '' is
|
| - in exts, then if the file referred to by this path exists, 'self' will
|
| - be returned.
|
| -
|
| - The extension '*' has a magic meaning, which means "any path that
|
| - begins with self.path+'.' is acceptable".
|
| - """
|
| - p = self.path
|
| - for ext in exts:
|
| - if not ext and self.exists():
|
| - return self
|
| - if ext == '*':
|
| - basedot = basename(p)+'.'
|
| - for fn in listdir(dirname(p)):
|
| - if fn.startswith(basedot):
|
| - return self.clonePath(joinpath(dirname(p), fn))
|
| - p2 = p + ext
|
| - if exists(p2):
|
| - return self.clonePath(p2)
|
| -
|
| - def siblingExtension(self, ext):
|
| - return self.clonePath(self.path+ext)
|
| -
|
| -
|
| - def linkTo(self, linkFilePath):
|
| - """
|
| - Creates a symlink to self to at the path in the L{FilePath}
|
| - C{linkFilePath}. Only works on posix systems due to its dependence on
|
| - C{os.symlink}. Propagates C{OSError}s up from C{os.symlink} if
|
| - C{linkFilePath.parent()} does not exist, or C{linkFilePath} already
|
| - exists.
|
| -
|
| - @param linkFilePath: a FilePath representing the link to be created
|
| - @type linkFilePath: L{FilePath}
|
| - """
|
| - os.symlink(self.path, linkFilePath.path)
|
| -
|
| -
|
| - def open(self, mode='r'):
|
| - if self.alwaysCreate:
|
| - assert 'a' not in mode, "Appending not supported when alwaysCreate == True"
|
| - return self.create()
|
| - return open(self.path, mode+'b')
|
| -
|
| - # stat methods below
|
| -
|
| - def restat(self, reraise=True):
|
| - """
|
| - Re-calculate cached effects of 'stat'. To refresh information on this path
|
| - after you know the filesystem may have changed, call this method.
|
| -
|
| - @param reraise: a boolean. If true, re-raise exceptions from
|
| - L{os.stat}; otherwise, mark this path as not existing, and remove any
|
| - cached stat information.
|
| - """
|
| - try:
|
| - self.statinfo = stat(self.path)
|
| - except OSError:
|
| - self.statinfo = 0
|
| - if reraise:
|
| - raise
|
| -
|
| -
|
| - def chmod(self, mode):
|
| - """
|
| - Changes the permissions on self, if possible. Propagates errors from
|
| - C{os.chmod} up.
|
| -
|
| - @param mode: integer representing the new permissions desired (same as
|
| - the command line chmod)
|
| - @type mode: C{int}
|
| - """
|
| - os.chmod(self.path, mode)
|
| -
|
| -
|
| - def getsize(self):
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat()
|
| - st = self.statinfo
|
| - return st.st_size
|
| -
|
| -
|
| - def getModificationTime(self):
|
| - """
|
| - Retrieve the time of last access from this file.
|
| -
|
| - @return: a number of seconds from the epoch.
|
| - @rtype: float
|
| - """
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat()
|
| - st = self.statinfo
|
| - return float(st.st_mtime)
|
| -
|
| -
|
| - def getStatusChangeTime(self):
|
| - """
|
| - Retrieve the time of the last status change for this file.
|
| -
|
| - @return: a number of seconds from the epoch.
|
| - @rtype: float
|
| - """
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat()
|
| - st = self.statinfo
|
| - return float(st.st_ctime)
|
| -
|
| -
|
| - def getAccessTime(self):
|
| - """
|
| - Retrieve the time that this file was last accessed.
|
| -
|
| - @return: a number of seconds from the epoch.
|
| - @rtype: float
|
| - """
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat()
|
| - st = self.statinfo
|
| - return float(st.st_atime)
|
| -
|
| -
|
| - def exists(self):
|
| - """
|
| - Check if the C{path} exists.
|
| -
|
| - @return: C{True} if the stats of C{path} can be retrieved successfully,
|
| - C{False} in the other cases.
|
| - @rtype: C{bool}
|
| - """
|
| - if self.statinfo:
|
| - return True
|
| - else:
|
| - self.restat(False)
|
| - if self.statinfo:
|
| - return True
|
| - else:
|
| - return False
|
| -
|
| -
|
| - def isdir(self):
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat(False)
|
| - st = self.statinfo
|
| - if not st:
|
| - return False
|
| - return S_ISDIR(st.st_mode)
|
| -
|
| - def isfile(self):
|
| - st = self.statinfo
|
| - if not st:
|
| - self.restat(False)
|
| - st = self.statinfo
|
| - if not st:
|
| - return False
|
| - return S_ISREG(st.st_mode)
|
| -
|
| - def islink(self):
|
| - # We can't use cached stat results here, because that is the stat of
|
| - # the destination - (see #1773) which in *every case* but this one is
|
| - # the right thing to use. We could call lstat here and use that, but
|
| - # it seems unlikely we'd actually save any work that way. -glyph
|
| - return islink(self.path)
|
| -
|
| - def isabs(self):
|
| - return isabs(self.path)
|
| -
|
| - def listdir(self):
|
| - return listdir(self.path)
|
| -
|
| - def splitext(self):
|
| - return splitext(self.path)
|
| -
|
| - def __repr__(self):
|
| - return 'FilePath(%r)' % (self.path,)
|
| -
|
| - def touch(self):
|
| - try:
|
| - self.open('a').close()
|
| - except IOError:
|
| - pass
|
| - utime(self.path, None)
|
| -
|
| - def remove(self):
|
| - """
|
| - Removes the file or directory that is represented by self. If
|
| - C{self.path} is a directory, recursively remove all its children
|
| - before removing the directory. If it's a file or link, just delete
|
| - it.
|
| - """
|
| - if self.isdir() and not self.islink():
|
| - for child in self.children():
|
| - child.remove()
|
| - os.rmdir(self.path)
|
| - else:
|
| - os.remove(self.path)
|
| - self.restat(False)
|
| -
|
| -
|
| - def makedirs(self):
|
| - """
|
| - Create all directories not yet existing in C{path} segments, using
|
| - C{os.makedirs}.
|
| - """
|
| - return os.makedirs(self.path)
|
| -
|
| -
|
| - def globChildren(self, pattern):
|
| - """
|
| - Assuming I am representing a directory, return a list of
|
| - FilePaths representing my children that match the given
|
| - pattern.
|
| - """
|
| - import glob
|
| - path = self.path[-1] == '/' and self.path + pattern or slash.join([self.path, pattern])
|
| - return map(self.clonePath, glob.glob(path))
|
| -
|
| - def basename(self):
|
| - return basename(self.path)
|
| -
|
| - def dirname(self):
|
| - return dirname(self.path)
|
| -
|
| - def parent(self):
|
| - return self.clonePath(self.dirname())
|
| -
|
| - def setContent(self, content, ext='.new'):
|
| - sib = self.siblingExtension(ext)
|
| - sib.open('w').write(content)
|
| - if platform.isWindows() and exists(self.path):
|
| - os.unlink(self.path)
|
| - os.rename(sib.path, self.path)
|
| -
|
| - # new in 2.2.0
|
| -
|
| - def __cmp__(self, other):
|
| - if not isinstance(other, FilePath):
|
| - return NotImplemented
|
| - return cmp(self.path, other.path)
|
| -
|
| - def createDirectory(self):
|
| - os.mkdir(self.path)
|
| -
|
| - def requireCreate(self, val=1):
|
| - self.alwaysCreate = val
|
| -
|
| - def create(self):
|
| - """Exclusively create a file, only if this file previously did not exist.
|
| - """
|
| - fdint = os.open(self.path, (os.O_EXCL |
|
| - os.O_CREAT |
|
| - os.O_RDWR))
|
| -
|
| - # XXX TODO: 'name' attribute of returned files is not mutable or
|
| - # settable via fdopen, so this file is slighly less functional than the
|
| - # one returned from 'open' by default. send a patch to Python...
|
| -
|
| - return os.fdopen(fdint, 'w+b')
|
| -
|
| - def temporarySibling(self):
|
| - """
|
| - Create a path naming a temporary sibling of this path in a secure fashion.
|
| - """
|
| - sib = self.sibling(_secureEnoughString() + self.basename())
|
| - sib.requireCreate()
|
| - return sib
|
| -
|
| - _chunkSize = 2 ** 2 ** 2 ** 2
|
| -
|
| - def copyTo(self, destination):
|
| - # XXX TODO: *thorough* audit and documentation of the exact desired
|
| - # semantics of this code. Right now the behavior of existent
|
| - # destination symlinks is convenient, and quite possibly correct, but
|
| - # its security properties need to be explained.
|
| - if self.isdir():
|
| - if not destination.exists():
|
| - destination.createDirectory()
|
| - for child in self.children():
|
| - destChild = destination.child(child.basename())
|
| - child.copyTo(destChild)
|
| - elif self.isfile():
|
| - writefile = destination.open('w')
|
| - readfile = self.open()
|
| - while 1:
|
| - # XXX TODO: optionally use os.open, os.read and O_DIRECT and
|
| - # use os.fstatvfs to determine chunk sizes and make
|
| - # *****sure**** copy is page-atomic; the following is good
|
| - # enough for 99.9% of everybody and won't take a week to audit
|
| - # though.
|
| - chunk = readfile.read(self._chunkSize)
|
| - writefile.write(chunk)
|
| - if len(chunk) < self._chunkSize:
|
| - break
|
| - writefile.close()
|
| - readfile.close()
|
| - else:
|
| - # If you see the following message because you want to copy
|
| - # symlinks, fifos, block devices, character devices, or unix
|
| - # sockets, please feel free to add support to do sensible things in
|
| - # reaction to those types!
|
| - raise NotImplementedError(
|
| - "Only copying of files and directories supported")
|
| -
|
| - def moveTo(self, destination):
|
| - try:
|
| - os.rename(self.path, destination.path)
|
| - self.restat(False)
|
| - except OSError, ose:
|
| - if ose.errno == errno.EXDEV:
|
| - # man 2 rename, ubuntu linux 5.10 "breezy":
|
| -
|
| - # oldpath and newpath are not on the same mounted filesystem.
|
| - # (Linux permits a filesystem to be mounted at multiple
|
| - # points, but rename(2) does not work across different mount
|
| - # points, even if the same filesystem is mounted on both.)
|
| -
|
| - # that means it's time to copy trees of directories!
|
| - secsib = destination.temporarySibling()
|
| - self.copyTo(secsib) # slow
|
| - secsib.moveTo(destination) # visible
|
| -
|
| - # done creating new stuff. let's clean me up.
|
| - mysecsib = self.temporarySibling()
|
| - self.moveTo(mysecsib) # visible
|
| - mysecsib.remove() # slow
|
| - else:
|
| - raise
|
| -
|
| -
|
| -FilePath.clonePath = FilePath
|
|
|