import os
import os.path
import 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):
# FIXME : inefficiency
if os.path.islink(path) and not os.path.islink(
self.stats[xst]):
# 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)