summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--fs/base.py1
-rw-r--r--fs/filelike.py2
-rw-r--r--fs/mountfs.py43
-rw-r--r--fs/objecttree.py105
-rw-r--r--fs/path.py44
-rw-r--r--fs/tests/test_objecttree.py47
-rw-r--r--fs/wrapfs/__init__.py47
8 files changed, 128 insertions, 162 deletions
diff --git a/ChangeLog b/ChangeLog
index d14489b..9566ec2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -70,4 +70,5 @@
* Added copyfile_non_atomic and movefile_non_atomic for improved performance of multi-threaded copies
* Added ilistdir() and ilistdirinfo() methods, which are generator-based
variants of listdir() and listdirinfo().
+ * Removed obsolute module fs.objectree; use fs.path.PathMap instead.
diff --git a/fs/base.py b/fs/base.py
index 6ac3cc5..2194682 100644
--- a/fs/base.py
+++ b/fs/base.py
@@ -224,6 +224,7 @@ class FS(object):
The following are less common:
* *free_space* The free space (in bytes) available on the file system
+ * *total_space* The total space (in bytes) available on the file system
FS implementations may expose non-generic meta data through a self-named namespace. e.g. 'somefs.some_meta'
diff --git a/fs/filelike.py b/fs/filelike.py
index cbf71d2..476d1eb 100644
--- a/fs/filelike.py
+++ b/fs/filelike.py
@@ -32,7 +32,7 @@ import tempfile as _tempfile
try:
from cStringIO import StringIO as _StringIO
except ImportError:
- from StrimgIO import StringIO as _StringIO
+ from StringIO import StringIO as _StringIO
import fs
diff --git a/fs/mountfs.py b/fs/mountfs.py
index 48131a6..662da85 100644
--- a/fs/mountfs.py
+++ b/fs/mountfs.py
@@ -218,6 +218,49 @@ class MountFS(FS):
return paths
@synchronize
+ def ilistdir(self, path="/", wildcard=None, full=False, absolute=False, dirs_only=False, files_only=False):
+ fs, mount_path, delegate_path = self._delegate(path)
+
+ if fs is None:
+ raise ResourceNotFoundError(path)
+
+ if fs is self:
+ paths = self.mount_tree.names(path)
+ for path in self._listdir_helper(path,wildcard,full,absolute,dirs_only,files_only):
+ yield path
+ else:
+ paths = fs.ilistdir(delegate_path,
+ wildcard=wildcard,
+ full=False,
+ absolute=False,
+ dirs_only=dirs_only)
+ extra_paths = set(self.mount_tree.names(path))
+ if full:
+ pathhead = relpath(normpath(path))
+ def mkpath(p):
+ return pathjoin(pathhead,p)
+ elif absolute:
+ pathhead = abspath(normpath(path))
+ def mkpath(p):
+ return pathjoin(pathhead,p)
+ else:
+ def mkpath(p):
+ return p
+ for p in paths:
+ if p not in extra_paths:
+ yield mkpath(p)
+ for p in extra_paths:
+ if dirs_only:
+ if self.isdir(pathjoin(path,p)):
+ yield mkpath(p)
+ elif files_only:
+ if self.isfile(pathjoin(path,nm)):
+ yield mkpath(p)
+ else:
+ yield mkpath(p)
+
+
+ @synchronize
def makedir(self, path, recursive=False, allow_recreate=False):
fs, mount_path, delegate_path = self._delegate(path)
if fs is self or fs is None:
diff --git a/fs/objecttree.py b/fs/objecttree.py
deleted file mode 100644
index 2f04d90..0000000
--- a/fs/objecttree.py
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-class _ObjectDict(dict):
- pass
-
-
-class ObjectTree(object):
- """A class to facilitate the creation of tree structures."""
-
- def __init__(self):
- self.root = _ObjectDict()
-
- def _split(self, path):
- if '/' not in path:
- return "", path
- else:
- return path.rsplit('/', 1)
-
- def _splitpath(self, path):
- return [p for p in path.split('/') if p]
-
- def _locate(self, path):
- current = self.root
- for path_component in self._splitpath(path):
- if type(current) is not _ObjectDict:
- return None
- node = current.get(path_component, None)
- if node is None:
- return None
- current = node
- return current
-
- def __setitem__(self, path, object):
- if not path:
- raise IndexError("No path supplied")
- current = self.root
- path, name = self._split(path)
- for path_component in self._splitpath(path):
- node = current.get(path_component, None)
- if type(node) is not _ObjectDict:
- new_dict = _ObjectDict()
- current[path_component] = new_dict
- current = new_dict
- else:
- current = node
- current[name] = object
-
- def __getitem__(self, path):
- node = self._locate(path)
- if node is None:
- raise IndexError("Path does not exist")
- return node
-
- def __delitem__(self, path):
- path, name = self._split(path)
- node = self._locate(path)
- if node is None or type(node) is not _ObjectDict:
- raise IndexError("Path does not exist")
- del node[name]
-
- def get(self, path, default):
- node = self._locate(path)
- if node is None:
- return default
- return node
-
- def partialget(self, path, default=None):
- current = self.root
- partial_path = []
- remaining_path = self._splitpath(path)
- for path_component in remaining_path[:]:
- if type(current) is not _ObjectDict:
- return "/".join(partial_path), current, "/".join(remaining_path)
- partial_path.append(path_component)
- remaining_path.pop(0)
- node = current.get(path_component, None)
- if node is None:
- return None, default, None
- current = node
- return path, current, ""
-
- def isobject(self, path):
- node = self._locate(path)
- return type(node) is not _ObjectDict
-
- def __contains__(self, path):
- node = self._locate(path)
- return node is not None
-
- def __iter__(self):
- return iter(self.root)
-
- def keys(self):
- return self.root.keys()
-
- def iterkeys(self):
- return self.root.iterkeys()
-
- def items(self):
- return self.root.items()
-
- def iteritems(self):
- return self.root.iteritems()
-
-
diff --git a/fs/path.py b/fs/path.py
index 23bac89..b9477a7 100644
--- a/fs/path.py
+++ b/fs/path.py
@@ -17,7 +17,7 @@ def normpath(path):
"""Normalizes a path to be in the format expected by FS objects.
This function remove any leading or trailing slashes, collapses
- duplicate slashes, replaces forward with backward slashes, and generally
+ duplicate slashes, replaces backward with forward slashes, and generally
tries very hard to return a new path string the canonical FS format.
If the path is invalid, ValueError will be raised.
@@ -162,8 +162,16 @@ def pathjoin(*paths):
path = abspath(path)
return path
-# Allow pathjoin() to be used as fs.path.join()
-join = pathjoin
+
+def join(*paths):
+ """Joins any number of paths together, returning a new path string.
+
+ This is a simple alias for the ``pathjoin`` function, allowing it to be
+ used as ``fs.path.join`` in direct correspondance with ``os.path.join``.
+
+ :param paths: Paths to join are given in positional arguments
+ """
+ return pathjoin(*paths)
def pathsplit(path):
@@ -189,8 +197,17 @@ def pathsplit(path):
split = path.rsplit('/', 1)
return (split[0] or '/', split[1])
-# Allow pathsplit() to be used as fs.path.split()
-split = pathsplit
+
+def split(path):
+ """Splits a path into (head, tail) pair.
+
+ This is a simple alias for the ``pathsplit`` function, allowing it to be
+ used as ``fs.path.split`` in direct correspondance with ``os.path.split``.
+
+ :param path: Path to split
+ """
+ return pathsplit(path)
+
def splitext(path):
"""Splits the extension from the path, and returns the path (up to the last
@@ -337,9 +354,18 @@ def frombase(path1, path2):
class PathMap(object):
"""Dict-like object with paths for keys.
- A PathMap is like a dictionary where the keys are all FS paths. It allows
- various dictionary operations (e.g. listing values, clearing values) to
- be performed on a subset of the keys sharing some common prefix, e.g.::
+ A PathMap is like a dictionary where the keys are all FS paths. It has
+ two main advantages over a standard dictionary. First, keys are normalised
+ automatically::
+
+ >>> pm = PathMap()
+ >>> pm["hello/world"] = 42
+ >>> print pm["/hello/there/../world"]
+ 42
+
+ Second, various dictionary operations (e.g. listing or clearing values)
+ can be efficiently performed on a subset of keys sharing some common
+ prefix::
# list all values in the map
pm.values()
@@ -547,4 +573,4 @@ def iswildcard(path):
return not base_chars.isdisjoint(_wild_chars)
if __name__ == "__main__":
- print recursepath('a/b/c') \ No newline at end of file
+ print recursepath('a/b/c')
diff --git a/fs/tests/test_objecttree.py b/fs/tests/test_objecttree.py
deleted file mode 100644
index b8d9a12..0000000
--- a/fs/tests/test_objecttree.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-
- fs.tests.test_objectree: testcases for the fs objecttree module
-
-"""
-
-
-import unittest
-
-import fs.tests
-from fs import objecttree
-
-class TestObjectTree(unittest.TestCase):
- """Testcases for the ObjectTree class."""
-
- def test_getset(self):
- ot = objecttree.ObjectTree()
- ot['foo'] = "bar"
- self.assertEqual(ot['foo'], 'bar')
-
- ot = objecttree.ObjectTree()
- ot['foo/bar'] = "baz"
- self.assertEqual(ot['foo'], {'bar':'baz'})
- self.assertEqual(ot['foo/bar'], 'baz')
-
- del ot['foo/bar']
- self.assertEqual(ot['foo'], {})
-
- ot = objecttree.ObjectTree()
- ot['a/b/c'] = "A"
- ot['a/b/d'] = "B"
- ot['a/b/e'] = "C"
- ot['a/b/f'] = "D"
- self.assertEqual(sorted(ot['a/b'].values()), ['A', 'B', 'C', 'D'])
- self.assert_(ot.get('a/b/x', -1) == -1)
-
- self.assert_('a/b/c' in ot)
- self.assert_('a/b/x' not in ot)
- self.assert_(ot.isobject('a/b/c'))
- self.assert_(ot.isobject('a/b/d'))
- self.assert_(not ot.isobject('a/b'))
-
- left, object, right = ot.partialget('a/b/e/f/g')
- self.assertEqual(left, "a/b/e")
- self.assertEqual(object, "C")
- self.assertEqual(right, "f/g")
-
diff --git a/fs/wrapfs/__init__.py b/fs/wrapfs/__init__.py
index 760a601..9b02aeb 100644
--- a/fs/wrapfs/__init__.py
+++ b/fs/wrapfs/__init__.py
@@ -192,6 +192,32 @@ class WrapFS(FS):
return entries
@rewrite_errors
+ def ilistdir(self, path="", wildcard=None, full=False, absolute=False, dirs_only=False, files_only=False):
+ kwds = dict(wildcard=wildcard,
+ full=full,
+ absolute=absolute,
+ dirs_only=dirs_only,
+ files_only=files_only)
+ full = kwds.pop("full",False)
+ absolute = kwds.pop("absolute",False)
+ wildcard = kwds.pop("wildcard",None)
+ if wildcard is None:
+ wildcard = lambda fn:True
+ elif not callable(wildcard):
+ wildcard_re = re.compile(fnmatch.translate(wildcard))
+ wildcard = lambda fn:bool (wildcard_re.match(fn))
+ enc_path = self._encode(path)
+ for e in self.wrapped_fs.ilistdir(enc_path,**kwds):
+ e = basename(self._decode(pathjoin(enc_path,e)))
+ if not wildcard(e):
+ continue
+ if full:
+ e = pathjoin(path,e)
+ elif absolute:
+ e = abspath(pathjoin(path,e))
+ yield e
+
+ @rewrite_errors
def listdirinfo(self, path="", **kwds):
full = kwds.pop("full",False)
absolute = kwds.pop("absolute",False)
@@ -215,6 +241,27 @@ class WrapFS(FS):
return entries
@rewrite_errors
+ def ilistdirinfo(self, path="", **kwds):
+ full = kwds.pop("full",False)
+ absolute = kwds.pop("absolute",False)
+ wildcard = kwds.pop("wildcard",None)
+ if wildcard is None:
+ wildcard = lambda fn:True
+ elif not callable(wildcard):
+ wildcard_re = re.compile(fnmatch.translate(wildcard))
+ wildcard = lambda fn:bool (wildcard_re.match(fn))
+ enc_path = self._encode(path)
+ for (nm,info) in self.wrapped_fs.ilistdirinfo(enc_path,**kwds):
+ nm = basename(self._decode(pathjoin(enc_path,nm)))
+ if not wildcard(nm):
+ continue
+ if full:
+ nm = pathjoin(path,nm)
+ elif absolute:
+ nm = abspath(pathjoin(path,nm))
+ yield (nm,info)
+
+ @rewrite_errors
def makedir(self, path, *args, **kwds):
return self.wrapped_fs.makedir(self._encode(path),*args,**kwds)