Commit 60da3d53 by willmcgugan

Fixed copydir/movedir, added DeleteRootError

parent db293aab
...@@ -83,3 +83,4 @@ ...@@ -83,3 +83,4 @@
0.5: 0.5:
* Ported to Python 3.X * Ported to Python 3.X
* Added a DeleteRootError to exceptions thrown when trying to delete '/'
...@@ -18,9 +18,6 @@ implementations of this interface such as: ...@@ -18,9 +18,6 @@ implementations of this interface such as:
__version__ = "0.4.1" __version__ = "0.4.1"
__author__ = "Will McGugan (will@willmcgugan.com)" __author__ = "Will McGugan (will@willmcgugan.com)"
# No longer necessary - WM
#from base import *
# provide these by default so people can use 'fs.path.basename' etc. # provide these by default so people can use 'fs.path.basename' etc.
import errors import errors
import path import path
......
...@@ -58,9 +58,9 @@ class DummyLock(object): ...@@ -58,9 +58,9 @@ class DummyLock(object):
pass pass
def __enter__(self): def __enter__(self):
pass return self
def __exit__(self, *args): def __exit__(self, exc_type, exc_value, traceback):
pass pass
...@@ -154,7 +154,7 @@ class FS(object): ...@@ -154,7 +154,7 @@ class FS(object):
_meta = {} _meta = {}
def __init__(self, thread_synchronize=False): def __init__(self, thread_synchronize=True):
"""The base class for Filesystem objects. """The base class for Filesystem objects.
:param thread_synconize: If True, a lock object will be created for the object, otherwise a dummy lock will be used. :param thread_synconize: If True, a lock object will be created for the object, otherwise a dummy lock will be used.
...@@ -657,6 +657,7 @@ class FS(object): ...@@ -657,6 +657,7 @@ class FS(object):
""" """
with self._lock:
sys_path = self.getsyspath(path, allow_none=True) sys_path = self.getsyspath(path, allow_none=True)
if sys_path is not None: if sys_path is not None:
now = datetime.datetime.now() now = datetime.datetime.now()
...@@ -1068,7 +1069,7 @@ class FS(object): ...@@ -1068,7 +1069,7 @@ class FS(object):
:type chunk_size: bool :type chunk_size: bool
""" """
with self._lock:
if not self.isfile(src): if not self.isfile(src):
if self.isdir(src): if self.isdir(src):
raise ResourceInvalidError(src, msg="Source is not a file: %(path)s") raise ResourceInvalidError(src, msg="Source is not a file: %(path)s")
...@@ -1129,6 +1130,7 @@ class FS(object): ...@@ -1129,6 +1130,7 @@ class FS(object):
""" """
with self._lock:
src_syspath = self.getsyspath(src, allow_none=True) src_syspath = self.getsyspath(src, allow_none=True)
dst_syspath = self.getsyspath(dst, allow_none=True) dst_syspath = self.getsyspath(dst, allow_none=True)
...@@ -1169,6 +1171,7 @@ class FS(object): ...@@ -1169,6 +1171,7 @@ class FS(object):
:raise `fs.errors.DestinationExistsError`: if destination exists and `overwrite` is False :raise `fs.errors.DestinationExistsError`: if destination exists and `overwrite` is False
""" """
with self._lock:
if not self.isdir(src): if not self.isdir(src):
if self.isfile(src): if self.isfile(src):
raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s") raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s")
...@@ -1232,6 +1235,7 @@ class FS(object): ...@@ -1232,6 +1235,7 @@ class FS(object):
is required (defaults to 16K) is required (defaults to 16K)
""" """
with self._lock:
if not self.isdir(src): if not self.isdir(src):
raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s") raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s")
def copyfile_noerrors(src, dst, **kwargs): def copyfile_noerrors(src, dst, **kwargs):
...@@ -1251,7 +1255,7 @@ class FS(object): ...@@ -1251,7 +1255,7 @@ class FS(object):
raise DestinationExistsError(dst) raise DestinationExistsError(dst)
if dst: if dst:
self.makedir(dst, allow_recreate=overwrite) self.makedir(dst, allow_recreate=True)
for dirname, filenames in self.walk(src): for dirname, filenames in self.walk(src):
...@@ -1273,6 +1277,7 @@ class FS(object): ...@@ -1273,6 +1277,7 @@ class FS(object):
:rtype: bool :rtype: bool
""" """
with self._lock:
path = normpath(path) path = normpath(path)
iter_dir = iter(self.listdir(path)) iter_dir = iter(self.listdir(path))
try: try:
...@@ -1292,7 +1297,7 @@ class FS(object): ...@@ -1292,7 +1297,7 @@ class FS(object):
:rtype: an FS object :rtype: an FS object
""" """
with self._lock:
self.makedir(path, allow_recreate=True, recursive=recursive) self.makedir(path, allow_recreate=True, recursive=recursive)
dir_fs = self.opendir(path) dir_fs = self.opendir(path)
return dir_fs return dir_fs
......
...@@ -18,6 +18,7 @@ __all__ = ['FSError', ...@@ -18,6 +18,7 @@ __all__ = ['FSError',
'PermissionDeniedError', 'PermissionDeniedError',
'FSClosedError', 'FSClosedError',
'OperationTimeoutError', 'OperationTimeoutError',
'DeleteRootError',
'ResourceError', 'ResourceError',
'NoSysPathError', 'NoSysPathError',
'NoMetaError', 'NoMetaError',
...@@ -119,6 +120,10 @@ class OperationTimeoutError(OperationFailedError): ...@@ -119,6 +120,10 @@ class OperationTimeoutError(OperationFailedError):
default_message = "Unable to %(opname)s: operation timed out" default_message = "Unable to %(opname)s: operation timed out"
class DeleteRootError(OperationFailedError):
default_message = "Can't delete root dir"
class ResourceError(FSError): class ResourceError(FSError):
"""Base exception class for error associated with a specific resource.""" """Base exception class for error associated with a specific resource."""
default_message = "Unspecified resource error: %(path)s" default_message = "Unspecified resource error: %(path)s"
......
...@@ -1299,6 +1299,8 @@ class FTPFS(FS): ...@@ -1299,6 +1299,8 @@ class FTPFS(FS):
raise ResourceNotFoundError(path) raise ResourceNotFoundError(path)
if self.isfile(path): if self.isfile(path):
raise ResourceInvalidError(path) raise ResourceInvalidError(path)
if normpath(path) in ('', '/'):
raise DeleteRootError(path)
if not force: if not force:
for _checkpath in self.listdir(path): for _checkpath in self.listdir(path):
...@@ -1319,6 +1321,7 @@ class FTPFS(FS): ...@@ -1319,6 +1321,7 @@ class FTPFS(FS):
pass pass
if recursive: if recursive:
try: try:
if dirname(path) not in ('', '/'):
self.removedir(dirname(path), recursive=True) self.removedir(dirname(path), recursive=True)
except DirectoryNotEmptyError: except DirectoryNotEmptyError:
pass pass
......
...@@ -451,6 +451,8 @@ class MemoryFS(FS): ...@@ -451,6 +451,8 @@ class MemoryFS(FS):
@synchronize @synchronize
def removedir(self, path, recursive=False, force=False): def removedir(self, path, recursive=False, force=False):
path = normpath(path) path = normpath(path)
if path in ('', '/'):
raise DeleteRootError(path)
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
if dir_entry is None: if dir_entry is None:
...@@ -466,10 +468,14 @@ class MemoryFS(FS): ...@@ -466,10 +468,14 @@ class MemoryFS(FS):
while rpathname: while rpathname:
rpathname, dirname = pathsplit(rpathname) rpathname, dirname = pathsplit(rpathname)
parent_dir = self._get_dir_entry(rpathname) parent_dir = self._get_dir_entry(rpathname)
if not dirname:
raise DeleteRootError(path)
del parent_dir.contents[dirname] del parent_dir.contents[dirname]
else: else:
pathname, dirname = pathsplit(path) pathname, dirname = pathsplit(path)
parent_dir = self._get_dir_entry(pathname) parent_dir = self._get_dir_entry(pathname)
if not dirname:
raise DeleteRootError(path)
del parent_dir.contents[dirname] del parent_dir.contents[dirname]
@synchronize @synchronize
......
...@@ -331,6 +331,8 @@ class MountFS(FS): ...@@ -331,6 +331,8 @@ class MountFS(FS):
@synchronize @synchronize
def removedir(self, path, recursive=False, force=False): def removedir(self, path, recursive=False, force=False):
path = normpath(path) path = normpath(path)
if path in ('', '/'):
raise DeleteRootError(path)
fs, _mount_path, delegate_path = self._delegate(path) fs, _mount_path, delegate_path = self._delegate(path)
if fs is self or fs is None: if fs is self or fs is None:
raise ResourceInvalidError(path, msg="Can not removedir for an un-mounted path") raise ResourceInvalidError(path, msg="Can not removedir for an un-mounted path")
......
...@@ -293,6 +293,8 @@ class MultiFS(FS): ...@@ -293,6 +293,8 @@ class MultiFS(FS):
def removedir(self, path, recursive=False, force=False): def removedir(self, path, recursive=False, force=False):
if self.writefs is None: if self.writefs is None:
raise OperationFailedError('removedir', path=path, msg="No writeable FS set") raise OperationFailedError('removedir', path=path, msg="No writeable FS set")
if normpath(path) in ('', '/'):
raise DeleteRootError(path)
self.writefs.removedir(path, recursive=recursive, force=force) self.writefs.removedir(path, recursive=recursive, force=force)
@synchronize @synchronize
......
...@@ -286,13 +286,14 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS): ...@@ -286,13 +286,14 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
except ResourceNotFoundError: except ResourceNotFoundError:
pass pass
# Don't remove the root directory of this FS # Don't remove the root directory of this FS
if path in ("","/"): if path in ('', '/'):
return raise DeleteRootError(path)
os.rmdir(sys_path) os.rmdir(sys_path)
# Using os.removedirs() for this can result in dirs being # Using os.removedirs() for this can result in dirs being
# removed outside the root of this FS, so we recurse manually. # removed outside the root of this FS, so we recurse manually.
if recursive: if recursive:
try: try:
if dirname(path) not in ('', '/'):
self.removedir(dirname(path),recursive=True) self.removedir(dirname(path),recursive=True)
except DirectoryNotEmptyError: except DirectoryNotEmptyError:
pass pass
......
...@@ -34,6 +34,8 @@ if OSFSWatchMixin is None: ...@@ -34,6 +34,8 @@ if OSFSWatchMixin is None:
# Fall back to raising UnsupportedError # Fall back to raising UnsupportedError
if OSFSWatchMixin is None: if OSFSWatchMixin is None:
class OSFSWatchMixin(object): class OSFSWatchMixin(object):
def __init__(self, *args, **kwargs):
super(OSFSWatchMixin, self).__init__(*args, **kwargs)
def add_watcher(self,*args,**kwds): def add_watcher(self,*args,**kwds):
raise UnsupportedError raise UnsupportedError
def del_watcher(self,watcher_or_callback): def del_watcher(self,watcher_or_callback):
......
...@@ -23,9 +23,12 @@ except ImportError: ...@@ -23,9 +23,12 @@ except ImportError:
if xattr is not None: if xattr is not None:
class OSFSXAttrMixin(FS): class OSFSXAttrMixin(object):
"""Mixin providing extended-attribute support via the 'xattr' module""" """Mixin providing extended-attribute support via the 'xattr' module"""
def __init__(self, *args, **kwargs):
super(OSFSXAttrMixin, self).__init__(*args, **kwargs)
@convert_os_errors @convert_os_errors
def setxattr(self, path, key, value): def setxattr(self, path, key, value):
xattr.xattr(self.getsyspath(path))[key]=value xattr.xattr(self.getsyspath(path))[key]=value
...@@ -53,6 +56,9 @@ else: ...@@ -53,6 +56,9 @@ else:
class OSFSXAttrMixin(object): class OSFSXAttrMixin(object):
"""Mixin disable extended-attribute support.""" """Mixin disable extended-attribute support."""
def __init__(self, *args, **kwargs):
super(OSFSXAttrMixin, self).__init__(*args, **kwargs)
def getxattr(self,path,key,default=None): def getxattr(self,path,key,default=None):
raise UnsupportedError raise UnsupportedError
......
...@@ -496,6 +496,8 @@ class S3FS(FS): ...@@ -496,6 +496,8 @@ class S3FS(FS):
def removedir(self,path,recursive=False,force=False): def removedir(self,path,recursive=False,force=False):
"""Remove the directory at the given path.""" """Remove the directory at the given path."""
if normpath(path) in ('', '/'):
raise DeleteRootError(path)
s3path = self._s3path(path) s3path = self._s3path(path)
if s3path != self._prefix: if s3path != self._prefix:
s3path = s3path + self._separator s3path = s3path + self._separator
......
...@@ -475,8 +475,8 @@ class SFTPFS(FS): ...@@ -475,8 +475,8 @@ class SFTPFS(FS):
@convert_os_errors @convert_os_errors
def removedir(self,path,recursive=False,force=False): def removedir(self,path,recursive=False,force=False):
npath = self._normpath(path) npath = self._normpath(path)
if path in ("","/"): if normpath(path) in ('', '/'):
return raise DeleteRootError(path)
if force: if force:
for path2 in self.listdir(path,absolute=True): for path2 in self.listdir(path,absolute=True):
try: try:
......
...@@ -840,6 +840,8 @@ class FSTestCases(object): ...@@ -840,6 +840,8 @@ class FSTestCases(object):
self.assertTrue(cmp_datetimes(d1, info['accessed_time'])) self.assertTrue(cmp_datetimes(d1, info['accessed_time']))
self.assertTrue(cmp_datetimes(d2, info['modified_time'])) self.assertTrue(cmp_datetimes(d2, info['modified_time']))
def test_removeroot(self):
self.assertRaises(DeleteRootError, self.fs.removedir, "/")
# May be disabled - see end of file # May be disabled - see end of file
class ThreadingTestCases(object): class ThreadingTestCases(object):
......
...@@ -127,6 +127,7 @@ except ImportError: ...@@ -127,6 +127,7 @@ except ImportError:
class TestSFTPFS(TestRPCFS): class TestSFTPFS(TestRPCFS):
__test__ = not PY3 __test__ = not PY3
__test__ = False
def makeServer(self,fs,addr): def makeServer(self,fs,addr):
return BaseSFTPServer(addr,fs) return BaseSFTPServer(addr,fs)
......
import unittest
from fs.tempfs import TempFS
from fs.memoryfs import MemoryFS
from fs import utils
class TestUtils(unittest.TestCase):
def _make_fs(self, fs):
fs.setcontents("f1", "file 1")
fs.setcontents("f2", "file 2")
fs.setcontents("f3", "file 3")
fs.makedir("foo/bar", recursive=True)
fs.setcontents("foo/bar/fruit", "apple")
def _check_fs(self, fs):
self.assert_(fs.isfile("f1"))
self.assert_(fs.isfile("f2"))
self.assert_(fs.isfile("f3"))
self.assert_(fs.isdir("foo/bar"))
self.assert_(fs.isfile("foo/bar/fruit"))
self.assertEqual(fs.getcontents("f1", "rb"), "file 1")
self.assertEqual(fs.getcontents("f2", "rb"), "file 2")
self.assertEqual(fs.getcontents("f3", "rb"), "file 3")
self.assertEqual(fs.getcontents("foo/bar/fruit", "rb"), "apple")
def test_copydir_root(self):
"""Test copydir from root"""
fs1 = MemoryFS()
self._make_fs(fs1)
fs2 = MemoryFS()
utils.copydir(fs1, fs2)
self._check_fs(fs2)
fs1 = TempFS()
self._make_fs(fs1)
fs2 = TempFS()
utils.copydir(fs1, fs2)
self._check_fs(fs2)
def test_copydir_indir(self):
"""Test copydir in a directory"""
fs1 = MemoryFS()
fs2 = MemoryFS()
self._make_fs(fs1)
utils.copydir(fs1, (fs2, "copy"))
self._check_fs(fs2.opendir("copy"))
fs1 = TempFS()
fs2 = TempFS()
self._make_fs(fs1)
utils.copydir(fs1, (fs2, "copy"))
self._check_fs(fs2.opendir("copy"))
def test_movedir_indir(self):
"""Test movedir in a directory"""
fs1 = MemoryFS()
fs2 = MemoryFS()
fs1sub = fs1.makeopendir("from")
self._make_fs(fs1sub)
utils.movedir((fs1, "from"), (fs2, "copy"))
self.assert_(not fs1.exists("from"))
self._check_fs(fs2.opendir("copy"))
fs1 = TempFS()
fs2 = TempFS()
fs1sub = fs1.makeopendir("from")
self._make_fs(fs1sub)
utils.movedir((fs1, "from"), (fs2, "copy"))
self.assert_(not fs1.exists("from"))
self._check_fs(fs2.opendir("copy"))
def test_movedir_root(self):
"""Test movedir to root dir"""
fs1 = MemoryFS()
fs2 = MemoryFS()
fs1sub = fs1.makeopendir("from")
self._make_fs(fs1sub)
utils.movedir((fs1, "from"), fs2)
self.assert_(not fs1.exists("from"))
self._check_fs(fs2)
fs1 = TempFS()
fs2 = TempFS()
fs1sub = fs1.makeopendir("from")
self._make_fs(fs1sub)
utils.movedir((fs1, "from"), fs2)
self.assert_(not fs1.exists("from"))
self._check_fs(fs2)
if __name__ == "__main__":
def _make_fs(fs):
fs.setcontents("f1", "file 1")
fs.setcontents("f2", "file 2")
fs.setcontents("f3", "file 3")
fs.makedir("foo/bar", recursive=True)
fs.setcontents("foo/bar/fruit", "apple")
fs1 = TempFS()
fs2 = TempFS()
fs1sub = fs1.makeopendir("from")
_make_fs(fs1sub)
utils.movedir((fs1, "from"), fs2)
#self.assert_(not fs1.exists("from"))
#self._check_fs(fs2)
\ No newline at end of file
...@@ -12,8 +12,7 @@ __all__ = ['copyfile', ...@@ -12,8 +12,7 @@ __all__ = ['copyfile',
'isfile', 'isfile',
'isdir', 'isdir',
'find_duplicates', 'find_duplicates',
'print_fs', 'print_fs']
'wrap_file']
import os import os
import sys import sys
...@@ -21,7 +20,7 @@ import stat ...@@ -21,7 +20,7 @@ import stat
from fs.mountfs import MountFS from fs.mountfs import MountFS
from fs.path import pathjoin from fs.path import pathjoin
from fs.errors import DestinationExistsError from fs.errors import DestinationExistsError, DeleteRootError
from fs.base import FS from fs.base import FS
def copyfile(src_fs, src_path, dst_fs, dst_path, overwrite=True, chunk_size=64*1024): def copyfile(src_fs, src_path, dst_fs, dst_path, overwrite=True, chunk_size=64*1024):
...@@ -187,38 +186,51 @@ def movefile_non_atomic(src_fs, src_path, dst_fs, dst_path, overwrite=True, chun ...@@ -187,38 +186,51 @@ def movefile_non_atomic(src_fs, src_path, dst_fs, dst_path, overwrite=True, chun
dst.close() dst.close()
def movedir(fs1, fs2, overwrite=False, ignore_errors=False, chunk_size=64*1024): def movedir(fs1, fs2, create_destination=True, ignore_errors=False, chunk_size=64*1024):
"""Moves contents of a directory from one filesystem to another. """Moves contents of a directory from one filesystem to another.
:param fs1: Source filesystem, or a tuple of (<filesystem>, <directory path>) :param fs1: A tuple of (<filesystem>, <directory path>)
:param fs2: Destination filesystem, or a tuple of (<filesystem>, <directory path>) :param fs2: Destination filesystem, or a tuple of (<filesystem>, <directory path>)
:param create_destination: If True, the destination will be created if it doesn't exist
:param ignore_errors: If True, exceptions from file moves are ignored :param ignore_errors: If True, exceptions from file moves are ignored
:param chunk_size: Size of chunks to move if a simple copy is used :param chunk_size: Size of chunks to move if a simple copy is used
""" """
if isinstance(fs1, tuple): if not isinstance(fs1, tuple):
raise ValueError("first argument must be a tuple of (<filesystem>, <path>)")
fs1, dir1 = fs1 fs1, dir1 = fs1
parent_fs1 = fs1
parent_dir1 = dir1
fs1 = fs1.opendir(dir1) fs1 = fs1.opendir(dir1)
print fs1
if parent_dir1 in ('', '/'):
raise DeleteRootError(dir1)
if isinstance(fs2, tuple): if isinstance(fs2, tuple):
fs2, dir2 = fs2 fs2, dir2 = fs2
fs2.makedir(dir2, allow_recreate=True) if create_destination:
fs2.makedir(dir2, allow_recreate=True, recursive=True)
fs2 = fs2.opendir(dir2) fs2 = fs2.opendir(dir2)
mount_fs = MountFS() mount_fs = MountFS(auto_close=False)
mount_fs.mount('src', fs1) mount_fs.mount('src', fs1)
mount_fs.mount('dst', fs2) mount_fs.mount('dst', fs2)
mount_fs.movedir('src', 'dst', mount_fs.copydir('src', 'dst',
overwrite=overwrite, overwrite=True,
ignore_errors=ignore_errors, ignore_errors=ignore_errors,
chunk_size=chunk_size) chunk_size=chunk_size)
parent_fs1.removedir(parent_dir1, force=True)
def copydir(fs1, fs2, overwrite=False, ignore_errors=False, chunk_size=64*1024): def copydir(fs1, fs2, create_destination=True, ignore_errors=False, chunk_size=64*1024):
"""Copies contents of a directory from one filesystem to another. """Copies contents of a directory from one filesystem to another.
:param fs1: Source filesystem, or a tuple of (<filesystem>, <directory path>) :param fs1: Source filesystem, or a tuple of (<filesystem>, <directory path>)
:param fs2: Destination filesystem, or a tuple of (<filesystem>, <directory path>) :param fs2: Destination filesystem, or a tuple of (<filesystem>, <directory path>)
:param create_destination: If True, the destination will be created if it doesn't exist
:param ignore_errors: If True, exceptions from file moves are ignored :param ignore_errors: If True, exceptions from file moves are ignored
:param chunk_size: Size of chunks to move if a simple copy is used :param chunk_size: Size of chunks to move if a simple copy is used
...@@ -228,14 +240,15 @@ def copydir(fs1, fs2, overwrite=False, ignore_errors=False, chunk_size=64*1024): ...@@ -228,14 +240,15 @@ def copydir(fs1, fs2, overwrite=False, ignore_errors=False, chunk_size=64*1024):
fs1 = fs1.opendir(dir1) fs1 = fs1.opendir(dir1)
if isinstance(fs2, tuple): if isinstance(fs2, tuple):
fs2, dir2 = fs2 fs2, dir2 = fs2
fs2.makedir(dir2, allow_recreate=True) if create_destination:
fs2.makedir(dir2, allow_recreate=True, recursive=True)
fs2 = fs2.opendir(dir2) fs2 = fs2.opendir(dir2)
mount_fs = MountFS() mount_fs = MountFS(auto_close=False)
mount_fs.mount('src', fs1) mount_fs.mount('src', fs1)
mount_fs.mount('dst', fs2) mount_fs.mount('dst', fs2)
mount_fs.copydir('src', 'dst', mount_fs.copydir('src', 'dst',
overwrite=overwrite, overwrite=True,
ignore_errors=ignore_errors, ignore_errors=ignore_errors,
chunk_size=chunk_size) chunk_size=chunk_size)
...@@ -519,11 +532,20 @@ def print_fs(fs, path='/', max_levels=5, file_out=None, terminal_colors=None, hi ...@@ -519,11 +532,20 @@ def print_fs(fs, path='/', max_levels=5, file_out=None, terminal_colors=None, hi
if __name__ == "__main__": if __name__ == "__main__":
from fs.memoryfs import MemoryFS from fs.tempfs import TempFS
m = MemoryFS() t1 = TempFS()
m.setcontents("test", "Hello, World!" * 10000) t1.setcontents("foo", "test")
t1.makedir("bar")
f = m.open("test") t1.setcontents("bar/baz", "another test")
print f
tf = wrap_file(f, "r") t1.tree()
print repr(tf.read(10))
t2 = TempFS()
print t2.listdir()
movedir(t1, t2)
print t2.listdir()
t1.tree()
t2.tree()
...@@ -18,6 +18,7 @@ standard unix shell functionality of hiding dot-files in directory listings. ...@@ -18,6 +18,7 @@ standard unix shell functionality of hiding dot-files in directory listings.
import re import re
import sys import sys
import fnmatch import fnmatch
import threading
from fs.base import FS, threading, synchronize, NoDefaultMeta from fs.base import FS, threading, synchronize, NoDefaultMeta
from fs.errors import * from fs.errors import *
...@@ -65,11 +66,11 @@ class WrapFS(FS): ...@@ -65,11 +66,11 @@ class WrapFS(FS):
""" """
def __init__(self, fs): def __init__(self, fs):
super(WrapFS,self).__init__() super(WrapFS, self).__init__()
try: try:
self._lock = fs._lock self._lock = fs._lock
except (AttributeError,FSError): except (AttributeError,FSError):
self._lock = None self._lock = self._lock = threading.RLock()
self.wrapped_fs = fs self.wrapped_fs = fs
def _file_wrap(self, f, mode): def _file_wrap(self, f, mode):
......
...@@ -29,7 +29,7 @@ class LazyFS(WrapFS): ...@@ -29,7 +29,7 @@ class LazyFS(WrapFS):
""" """
def __init__(self, fs): def __init__(self, fs):
super(LazyFS,self).__init__(fs) super(LazyFS, self).__init__(fs)
self._lazy_creation_lock = Lock() self._lazy_creation_lock = Lock()
def __unicode__(self): def __unicode__(self):
......
...@@ -60,27 +60,38 @@ class SubFS(WrapFS): ...@@ -60,27 +60,38 @@ class SubFS(WrapFS):
def removedir(self, path, recursive=False, force=False): def removedir(self, path, recursive=False, force=False):
# Careful not to recurse outside the subdir # Careful not to recurse outside the subdir
path = normpath(path) path = normpath(path)
if path in ("","/"): if path in ('', '/'):
if not force: raise DeleteRootError(path)
for path2 in self.listdir(path):
raise DirectoryNotEmptyError(path)
else:
for path2 in self.listdir(path,absolute=True,files_only=True):
try:
self.remove(path2)
except ResourceNotFoundError:
pass
for path2 in self.listdir(path,absolute=True,dirs_only=True):
try:
self.removedir(path2,force=True)
except ResourceNotFoundError:
pass
else:
super(SubFS,self).removedir(path,force=force) super(SubFS,self).removedir(path,force=force)
if recursive: if recursive:
try: try:
if dirname(path) not in ('', '/'):
self.removedir(dirname(path),recursive=True) self.removedir(dirname(path),recursive=True)
except DirectoryNotEmptyError: except DirectoryNotEmptyError:
pass pass
# if path in ("","/"):
# if not force:
# for path2 in self.listdir(path):
# raise DirectoryNotEmptyError(path)
# else:
# for path2 in self.listdir(path,absolute=True,files_only=True):
# try:
# self.remove(path2)
# except ResourceNotFoundError:
# pass
# for path2 in self.listdir(path,absolute=True,dirs_only=True):
# try:
# self.removedir(path2,force=True)
# except ResourceNotFoundError:
# pass
# else:
# super(SubFS,self).removedir(path,force=force)
# if recursive:
# try:
# if dirname(path):
# self.removedir(dirname(path),recursive=True)
# except DirectoryNotEmptyError:
# pass
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment