Skip to content

Instantly share code, notes, and snippets.

@theodox
Created June 30, 2016 22:03
Show Gist options
  • Select an option

  • Save theodox/e9cc13e2b112965679125872777a5276 to your computer and use it in GitHub Desktop.

Select an option

Save theodox/e9cc13e2b112965679125872777a5276 to your computer and use it in GitHub Desktop.
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