class AbsPath: _components = None def __init__(self, path = None, components = None): if (path is None) == (components is None): raise ValueError('Exactly one of path or components must be provided') if path is not None: if isinstance(path, str): path = path.encode('utf-8') else: path = bytes(path) if len(path) < 1 or not path.startswith(b'/'): raise ValueError('Path does not look like valid absolute path', path) components = filter(lambda x: len(x) > 0 and x != b'.', path[1:].split(b'/')) self._components = tuple(components) if b'..' in self.components: raise ValueError('Parent references are not allowed: %s' % self) def __bytes__(self): return self.path def __repr__(self): return self.path.decode('utf-8', errors = 'backslashreplace') def __contains__(self, item): c = self.components ci = item.components if len(ci) >= len(c) and ci[:len(c)] == c: return True return False def __eq__(self, other): return self.components == other.components def __len__(self): return len(self.components) def __add__(self, other): if isinstance(other, str): other = other.encode('utf-8') if isinstance(other, bytes): other = self.__class__(b'/' + other) return self.__class__(components = self.components + other.components) @property def path(self): return b'/' + b'/'.join(self.components) @property def components(self): """ A tuple of path components. Note that root does not count as a component, so for a path of b'/' this is an empty tuple. """ return self._components def reparent(self, src, dst): if not self in src: raise ValueError('Path not in parent', self, src) tail = self.components[len(src):] return AbsPath(components = dst.components + tail) ROOT = AbsPath('/')