import os, os.path, stat
class PathSet(object) :
"""Represents an ordered set of unique normalized file paths.
The paths may be the subject to user (~) and variable ($) expansion.
This class fulfills the sequence contract (i.e. len(), x[], iterating etc.).
"""
def __init__ (self, *args, **kwds) :
"""Construct a set. Any number of either string-like objects or the sequences of
thereof is accepted.
The following keywords are recognized:
userExpand -- (default True)
varExpand -- (default True)
"""
self.paths = []
try :
self.varExpand = kwds["varExpand"]
except KeyError :
self.varExpand = True
try :
self.userExpand = kwds["userExpand"]
except KeyError :
self.userExpand = True
for x in args :
self.append(x)
def __iter__(self) :
return self.paths.__iter__()
def __len__(self) :
return len(self.paths)
def __str__(self) :
paths = self.paths
if len(paths) > 1 :
s = str(paths[0])
for x in paths[1:] :
s += ":" + str(x)
elif len(paths) > 0 :
s = str(paths[0])
else :
s = ""
return s
def __contains__ (self, path) :
return self.adopt(path) in self.paths
def __getitem__(self, index) :
return self.paths[index]
def __add__(self, other) :
x = self.__class__()
x.append(self)
x.append(other)
return x
def __iadd__(self, other) :
self.append(other)
return self
def adopt(self, path) :
"""Validates the specified path and converts it to a form suitable for inclusion
into the set. Must either return the path or raise ValueError if an inappropriate
path was given.
"""
if self.userExpand and path.startswith("~") :
path = os.path.expanduser(path)
if self.varExpand and "$" in path :
path = os.path.expandvars(path)
path = os.path.normpath(path)
if path in self.paths :
raise ValueError
else :
return path
def append(self, arg) :
if isinstance(arg, str) :
try :
self.paths.append(self.adopt(arg))
except ValueError :
pass
else :
for x in arg :
self.append(str(x)) # WARNING : a recursive call, stay alert
class ValidPathSet(PathSet) :
"""Represents the PathSet of the valid paths. The symbolic links are respected
and have the preference over the real directories they point to.
"""
def adopt(self, path) :
path = super(ValidPathSet, self).adopt(path)
try :
st = os.stat(path)
for xst in self.stats :
if os.path.samestat(st, xst) :
if os.path.islink(path) and not os.path.islink(self.stats[xst]) : # FIXME : inefficiency
# In case of real path followed by the symlink to it
# the latter has the preference
self.paths[self.paths.index(self.stats[xst])] = path
del self.stats[xst]; self.stats[st] = path
# In all other cases the path is discarded
raise ValueError # To prevent the appending of the path
self.stats[st] = path
return path
except OSError :
raise ValueError
def __init__(self, *args, **kwds) :
self.stats = {}
super(ValidPathSet, self).__init__(*args, **kwds)
|