summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrfkelly0 <rfkelly0@67cdc799-7952-0410-af00-57a81ceafa0f>2009-06-06 12:29:20 +0000
committerrfkelly0 <rfkelly0@67cdc799-7952-0410-af00-57a81ceafa0f>2009-06-06 12:29:20 +0000
commit75ce3de9142fac99960160e8b00d55d346f48c8c (patch)
treef43a2f66a4406fa8bc5833fd520d3518e13ba1e9
parent0212c1224b6c53b8aa61a0cdb343cf990fa47a82 (diff)
downloadpyfilesystem-git-75ce3de9142fac99960160e8b00d55d346f48c8c.tar.gz
move xattr stuff into its own first-level submodule
-rw-r--r--fs/__init__.py16
-rw-r--r--fs/expose/fuse/__init__.py2
-rw-r--r--fs/osfs.py2
-rw-r--r--fs/tests/test_xattr.py16
-rw-r--r--fs/wrapfs.py (renamed from fs/wrappers/__init__.py)109
-rw-r--r--fs/wrappers/hidedotfiles.py78
-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")
diff --git a/fs/osfs.py b/fs/osfs.py
index bee807c..03357e8 100644
--- a/fs/osfs.py
+++ b/fs/osfs.py
@@ -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)