diff options
-rw-r--r-- | fs/__init__.py | 16 | ||||
-rw-r--r-- | fs/expose/fuse/__init__.py | 2 | ||||
-rw-r--r-- | fs/osfs.py | 2 | ||||
-rw-r--r-- | fs/tests/test_xattr.py | 16 | ||||
-rw-r--r-- | fs/wrapfs.py (renamed from fs/wrappers/__init__.py) | 109 | ||||
-rw-r--r-- | fs/wrappers/hidedotfiles.py | 78 | ||||
-rw-r--r-- | fs/xattrs.py (renamed from fs/wrappers/xattr.py) | 31 |
7 files changed, 147 insertions, 107 deletions
diff --git a/fs/__init__.py b/fs/__init__.py index cd8a13b..3872617 100644 --- a/fs/__init__.py +++ b/fs/__init__.py @@ -1,12 +1,24 @@ """ -A filesystem abstraction. + + fs: a filesystem abstraction. + +This module provides an abstract base class 'FS' that defines a consistent +interface to different kinds of filesystem, along with a range of concrete +implementations of this interface such as: + + OSFS: access the local filesystem, through the 'os' module + TempFS: a temporary filesystem that's automatically cleared on exit + MemoryFS: a filesystem that exists only in memory + ZipFS: access a zipfile like a filesystem + S3FS: access files stored in Amazon S3 """ __version__ = "0.1.1dev" __author__ = "Will McGugan (will@willmcgugan.com)" -# 'base' imports * from 'path' and 'errors' +# 'base' imports * from 'path' and 'errors', so they'll +# be available here as well from base import * import errors diff --git a/fs/expose/fuse/__init__.py b/fs/expose/fuse/__init__.py index cae3a1b..6443279 100644 --- a/fs/expose/fuse/__init__.py +++ b/fs/expose/fuse/__init__.py @@ -192,7 +192,7 @@ class FSOperations(Operations): @handle_fs_errors def listxattr(self,path): try: - return self.fs.xattrs(path) + return self.fs.listxattrs(path) except AttributeError: raise UnsupportedError("listxattr") @@ -194,7 +194,7 @@ class OSFS(FS): except IOError, e: raise OperationFailedError('delete extended attribute', path=path, details=e) - def xattrs(self, path): + def listxattrs(self, path): try: return xattr.xattr(self.getsyspath(path)).keys() except IOError, e: diff --git a/fs/tests/test_xattr.py b/fs/tests/test_xattr.py index ca6e4d8..5eaa253 100644 --- a/fs/tests/test_xattr.py +++ b/fs/tests/test_xattr.py @@ -36,15 +36,15 @@ class XAttrTestCases: def test_list_xattrs(self): def do_list(p): - self.assertEquals(sorted(self.fs.xattrs(p)),[]) + self.assertEquals(sorted(self.fs.listxattrs(p)),[]) self.fs.setxattr(p,"xattr1","value1") - self.assertEquals(sorted(self.fs.xattrs(p)),["xattr1"]) + self.assertEquals(sorted(self.fs.listxattrs(p)),["xattr1"]) self.fs.setxattr(p,"attr2","value2") - self.assertEquals(sorted(self.fs.xattrs(p)),["attr2","xattr1"]) + self.assertEquals(sorted(self.fs.listxattrs(p)),["attr2","xattr1"]) self.fs.delxattr(p,"xattr1") - self.assertEquals(sorted(self.fs.xattrs(p)),["attr2"]) + self.assertEquals(sorted(self.fs.listxattrs(p)),["attr2"]) self.fs.delxattr(p,"attr2") - self.assertEquals(sorted(self.fs.xattrs(p)),[]) + self.assertEquals(sorted(self.fs.listxattrs(p)),[]) self.fs.createfile("test.txt","hello") do_list("test.txt") self.fs.makedir("mystuff") @@ -87,13 +87,13 @@ class XAttrTestCases: -from fs.wrappers.xattr import ensure_xattr +from fs.xattrs import ensure_xattrs from fs import tempfs class TestXAttr_TempFS(unittest.TestCase,FSTestCases,XAttrTestCases): def setUp(self): - self.fs = ensure_xattr(tempfs.TempFS()) + self.fs = ensure_xattrs(tempfs.TempFS()) def tearDown(self): td = self.fs._temp_dir @@ -109,7 +109,7 @@ from fs import memoryfs class TestXAttr_MemoryFS(unittest.TestCase,FSTestCases,XAttrTestCases): def setUp(self): - self.fs = ensure_xattr(memoryfs.MemoryFS()) + self.fs = ensure_xattrs(memoryfs.MemoryFS()) def check(self, p): return self.fs.exists(p) diff --git a/fs/wrappers/__init__.py b/fs/wrapfs.py index 4221d78..daf91d1 100644 --- a/fs/wrappers/__init__.py +++ b/fs/wrapfs.py @@ -1,25 +1,46 @@ """ - fs.wrappers: clases to transform files in a filesystem + fs.wrapfs: class for wrapping an existing FS object with added functionality -This sub-module provides facilities for easily wrapping an FS object inside -some sort of transformation - for example, to transparently compress or encrypt -files. +This module provides the class WrapFS, a base class for objects that wrap +another FS object and provide some transformation of its contents. It could +be very useful for implementing e.g. transparent encryption or compression +services. + +As a simple example of how this class could be used, the 'HideDotFiles' class +implements the standard unix shell functionality of hiding dot files in +directory listings. """ from fs.base import FS -class FSWrapper(FS): + +class WrapFS(FS): """FS that wraps another FS, providing translation etc. This class allows simple transforms to be applied to the names - and/or contents of files in an FS. It's particularly handy in - conjunction with wrappers from the "filelike" module. + and/or contents of files in an FS. It could be used to implement + e.g. compression or encryption in a relatively painless manner. + + The following methods can be overridden to control how files are + accessed in the underlying FS object: + + _file_wrap(file,mode): called for each file that is opened from + the underlying FS; may return a modified + file-like object. + + _encode(path): encode a path for access in the underlying FS + + _decode(path): decode a path from the underlying FS + + If the required path translation proceeds one component at a time, + it may be simpler to override the _encode_name() and _decode_name() + methods. """ def __init__(self,fs): - super(FSWrapper,self).__init__() + super(WrapFS,self).__init__() self.wrapped_fs = fs def _file_wrap(self,f,mode): @@ -62,6 +83,10 @@ class FSWrapper(FS): This method takes the mode given when opening a file, and should return a two-tuple giving the mode to be used in this FS as first item, and the mode to be used in the underlying FS as the second. + + An example of why this is needed is a WrapFS subclass that does + transparent file compression - in this case files from the wrapped + FS cannot be opened in append mode. """ return (mode,mode) @@ -128,3 +153,71 @@ class FSWrapper(FS): if hasattr(self.wrapped_fs,"close"): self.wrapped_fs.close() + +class HideDotFiles(WrapFS): + """FS wrapper class that hides dot-files in directory listings. + + The listdir() function takes an extra keyword argument 'hidden' + indicating whether hidden dotfiles shoud be included in the output. + It is False by default. + """ + + def is_hidden(self,path): + """Check whether the given path should be hidden.""" + return path and basename(path)[0] == "." + + def _encode(self,path): + return path + + def _decode(self,path): + return path + + def listdir(self,path="",**kwds): + hidden = kwds.pop("hidden",True) + entries = self.wrapped_fs.listdir(path,**kwds) + if not hidden: + entries = [e for e in entries if not self.is_hidden(e)] + return entries + + def walk(self, path="/", wildcard=None, dir_wildcard=None, search="breadth",hidden=False): + if search == "breadth": + dirs = [path] + while dirs: + current_path = dirs.pop() + paths = [] + for filename in self.listdir(current_path,hidden=hidden): + path = pathjoin(current_path, filename) + if self.isdir(path): + if dir_wildcard is not None: + if fnmatch.fnmatch(path, dir_wilcard): + dirs.append(path) + else: + dirs.append(path) + else: + if wildcard is not None: + if fnmatch.fnmatch(path, wildcard): + paths.append(filename) + else: + paths.append(filename) + yield (current_path, paths) + elif search == "depth": + def recurse(recurse_path): + for path in self.listdir(recurse_path, wildcard=dir_wildcard, full=True, dirs_only=True,hidden=hidden): + for p in recurse(path): + yield p + yield (recurse_path, self.listdir(recurse_path, wildcard=wildcard, files_only=True,hidden=hidden)) + for p in recurse(path): + yield p + else: + raise ValueError("Search should be 'breadth' or 'depth'") + + + def isdirempty(self, path): + path = normpath(path) + iter_dir = iter(self.listdir(path,hidden=True)) + try: + iter_dir.next() + except StopIteration: + return True + return False + diff --git a/fs/wrappers/hidedotfiles.py b/fs/wrappers/hidedotfiles.py deleted file mode 100644 index 527a2d7..0000000 --- a/fs/wrappers/hidedotfiles.py +++ /dev/null @@ -1,78 +0,0 @@ -""" - - fs.wrappers.hidedotfiles: FS wrapper to hide dot-files in dir listings - -""" - -from fs.path import * -from fs.errors import * -from fs.wrappers import FSWrapper - - -class HideDotFiles(FSWrapper): - """FS wrapper class that hides dot-files in directory listings. - - The listdir() function takes an extra keyword argument 'hidden' - indicating whether hidden dotfiles shoud be included in the output. - It is False by default. - """ - - def is_hidden(self,path): - """Check whether the given path should be hidden.""" - return path and basename(path)[0] == "." - - def _encode(self,path): - return path - - def _decode(self,path): - return path - - def listdir(self,path="",**kwds): - hidden = kwds.pop("hidden",True) - entries = self.wrapped_fs.listdir(path,**kwds) - if not hidden: - entries = [e for e in entries if not self.is_hidden(e)] - return entries - - def walk(self, path="/", wildcard=None, dir_wildcard=None, search="breadth",hidden=False): - if search == "breadth": - dirs = [path] - while dirs: - current_path = dirs.pop() - paths = [] - for filename in self.listdir(current_path,hidden=hidden): - path = pathjoin(current_path, filename) - if self.isdir(path): - if dir_wildcard is not None: - if fnmatch.fnmatch(path, dir_wilcard): - dirs.append(path) - else: - dirs.append(path) - else: - if wildcard is not None: - if fnmatch.fnmatch(path, wildcard): - paths.append(filename) - else: - paths.append(filename) - yield (current_path, paths) - elif search == "depth": - def recurse(recurse_path): - for path in self.listdir(recurse_path, wildcard=dir_wildcard, full=True, dirs_only=True,hidden=hidden): - for p in recurse(path): - yield p - yield (recurse_path, self.listdir(recurse_path, wildcard=wildcard, files_only=True,hidden=hidden)) - for p in recurse(path): - yield p - else: - raise ValueError("Search should be 'breadth' or 'depth'") - - - def isdirempty(self, path): - path = normpath(path) - iter_dir = iter(self.listdir(path,hidden=True)) - try: - iter_dir.next() - except StopIteration: - return True - return False - diff --git a/fs/wrappers/xattr.py b/fs/xattrs.py index 524477f..79330c1 100644 --- a/fs/wrappers/xattr.py +++ b/fs/xattrs.py @@ -1,13 +1,25 @@ """ - fs.wrappers.xattr: FS wrapper for simulating extended-attribute support. + fs.xattrs: extended attribute support for FS This module defines a standard interface for FS subclasses that want to -support extended attributes, and an FSWrapper subclass that can simulate +support extended file attributes, and a WrapFS subclass that can simulate extended attributes on top of an ordinery FS. +FS instances offering extended attribute support must provide the following +methods: + + getxattr(path,name) - get the named attribute for the given path, + or None if it does not exist + setxattr(path,name,value) - set the named attribute for the given path + to the given value + delxattr(path,name) - delete the named attribute for the given path, + raising KeyError if it does not exist + listxattrs(path) - iterator over all stored attribute names for + the given path + If extended attributes are required by FS-consuming code, it should use the -function 'ensure_xattr'. This will interrogate an FS object to determine +function 'ensure_xattrs'. This will interrogate an FS object to determine if it has native xattr support, and return a wrapped version if it does not. """ @@ -18,10 +30,10 @@ except ImportError: from fs.path import * from fs.errors import * -from fs.wrappers import FSWrapper +from fs.wrapfs import WrapFS -def ensure_xattr(fs): +def ensure_xattrs(fs): """Ensure that the given FS supports xattrs, simulating them if required. Given an FS object, this function returns an equivalent FS that has support @@ -29,13 +41,14 @@ def ensure_xattr(fs): supported natively, or a wrapper class is they must be simulated. """ try: - # This doesn't have to exist, it should return None by default + # This attr doesn't have to exist, None should be returned by default fs.getxattr("/","testingx-xattr") return fs except Exception: return SimulateXAttr(fs) -class SimulateXAttr(FSWrapper): + +class SimulateXAttr(WrapFS): """FS wrapper class that simulates xattr support. The following methods are supplied for manipulating extended attributes: @@ -45,7 +58,7 @@ class SimulateXAttr(FSWrapper): * delxattr: delete an xattr of a path by name For each file in the underlying FS, this class maintains a corresponding - file '.xattrs.FILENAME' containing its extended attributes. Extended + '.xattrs.FILENAME' file containing its extended attributes. Extended attributes of a directory are stored in the file '.xattrs' within the directory itself. """ @@ -104,7 +117,7 @@ class SimulateXAttr(FSWrapper): pass self._set_attr_dict(path, attrs) - def xattrs(self,path): + def listxattrs(self,path): """List all the extended attribute keys set on the given path.""" if not self.exists(path): raise ResourceNotFoundError(path) |