333 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
module with a base subversion path object.
 | 
						|
"""
 | 
						|
import os, sys, time, re, string
 | 
						|
import py
 | 
						|
from py.__.path import common
 | 
						|
 | 
						|
ALLOWED_CHARS = "_ -/\\=$.~+" #add characters as necessary when tested
 | 
						|
if sys.platform == "win32":
 | 
						|
    ALLOWED_CHARS += ":"
 | 
						|
ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:'
 | 
						|
    
 | 
						|
def _getsvnversion(ver=[]):
 | 
						|
    try:
 | 
						|
        return ver[0]
 | 
						|
    except IndexError:
 | 
						|
        v = py.process.cmdexec("svn -q --version")
 | 
						|
        v.strip()
 | 
						|
        v = '.'.join(v.split('.')[:2])
 | 
						|
        ver.append(v)
 | 
						|
        return v
 | 
						|
 | 
						|
def _escape_helper(text):
 | 
						|
    text = str(text)
 | 
						|
    if py.std.sys.platform != 'win32':
 | 
						|
        text = str(text).replace('$', '\\$')
 | 
						|
    return text
 | 
						|
 | 
						|
def _check_for_bad_chars(text, allowed_chars=ALLOWED_CHARS):
 | 
						|
    for c in str(text):
 | 
						|
        if c.isalnum():
 | 
						|
            continue
 | 
						|
        if c in allowed_chars:
 | 
						|
            continue
 | 
						|
        return True
 | 
						|
    return False
 | 
						|
 | 
						|
#_______________________________________________________________
 | 
						|
 | 
						|
class SvnPathBase(common.FSPathBase):
 | 
						|
    """ Base implementation for SvnPath implementations. """
 | 
						|
    sep = '/'
 | 
						|
 | 
						|
    def _geturl(self):
 | 
						|
        return self.strpath
 | 
						|
    url = property(_geturl, None, None, "url of this svn-path.")
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        """ return a string representation (including rev-number) """
 | 
						|
        return self.strpath
 | 
						|
 | 
						|
    def __hash__(self):
 | 
						|
        return hash(self.strpath)
 | 
						|
 | 
						|
    def new(self, **kw):
 | 
						|
        """ create a modified version of this path. A 'rev' argument
 | 
						|
            indicates a new revision.
 | 
						|
            the following keyword arguments modify various path parts:
 | 
						|
 | 
						|
              http://host.com/repo/path/file.ext
 | 
						|
              |-----------------------|          dirname
 | 
						|
                                        |------| basename
 | 
						|
                                        |--|     purebasename
 | 
						|
                                            |--| ext
 | 
						|
        """
 | 
						|
        obj = object.__new__(self.__class__)
 | 
						|
        obj.rev = kw.get('rev', self.rev)
 | 
						|
        dirname, basename, purebasename, ext = self._getbyspec(
 | 
						|
             "dirname,basename,purebasename,ext")
 | 
						|
        if 'basename' in kw:
 | 
						|
            if 'purebasename' in kw or 'ext' in kw:
 | 
						|
                raise ValueError("invalid specification %r" % kw)
 | 
						|
        else:
 | 
						|
            pb = kw.setdefault('purebasename', purebasename)
 | 
						|
            ext = kw.setdefault('ext', ext)
 | 
						|
            if ext and not ext.startswith('.'):
 | 
						|
                ext = '.' + ext
 | 
						|
            kw['basename'] = pb + ext
 | 
						|
 | 
						|
        kw.setdefault('dirname', dirname)
 | 
						|
        kw.setdefault('sep', self.sep)
 | 
						|
        if kw['basename']:
 | 
						|
            obj.strpath = "%(dirname)s%(sep)s%(basename)s" % kw
 | 
						|
        else:
 | 
						|
            obj.strpath = "%(dirname)s" % kw
 | 
						|
        return obj
 | 
						|
 | 
						|
    def _getbyspec(self, spec):
 | 
						|
        """ get specified parts of the path.  'arg' is a string
 | 
						|
            with comma separated path parts. The parts are returned
 | 
						|
            in exactly the order of the specification.
 | 
						|
 | 
						|
            you may specify the following parts:
 | 
						|
 | 
						|
            http://host.com/repo/path/file.ext
 | 
						|
            |-----------------------|          dirname
 | 
						|
                                      |------| basename
 | 
						|
                                      |--|     purebasename
 | 
						|
                                          |--| ext
 | 
						|
        """
 | 
						|
        res = []
 | 
						|
        parts = self.strpath.split(self.sep)
 | 
						|
        for name in spec.split(','):
 | 
						|
            name = name.strip()
 | 
						|
            if name == 'dirname':
 | 
						|
                res.append(self.sep.join(parts[:-1]))
 | 
						|
            elif name == 'basename':
 | 
						|
                res.append(parts[-1])
 | 
						|
            else:
 | 
						|
                basename = parts[-1]
 | 
						|
                i = basename.rfind('.')
 | 
						|
                if i == -1:
 | 
						|
                    purebasename, ext = basename, ''
 | 
						|
                else:
 | 
						|
                    purebasename, ext = basename[:i], basename[i:]
 | 
						|
                if name == 'purebasename':
 | 
						|
                    res.append(purebasename)
 | 
						|
                elif name == 'ext':
 | 
						|
                    res.append(ext)
 | 
						|
                else:
 | 
						|
                    raise NameError, "Don't know part %r" % name
 | 
						|
        return res
 | 
						|
 | 
						|
    def __eq__(self, other):
 | 
						|
        """ return true if path and rev attributes each match """
 | 
						|
        return (str(self) == str(other) and
 | 
						|
               (self.rev == other.rev or self.rev == other.rev))
 | 
						|
 | 
						|
    def __ne__(self, other):
 | 
						|
        return not self == other
 | 
						|
 | 
						|
    def join(self, *args):
 | 
						|
        """ return a new Path (with the same revision) which is composed
 | 
						|
            of the self Path followed by 'args' path components.
 | 
						|
        """
 | 
						|
        if not args:
 | 
						|
            return self
 | 
						|
 | 
						|
        args = tuple([arg.strip(self.sep) for arg in args])
 | 
						|
        parts = (self.strpath, ) + args
 | 
						|
        newpath = self.__class__(self.sep.join(parts), self.rev)
 | 
						|
        return newpath
 | 
						|
 | 
						|
    def propget(self, name):
 | 
						|
        """ return the content of the given property. """
 | 
						|
        value = self._propget(name)
 | 
						|
        return value
 | 
						|
 | 
						|
    def proplist(self):
 | 
						|
        """ list all property names. """
 | 
						|
        content = self._proplist()
 | 
						|
        return content
 | 
						|
 | 
						|
    def listdir(self, fil=None, sort=None):
 | 
						|
        """ list directory contents, possibly filter by the given fil func
 | 
						|
            and possibly sorted.
 | 
						|
        """
 | 
						|
        if isinstance(fil, str):
 | 
						|
            fil = common.fnmatch(fil)
 | 
						|
        nameinfo_seq = self._listdir_nameinfo()
 | 
						|
        if len(nameinfo_seq) == 1:
 | 
						|
            name, info = nameinfo_seq[0]
 | 
						|
            if name == self.basename and info.kind == 'file':
 | 
						|
                #if not self.check(dir=1):
 | 
						|
                raise py.error.ENOTDIR(self)
 | 
						|
        paths = self._make_path_tuple(nameinfo_seq)
 | 
						|
 | 
						|
        if fil or sort:
 | 
						|
            paths = filter(fil, paths)
 | 
						|
            paths = isinstance(paths, list) and paths or list(paths)
 | 
						|
            if callable(sort):
 | 
						|
                paths.sort(sort)
 | 
						|
            elif sort:
 | 
						|
                paths.sort()
 | 
						|
        return paths
 | 
						|
 | 
						|
    def info(self):
 | 
						|
        """ return an Info structure with svn-provided information. """
 | 
						|
        parent = self.dirpath()
 | 
						|
        nameinfo_seq = parent._listdir_nameinfo()
 | 
						|
        bn = self.basename
 | 
						|
        for name, info in nameinfo_seq:
 | 
						|
            if name == bn:
 | 
						|
                return info
 | 
						|
        raise py.error.ENOENT(self)
 | 
						|
 | 
						|
    def size(self):
 | 
						|
        """ Return the size of the file content of the Path. """
 | 
						|
        return self.info().size
 | 
						|
 | 
						|
    def mtime(self):
 | 
						|
        """ Return the last modification time of the file. """
 | 
						|
        return self.info().mtime
 | 
						|
 | 
						|
    # shared help methods
 | 
						|
 | 
						|
    def _escape(self, cmd):
 | 
						|
        return _escape_helper(cmd)
 | 
						|
 | 
						|
    def _make_path_tuple(self, nameinfo_seq):
 | 
						|
        """ return a tuple of paths from a nameinfo-tuple sequence.
 | 
						|
        """
 | 
						|
        #assert self.rev is not None, "revision of %s should not be None here" % self
 | 
						|
        res = []
 | 
						|
        for name, info in nameinfo_seq:
 | 
						|
            child = self.join(name)
 | 
						|
            res.append(child)
 | 
						|
        return tuple(res)
 | 
						|
 | 
						|
 | 
						|
    def _childmaxrev(self):
 | 
						|
        """ return maximum revision number of childs (or self.rev if no childs) """
 | 
						|
        rev = self.rev
 | 
						|
        for name, info in self._listdir_nameinfo():
 | 
						|
            rev = max(rev, info.created_rev)
 | 
						|
        return rev
 | 
						|
 | 
						|
    #def _getlatestrevision(self):
 | 
						|
    #    """ return latest repo-revision for this path. """
 | 
						|
    #    url = self.strpath
 | 
						|
    #    path = self.__class__(url, None)
 | 
						|
    #
 | 
						|
    #    # we need a long walk to find the root-repo and revision
 | 
						|
    #    while 1:
 | 
						|
    #        try:
 | 
						|
    #            rev = max(rev, path._childmaxrev())
 | 
						|
    #            previous = path
 | 
						|
    #            path = path.dirpath()
 | 
						|
    #        except (IOError, process.cmdexec.Error):
 | 
						|
    #            break
 | 
						|
    #    if rev is None:
 | 
						|
    #        raise IOError, "could not determine newest repo revision for %s" % self
 | 
						|
    #    return rev
 | 
						|
 | 
						|
    class Checkers(common.FSCheckers):
 | 
						|
        def dir(self):
 | 
						|
            try:
 | 
						|
                return self.path.info().kind == 'dir'
 | 
						|
            except py.error.Error:
 | 
						|
                return self._listdirworks()
 | 
						|
 | 
						|
        def _listdirworks(self):
 | 
						|
            try:
 | 
						|
                self.path.listdir()
 | 
						|
            except py.error.ENOENT:
 | 
						|
                return False
 | 
						|
            else:
 | 
						|
                return True
 | 
						|
 | 
						|
        def file(self):
 | 
						|
            try:
 | 
						|
                return self.path.info().kind == 'file'
 | 
						|
            except py.error.ENOENT:
 | 
						|
                return False
 | 
						|
 | 
						|
        def exists(self):
 | 
						|
            try:
 | 
						|
                return self.path.info()
 | 
						|
            except py.error.ENOENT:
 | 
						|
                return self._listdirworks()
 | 
						|
 | 
						|
def parse_apr_time(timestr):
 | 
						|
    i = timestr.rfind('.')
 | 
						|
    if i == -1:
 | 
						|
        raise ValueError, "could not parse %s" % timestr
 | 
						|
    timestr = timestr[:i]
 | 
						|
    parsedtime = time.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
 | 
						|
    return time.mktime(parsedtime)
 | 
						|
 | 
						|
class PropListDict(dict):
 | 
						|
    """ a Dictionary which fetches values (InfoSvnCommand instances) lazily"""
 | 
						|
    def __init__(self, path, keynames):
 | 
						|
        dict.__init__(self, [(x, None) for x in keynames])
 | 
						|
        self.path = path
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
        value = dict.__getitem__(self, key)
 | 
						|
        if value is None:
 | 
						|
            value = self.path.propget(key)
 | 
						|
            dict.__setitem__(self, key, value)
 | 
						|
        return value
 | 
						|
 | 
						|
def fixlocale():
 | 
						|
    if sys.platform != 'win32':
 | 
						|
        return 'LC_ALL=C '
 | 
						|
    return ''
 | 
						|
 | 
						|
# some nasty chunk of code to solve path and url conversion and quoting issues
 | 
						|
ILLEGAL_CHARS = '* | \ / : < > ? \t \n \x0b \x0c \r'.split(' ')
 | 
						|
if os.sep in ILLEGAL_CHARS:
 | 
						|
    ILLEGAL_CHARS.remove(os.sep)
 | 
						|
ISWINDOWS = sys.platform == 'win32'
 | 
						|
_reg_allow_disk = re.compile(r'^([a-z]\:\\)?[^:]+$', re.I)
 | 
						|
def _check_path(path):
 | 
						|
    illegal = ILLEGAL_CHARS[:]
 | 
						|
    sp = path.strpath
 | 
						|
    if ISWINDOWS:
 | 
						|
        illegal.remove(':')
 | 
						|
        if not _reg_allow_disk.match(sp):
 | 
						|
            raise ValueError('path may not contain a colon (:)')
 | 
						|
    for char in sp:
 | 
						|
        if char not in string.printable or char in illegal:
 | 
						|
            raise ValueError('illegal character %r in path' % (char,))
 | 
						|
 | 
						|
def path_to_fspath(path, addat=True):
 | 
						|
    _check_path(path)
 | 
						|
    sp = path.strpath
 | 
						|
    if addat and path.rev != -1:
 | 
						|
        sp = '%s@%s' % (sp, path.rev)
 | 
						|
    elif addat:
 | 
						|
        sp = '%s@HEAD' % (sp,)
 | 
						|
    return sp
 | 
						|
    
 | 
						|
def url_from_path(path):
 | 
						|
    fspath = path_to_fspath(path, False)
 | 
						|
    quote = py.std.urllib.quote
 | 
						|
    if ISWINDOWS:
 | 
						|
        match = _reg_allow_disk.match(fspath)
 | 
						|
        fspath = fspath.replace('\\', '/')
 | 
						|
        if match.group(1):
 | 
						|
            fspath = '/%s%s' % (match.group(1).replace('\\', '/'),
 | 
						|
                                quote(fspath[len(match.group(1)):]))
 | 
						|
        else:
 | 
						|
            fspath = quote(fspath)
 | 
						|
    else:
 | 
						|
        fspath = quote(fspath)
 | 
						|
    if path.rev != -1:
 | 
						|
        fspath = '%s@%s' % (fspath, path.rev)
 | 
						|
    else:
 | 
						|
        fspath = '%s@HEAD' % (fspath,)
 | 
						|
    return 'file://%s' % (fspath,)
 | 
						|
 |