Created
June 30, 2016 22:03
-
-
Save theodox/e9cc13e2b112965679125872777a5276 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| class UPathError(ValueError): | |
| pass | |
| class PathIsNotRelative(UPathError): | |
| pass | |
| class PathIsNotAbsolute(UPathError): | |
| pass | |
| class UPath(unicode): | |
| ''' | |
| Immutable path object with enforced right slash pathing | |
| On windows, equality tests done case-insenstively | |
| ''' | |
| def __new__(cls, *args, **kwargs): | |
| """ | |
| override __new__ to so that we can pass multiple arguments in derived classes | |
| """ | |
| return super(UPath, cls).__new__(cls, UPath.as_path(args[0])) | |
| def __init__(self, dta): | |
| if not dta: | |
| raise UPathError("No path provided") | |
| self._low = self.lower() | |
| if os.name == 'nt': | |
| self._hsh = hash(self._low) | |
| else: | |
| self._hsh = hash(self) | |
| @classmethod | |
| def as_path(cls, data): | |
| if hasattr(data, '_uni'): | |
| return data._uni | |
| unicoded = data.encode('utf-8') | |
| result = os.path.normpath(data) | |
| result = os.path.expandvars(result) | |
| result = result.replace('\\', '/') | |
| # preserve double right slash for perforce and UNC paths | |
| is_depot_path = result.startswith("//") | |
| while "//" in result: | |
| result = result.replace('//', '/') | |
| if is_depot_path: result = "/" + result | |
| return result | |
| def is_abs(self): | |
| if self[0] == '/': return True | |
| if ":" in self[:2]: return True | |
| return False | |
| def down(self): | |
| pieces = self.split('/') | |
| pieces.reverse() | |
| result = pieces.pop() | |
| while pieces: | |
| yield UPath(result) | |
| result += "/" | |
| result += pieces.pop() | |
| yield self | |
| def up(self): | |
| pieces = self.split('/') | |
| while pieces: | |
| yield UPath("/".join(pieces)) | |
| pieces.pop() | |
| def __hash__(self): | |
| return self._hsh | |
| def __eq__(self, other): | |
| if not other: | |
| return False | |
| if hasattr(other, '_hsh'): | |
| return self._hsh == other._hsh | |
| return self._low == self.as_path(other).lower() | |
| def __add__(self, other): | |
| if other.startswith("."): | |
| return UPath(unicode(self) + other) | |
| return UPath(unicode(self) + "/" + other) | |
| def __radd__(self, other): | |
| return UPath(other + "/" + unicode(self)) | |
| def __div__(self, other): | |
| return self.__add__(other) | |
| def __rdiv__(self, other): | |
| return self.__radd__(other) | |
| def __contains__(self, item): | |
| if os.name != 'nt': | |
| cmp = item._uni if hasattr(item, '_uni') else item | |
| return self.__contains__(cmp) | |
| else: | |
| cmp = item._low if hasattr(item, '_low') else str(item).lower() | |
| return self._low.__contains__(cmp) | |
| def __getattr__(self, item): | |
| if item in self.__dict__: | |
| return self.__dict__[item] | |
| return self.__getattribute__(item) | |
| def contains(self, path): | |
| other = UPath(path) | |
| return other._low.startswith(self._low) | |
| def replace(self, old, new): | |
| repl = unicode(self).replace(old, new) | |
| return UPath(repl) | |
| class RelPath(UPath): | |
| """ | |
| A UPath which is guaranteed to be relative | |
| """ | |
| def __init__(self, pth): | |
| super(RelPath, self).__init__(pth) | |
| if self.is_abs(): | |
| raise PathIsNotRelative("%s is not a relative path" % self) | |
| class AbsPath(UPath): | |
| """ | |
| A UPath guaranteed to be absolute | |
| """ | |
| def __init__(self, pth): | |
| super(AbsPath, self).__init__(pth) | |
| if not self.is_abs(): | |
| raise PathIsNotAbsolute("%s is not an absolute path" % self) | |
| class PathURI(UPath): | |
| """ | |
| A compound path with two parts: an absolute root and a relative path | |
| A PathURI equates with other Upaths as if it were its own fullpath, | |
| thus: | |
| pu = PathURI('c:/', "test') | |
| path = UPath("c:/test") | |
| pu == path | |
| # True | |
| """ | |
| def __new__(cls, *args, **kwargs): | |
| """ | |
| override __new__ to so that we can pass multiple arguments in derived classes | |
| """ | |
| if args[0] is None: | |
| raise UPathError("ProjectURI needs a root directory") | |
| fp = unicode(args[0]) + "/" + unicode(args[1]) | |
| return super(UPath, cls).__new__(cls, UPath.as_path(fp)) | |
| def __init__(self, root, path): | |
| if path is None: | |
| raise UPathError("must supply a relative path") | |
| self.path = RelPath(path) | |
| self._root = AbsPath(root) | |
| super(ProjectURI, self).__init__(self.full_path) | |
| @property | |
| def full_path(self): | |
| return AbsPath(self.root / self.path) | |
| @property | |
| def root(self): | |
| return self._root | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment