Commit 897410bd by rfkelly0

Uniform "overwrite" behaviour for copy/move/copydir/movedir.

New "force" argument to removedir().
parent 6a560274
...@@ -91,6 +91,7 @@ class NoSysPathError(FSError): pass ...@@ -91,6 +91,7 @@ class NoSysPathError(FSError): pass
class PathError(FSError): pass class PathError(FSError): pass
class ResourceLockedError(FSError): pass class ResourceLockedError(FSError): pass
class ResourceNotFoundError(FSError): pass class ResourceNotFoundError(FSError): pass
class DestinationExistsError(FSError): pass
class SystemError(FSError): pass class SystemError(FSError): pass
class ResourceInvalid(FSError): pass class ResourceInvalid(FSError): pass
...@@ -222,6 +223,9 @@ class FS(object): ...@@ -222,6 +223,9 @@ class FS(object):
self._lock = dummy_threading.RLock() self._lock = dummy_threading.RLock()
def __getstate__(self): def __getstate__(self):
# Locks can't be pickled, so instead we just indicate the
# type of lock that should be there. None == no lock,
# True == a proper lock, False == a dummy lock.
state = self.__dict__.copy() state = self.__dict__.copy()
lock = state.get("_lock",None) lock = state.get("_lock",None)
if lock is not None: if lock is not None:
...@@ -351,11 +355,12 @@ class FS(object): ...@@ -351,11 +355,12 @@ class FS(object):
""" """
raise UnsupportedError("UNSUPPORTED") raise UnsupportedError("UNSUPPORTED")
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False, force=False):
"""Remove a directory """Remove a directory
path -- Path of the directory to remove path -- Path of the directory to remove
recursive -- If True, then blank parent directories will be removed recursive -- If True, then blank parent directories will be removed
force -- If True, any directory contents will be removed
""" """
raise UnsupportedError("UNSUPPORTED") raise UnsupportedError("UNSUPPORTED")
...@@ -560,6 +565,8 @@ class FS(object): ...@@ -560,6 +565,8 @@ class FS(object):
if not self.isfile(src): if not self.isfile(src):
raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s") raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s")
if not overwrite and self.exists(dst):
raise DestinationExistsError("COPYFILE_FAILED", src, dst, msg="Destination file exists: %(path2)s")
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)
...@@ -570,9 +577,6 @@ class FS(object): ...@@ -570,9 +577,6 @@ class FS(object):
src_file, dst_file = None, None src_file, dst_file = None, None
try: try:
src_file = self.open(src, "rb") src_file = self.open(src, "rb")
if not overwrite:
if self.exists(dst):
raise OperationFailedError("COPYFILE_FAILED", src, dst, msg="Destination file exists: %(path2)s")
dst_file = self.open(dst, "wb") dst_file = self.open(dst, "wb")
while True: while True:
...@@ -586,11 +590,12 @@ class FS(object): ...@@ -586,11 +590,12 @@ class FS(object):
if dst_file is not None: if dst_file is not None:
dst_file.close() dst_file.close()
def move(self, src, dst, chunk_size=16384): def move(self, src, dst, overwrite=False, chunk_size=16384):
"""Moves a file from one location to another. """Moves a file from one location to another.
src -- Source path src -- Source path
dst -- Destination path dst -- Destination path
overwrite -- If True, then the destination may be overwritten
""" """
...@@ -600,23 +605,28 @@ class FS(object): ...@@ -600,23 +605,28 @@ class FS(object):
if src_syspath is not None and dst_syspath is not None: if src_syspath is not None and dst_syspath is not None:
if not self.isfile(src): if not self.isfile(src):
raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s") raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s")
if not overwrite and self.exists(dst):
raise DestinationExistsError("MOVE_FAILED", src, dst, msg="Destination file exists: %(path2)s")
shutil.move(src_syspath, dst_syspath) shutil.move(src_syspath, dst_syspath)
else: else:
self.copy(src, dst, chunk_size=chunk_size) self.copy(src, dst, overwrite=overwrite, chunk_size=chunk_size)
self.remove(src) self.remove(src)
def movedir(self, src, dst, ignore_errors=False, chunk_size=16384): def movedir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
"""Moves a directory from one location to another. """Moves a directory from one location to another.
src -- Source directory path src -- Source directory path
dst -- Destination directory path dst -- Destination directory path
overwrite -- If True then any existing files in the destination directory will be overwritten
ignore_errors -- If True then this method will ignore FSError exceptions when moving files ignore_errors -- If True then this method will ignore FSError exceptions when moving files
chunk_size -- Size of chunks to use when copying, if a simple copy is required chunk_size -- Size of chunks to use when copying, if a simple copy is required
""" """
if not self.isdir(src): if not self.isdir(src):
raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s") raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s")
if not overwrite and self.exists(dst):
raise DestinationExistsError("MOVEDIR_FAILED", src, dst, msg="Destination exists: %(path2)s")
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)
...@@ -628,10 +638,9 @@ class FS(object): ...@@ -628,10 +638,9 @@ class FS(object):
except WindowsError: except WindowsError:
pass pass
def movefile_noerrors(src, dst, overwrite):
def movefile_noerrors(src, dst):
try: try:
return self.move(src, dst) return self.move(src, dst, overwrite)
except FSError: except FSError:
return return
if ignore_errors: if ignore_errors:
...@@ -650,29 +659,30 @@ class FS(object): ...@@ -650,29 +659,30 @@ class FS(object):
src_filename = pathjoin(dirname, filename) src_filename = pathjoin(dirname, filename)
dst_filename = pathjoin(dst_dirpath, filename) dst_filename = pathjoin(dst_dirpath, filename)
movefile(src_filename, dst_filename, chunk_size=chunk_size) movefile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size)
self.removedir(dirname) self.removedir(dirname)
def copydir(self, src, dst, ignore_errors=False, chunk_size=16384): def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
"""Copies a directory from one location to another. """Copies a directory from one location to another.
src -- Source directory path src -- Source directory path
dst -- Destination directory path dst -- Destination directory path
overwrite -- If True then any existing files in the destination directory will be overwritten
ignore_errors -- If True, exceptions when copying will be ignored ignore_errors -- If True, exceptions when copying will be ignored
chunk_size -- Size of chunks to use when copying, if a simple copy is required chunk_size -- Size of chunks to use when copying, if a simple copy is required
""" """
if not self.isdir(src): if not self.isdir(src):
raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s") raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s")
if not self.isdir(dst): if not overwrite and self.exists(dst):
raise ResourceInvalid("WRONG_TYPE", dst, msg="Source is not a dst: %(path)s") raise DestinationExistsError("COPYDIR_FAILED", dst, msg="Destination exists: %(path)s")
def copyfile_noerrors(src, dst): def copyfile_noerrors(src, dst, overwrite):
try: try:
return self.copy(src, dst) return self.copy(src, dst, overwrite=overwrite)
except FSError: except FSError:
return return
if ignore_errors: if ignore_errors:
...@@ -692,7 +702,7 @@ class FS(object): ...@@ -692,7 +702,7 @@ class FS(object):
src_filename = pathjoin(dirname, filename) src_filename = pathjoin(dirname, filename)
dst_filename = pathjoin(dst_dirpath, filename) dst_filename = pathjoin(dst_dirpath, filename)
copyfile(src_filename, dst_filename, chunk_size=chunk_size) copyfile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size)
def isdirempty(self, path): def isdirempty(self, path):
...@@ -790,8 +800,8 @@ class SubFS(FS): ...@@ -790,8 +800,8 @@ class SubFS(FS):
def remove(self, path): def remove(self, path):
return self.parent.remove(self._delegate(path)) return self.parent.remove(self._delegate(path))
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False,force=False):
self.parent.removedir(self._delegate(path), recursive=recursive) self.parent.removedir(self._delegate(path), recursive=recursive, force=force)
def getinfo(self, path): def getinfo(self, path):
return self.parent.getinfo(self._delegate(path)) return self.parent.getinfo(self._delegate(path))
......
...@@ -370,7 +370,7 @@ class MemoryFS(FS): ...@@ -370,7 +370,7 @@ class MemoryFS(FS):
finally: finally:
self._lock.release() self._lock.release()
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False, force=False):
self._lock.acquire() self._lock.acquire()
try: try:
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
...@@ -382,7 +382,7 @@ class MemoryFS(FS): ...@@ -382,7 +382,7 @@ class MemoryFS(FS):
if not dir_entry.isdir(): if not dir_entry.isdir():
raise ResourceInvalid("WRONG_TYPE", path, msg="Can't remove resource, its not a directory: %(path)s" ) raise ResourceInvalid("WRONG_TYPE", path, msg="Can't remove resource, its not a directory: %(path)s" )
if dir_entry.contents: if dir_entry.contents and not force:
raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s") raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s")
if recursive: if recursive:
......
...@@ -198,7 +198,7 @@ class MountFS(FS): ...@@ -198,7 +198,7 @@ class MountFS(FS):
finally: finally:
self._lock.release() self._lock.release()
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False, force=False):
self._lock.acquire() self._lock.acquire()
try: try:
...@@ -209,10 +209,10 @@ class MountFS(FS): ...@@ -209,10 +209,10 @@ class MountFS(FS):
if fs is None or fs is self: if fs is None or fs is self:
raise OperationFailedError("REMOVEDIR_FAILED", path, msg="Can not removedir for an un-mounted path") raise OperationFailedError("REMOVEDIR_FAILED", path, msg="Can not removedir for an un-mounted path")
if not fs.isdirempty(delegate_path): if not force and not fs.isdirempty(delegate_path):
raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s") raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s")
return fs.removedir(delegate_path, recursive) return fs.removedir(delegate_path, recursive, force)
finally: finally:
self._lock.release() self._lock.release()
...@@ -351,4 +351,4 @@ if __name__ == "__main__": ...@@ -351,4 +351,4 @@ if __name__ == "__main__":
print mountfs.getinfo("1/2") print mountfs.getinfo("1/2")
#print mountfs._delegate('1/2/Memroot/B') #print mountfs._delegate('1/2/Memroot/B')
\ No newline at end of file
...@@ -101,11 +101,16 @@ class OSFS(FS): ...@@ -101,11 +101,16 @@ class OSFS(FS):
except OSError, e: except OSError, e:
raise OperationFailedError("REMOVE_FAILED", path, details=e) raise OperationFailedError("REMOVE_FAILED", path, details=e)
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False,force=False):
sys_path = self.getsyspath(path) sys_path = self.getsyspath(path)
# 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 return
if force:
for path2 in self.listdir(path,absolute=True,files_only=True):
self.remove(path2)
for path2 in self.listdir(path,absolute=True,dirs_only=True):
self.removedir(path2,force=True)
try: try:
os.rmdir(sys_path) os.rmdir(sys_path)
except OSError, e: except OSError, e:
......
...@@ -180,8 +180,8 @@ class RPCFS(FS): ...@@ -180,8 +180,8 @@ class RPCFS(FS):
def remove(self,path): def remove(self,path):
return self.proxy.remove(path) return self.proxy.remove(path)
def removedir(self,path,recursive=False): def removedir(self,path,recursive=False,force=False):
return self.proxy.removedir(path,recursive) return self.proxy.removedir(path,recursive,force)
def rename(self,src,dst): def rename(self,src,dst):
return self.proxy.rename(src,dst) return self.proxy.rename(src,dst)
...@@ -201,14 +201,14 @@ class RPCFS(FS): ...@@ -201,14 +201,14 @@ class RPCFS(FS):
def copy(self,src,dst,overwrite=False,chunk_size=16384): def copy(self,src,dst,overwrite=False,chunk_size=16384):
return self.proxy.copy(src,dst,overwrite,chunk_size) return self.proxy.copy(src,dst,overwrite,chunk_size)
def move(self,src,dst,chunk_size=16384): def move(self,src,dst,overwrite=False,chunk_size=16384):
return self.proxy.move(src,dst,chunk_size) return self.proxy.move(src,dst,overwrite,chunk_size)
def movedir(self,src,dst,ignore_errors=False,chunk_size=16384): def movedir(self,src,dst,overwrite=False,ignore_errors=False,chunk_size=16384):
return self.proxy.movedir(src,dst,ignore_errors,chunk_size) return self.proxy.movedir(src,dst,overwrite,ignore_errors,chunk_size)
def copydir(self,src,dst,ignore_errors=False,chunk_size=16384): def copydir(self,src,dst,overwrite=False,ignore_errors=False,chunk_size=16384):
return self.proxy.copydir(src,dst,ignore_errors,chunk_size) return self.proxy.copydir(src,dst,overwrite,ignore_errors,chunk_size)
class RPCFSInterface(object): class RPCFSInterface(object):
...@@ -246,8 +246,8 @@ class RPCFSInterface(object): ...@@ -246,8 +246,8 @@ class RPCFSInterface(object):
def remove(self,path): def remove(self,path):
return self.fs.remove(path) return self.fs.remove(path)
def removedir(self,path,recursive=False): def removedir(self,path,recursive=False,force=False):
return self.fs.removedir(path,recursive) return self.fs.removedir(path,recursive,force)
def rename(self,src,dst): def rename(self,src,dst):
return self.fs.rename(src,dst) return self.fs.rename(src,dst)
...@@ -267,14 +267,14 @@ class RPCFSInterface(object): ...@@ -267,14 +267,14 @@ class RPCFSInterface(object):
def copy(self,src,dst,overwrite=False,chunk_size=16384): def copy(self,src,dst,overwrite=False,chunk_size=16384):
return self.fs.copy(src,dst,overwrite,chunk_size) return self.fs.copy(src,dst,overwrite,chunk_size)
def move(self,src,dst,chunk_size=16384): def move(self,src,dst,overwrite=False,chunk_size=16384):
return self.fs.move(src,dst,chunk_size) return self.fs.move(src,dst,overwrite,chunk_size)
def movedir(self,src,dst,ignore_errors=False,chunk_size=16384): def movedir(self,src,dst,overwrite=False,ignore_errors=False,chunk_size=16384):
return self.fs.movedir(src,dst,ignore_errors,chunk_size) return self.fs.movedir(src,dst,overwrite,ignore_errors,chunk_size)
def copydir(self,src,dst,ignore_errors=False,chunk_size=16384): def copydir(self,src,dst,overwrite=False,ignore_errors=False,chunk_size=16384):
return self.fs.copydir(src,dst,ignore_errors,chunk_size) return self.fs.copydir(src,dst,overwrite,ignore_errors,chunk_size)
class RPCFSServer(SimpleXMLRPCServer): class RPCFSServer(SimpleXMLRPCServer):
......
...@@ -126,7 +126,7 @@ class S3FS(FS): ...@@ -126,7 +126,7 @@ class S3FS(FS):
Since S3 only offers "eventual consistency" of data, it is possible Since S3 only offers "eventual consistency" of data, it is possible
to create a key but be unable to read it back straight away. This to create a key but be unable to read it back straight away. This
method works around that limitation by polling the key until it reads method works around that limitation by polling the key until it reads
back the expected by the given key. back the value expected by the given key.
Note that this could easily fail if the key is modified by another Note that this could easily fail if the key is modified by another
program, meaning the content will never be as specified in the given program, meaning the content will never be as specified in the given
...@@ -273,7 +273,6 @@ class S3FS(FS): ...@@ -273,7 +273,6 @@ class S3FS(FS):
paths.append(nm) paths.append(nm)
if not isDir: if not isDir:
if s3path != self._prefix: if s3path != self._prefix:
print "NOT A DIR:", s3path
raise OperationFailedError("LISTDIR_FAILED",path) raise OperationFailedError("LISTDIR_FAILED",path)
return self._listdir_helper(path,paths,wildcard,full,absolute,hidden,dirs_only,files_only) return self._listdir_helper(path,paths,wildcard,full,absolute,hidden,dirs_only,files_only)
...@@ -354,19 +353,26 @@ class S3FS(FS): ...@@ -354,19 +353,26 @@ class S3FS(FS):
while k: while k:
k = self._s3bukt.get_key(s3path) k = self._s3bukt.get_key(s3path)
def removedir(self,path,recursive=False): def removedir(self,path,recursive=False,force=False):
"""Remove the directory at the given path.""" """Remove the directory at the given path."""
s3path = self._s3path(path) + self._separator s3path = self._s3path(path) + self._separator
ks = self._s3bukt.list(prefix=s3path,delimiter=self._separator) if force:
# Fail if the directory is not empty # If we will be forcibly removing any directory contents, we
# might as well get the un-delimited list straight away.
ks = self._s3bukt.list(prefix=s3path)
else:
ks = self._s3bukt.list(prefix=s3path,delimiter=self._separator)
# Fail if the directory is not empty, or remove them if forced
for k in ks: for k in ks:
if k.name != s3path: if k.name != s3path:
raise OperationFailedError("REMOVEDIR_FAILED",path) if not force:
raise OperationFailedError("REMOVEDIR_FAILED",path)
self._s3bukt.delete_key(k.name)
self._s3bukt.delete_key(s3path) self._s3bukt.delete_key(s3path)
if recursive: if recursive:
pdir = dirname(path) pdir = dirname(path)
try: try:
self.removedir(pdir,True) self.removedir(pdir,recursive=True,force=False)
except OperationFailedError: except OperationFailedError:
pass pass
...@@ -408,11 +414,11 @@ class S3FS(FS): ...@@ -408,11 +414,11 @@ class S3FS(FS):
# It exists as a regular file # It exists as a regular file
if k.name == s3path_dst: if k.name == s3path_dst:
if not overwrite: if not overwrite:
raise OperationFailedError("COPYFILE_FAILED",src,dst,msg="Destination file exists: %(path2)s") raise DestinationExistsError("COPYFILE_FAILED",src,dst,msg="Destination file exists: %(path2)s")
dstOK = True dstOK = True
break break
# Check if it refers to a directory. If so, we copy *into* it. # Check if it refers to a directory. If so, we copy *into* it.
# Since S3 lists in lexicographic order, subsequence iterations # Since S3 lists in lexicographic order, subsequent iterations
# of the loop will check for the existence of the new filename. # of the loop will check for the existence of the new filename.
if k.name == s3path_dstD: if k.name == s3path_dstD:
nm = resourcename(src) nm = resourcename(src)
...@@ -433,8 +439,8 @@ class S3FS(FS): ...@@ -433,8 +439,8 @@ class S3FS(FS):
k = self._s3bukt.get_key(s3path_dst) k = self._s3bukt.get_key(s3path_dst)
self._sync_key(k) self._sync_key(k)
def move(self,src,dst,chunk_size=16384): def move(self,src,dst,overwrite=False,chunk_size=16384):
"""Move a file from one location to another.""" """Move a file from one location to another."""
self.copy(src,dst) self.copy(src,dst,overwrite=overwrite)
self._s3bukt.delete_key(self._s3path(src)) self._s3bukt.delete_key(self._s3path(src))
...@@ -196,6 +196,12 @@ class TestOSFS(unittest.TestCase): ...@@ -196,6 +196,12 @@ class TestOSFS(unittest.TestCase):
self.assert_(not check("foo/bar")) self.assert_(not check("foo/bar"))
self.assert_(not check("foo")) self.assert_(not check("foo"))
self.fs.makedir("frollic/waggle", recursive=True)
self.fs.createfile("frollic/waddle.txt","waddlewaddlewaddle")
self.assertRaises(fs.OperationFailedError,self.fs.removedir,"frollic")
self.fs.removedir("frollic",force=True)
self.assert_(not check("frollic"))
def test_listdir(self): def test_listdir(self):
def makefile(fname): def makefile(fname):
...@@ -295,6 +301,15 @@ class TestOSFS(unittest.TestCase): ...@@ -295,6 +301,15 @@ class TestOSFS(unittest.TestCase):
self.assert_(check("/c.txt")) self.assert_(check("/c.txt"))
self.assert_(checkcontents("/c.txt")) self.assert_(checkcontents("/c.txt"))
makefile("foo/bar/a.txt")
self.assertRaises(fs.DestinationExistsError,self.fs.move,"foo/bar/a.txt","/c.txt")
self.assert_(check("foo/bar/a.txt"))
self.assert_(check("/c.txt"))
self.fs.move("foo/bar/a.txt","/c.txt",overwrite=True)
self.assert_(not check("foo/bar/a.txt"))
self.assert_(check("/c.txt"))
def test_movedir(self): def test_movedir(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = "If the implementation is hard to explain, it's a bad idea."
...@@ -311,7 +326,6 @@ class TestOSFS(unittest.TestCase): ...@@ -311,7 +326,6 @@ class TestOSFS(unittest.TestCase):
self.fs.makedir("a/foo/bar", recursive=True) self.fs.makedir("a/foo/bar", recursive=True)
makefile("a/foo/bar/baz.txt") makefile("a/foo/bar/baz.txt")
self.fs.makedir("copy of a")
self.fs.movedir("a", "copy of a") self.fs.movedir("a", "copy of a")
self.assert_(check("copy of a/1.txt")) self.assert_(check("copy of a/1.txt"))
...@@ -327,6 +341,51 @@ class TestOSFS(unittest.TestCase): ...@@ -327,6 +341,51 @@ class TestOSFS(unittest.TestCase):
self.assert_(not check("a/foo")) self.assert_(not check("a/foo"))
self.assert_(not check("a")) self.assert_(not check("a"))
self.fs.makedir("a")
self.assertRaises(fs.DestinationExistsError,self.fs.movedir,"copy of a","a")
self.fs.movedir("copy of a","a",overwrite=True)
self.assert_(not check("copy of a"))
self.assert_(check("a/1.txt"))
self.assert_(check("a/2.txt"))
self.assert_(check("a/3.txt"))
self.assert_(check("a/foo/bar/baz.txt"))
def test_copyfile(self):
check = self.check
contents = "If the implementation is hard to explain, it's a bad idea."
def makefile(path,contents=contents):
f = self.fs.open(path, "wb")
f.write(contents)
f.close()
def checkcontents(path,contents=contents):
f = self.fs.open(path, "rb")
check_contents = f.read()
f.close()
self.assertEqual(check_contents,contents)
return contents == check_contents
self.fs.makedir("foo/bar", recursive=True)
makefile("foo/bar/a.txt")
self.assert_(check("foo/bar/a.txt"))
self.assert_(checkcontents("foo/bar/a.txt"))
self.fs.copy("foo/bar/a.txt", "foo/b.txt")
self.assert_(check("foo/bar/a.txt"))
self.assert_(check("foo/b.txt"))
self.assert_(checkcontents("foo/b.txt"))
self.fs.copy("foo/b.txt", "c.txt")
self.assert_(check("foo/b.txt"))
self.assert_(check("/c.txt"))
self.assert_(checkcontents("/c.txt"))
makefile("foo/bar/a.txt","different contents")
self.assertRaises(fs.DestinationExistsError,self.fs.copy,"foo/bar/a.txt","/c.txt")
self.assert_(checkcontents("/c.txt"))
self.fs.copy("foo/bar/a.txt","/c.txt",overwrite=True)
self.assert_(checkcontents("foo/bar/a.txt","different contents"))
self.assert_(checkcontents("/c.txt","different contents"))
def test_copydir(self): def test_copydir(self):
check = self.check check = self.check
...@@ -344,7 +403,6 @@ class TestOSFS(unittest.TestCase): ...@@ -344,7 +403,6 @@ class TestOSFS(unittest.TestCase):
self.fs.makedir("a/foo/bar", recursive=True) self.fs.makedir("a/foo/bar", recursive=True)
makefile("a/foo/bar/baz.txt") makefile("a/foo/bar/baz.txt")
self.fs.makedir("copy of a")
self.fs.copydir("a", "copy of a") self.fs.copydir("a", "copy of a")
self.assert_(check("copy of a/1.txt")) self.assert_(check("copy of a/1.txt"))
self.assert_(check("copy of a/2.txt")) self.assert_(check("copy of a/2.txt"))
...@@ -356,6 +414,14 @@ class TestOSFS(unittest.TestCase): ...@@ -356,6 +414,14 @@ class TestOSFS(unittest.TestCase):
self.assert_(check("a/3.txt")) self.assert_(check("a/3.txt"))
self.assert_(check("a/foo/bar/baz.txt")) self.assert_(check("a/foo/bar/baz.txt"))
self.assertRaises(fs.DestinationExistsError,self.fs.copydir,"a","b")
self.fs.copydir("a","b",overwrite=True)
self.assert_(check("b/1.txt"))
self.assert_(check("b/2.txt"))
self.assert_(check("b/3.txt"))
self.assert_(check("b/foo/bar/baz.txt"))
def test_copydir_with_hidden(self): def test_copydir_with_hidden(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = "If the implementation is hard to explain, it's a bad idea."
...@@ -369,7 +435,6 @@ class TestOSFS(unittest.TestCase): ...@@ -369,7 +435,6 @@ class TestOSFS(unittest.TestCase):
makefile("a/2.txt") makefile("a/2.txt")
makefile("a/.hidden.txt") makefile("a/.hidden.txt")
self.fs.makedir("copy of a")
self.fs.copydir("a", "copy of a") self.fs.copydir("a", "copy of a")
self.assert_(check("copy of a/1.txt")) self.assert_(check("copy of a/1.txt"))
self.assert_(check("copy of a/2.txt")) self.assert_(check("copy of a/2.txt"))
...@@ -671,7 +736,7 @@ class TestS3FS(TestOSFS): ...@@ -671,7 +736,7 @@ class TestS3FS(TestOSFS):
def test_with_statement(self): def test_with_statement(self):
import sys import sys
if sys.version_info[0] >= 2 and sys.version_info[1] >= 5: if sys.version_info[0] >= 2 and sys.version_info[1] >= 5:
# A successful with statement # A successful 'with' statement
contents = "testing the with statement" contents = "testing the with statement"
code = "from __future__ import with_statement\n" code = "from __future__ import with_statement\n"
code += "with self.fs.open('f.txt','w-') as testfile:\n" code += "with self.fs.open('f.txt','w-') as testfile:\n"
...@@ -679,7 +744,7 @@ class TestS3FS(TestOSFS): ...@@ -679,7 +744,7 @@ class TestS3FS(TestOSFS):
code += "self.assertEquals(self.fs.getcontents('f.txt'),contents)" code += "self.assertEquals(self.fs.getcontents('f.txt'),contents)"
code = compile(code,"<string>",'exec') code = compile(code,"<string>",'exec')
eval(code) eval(code)
# A with statement raising an error # A 'with' statement raising an error
contents = "testing the with statement" contents = "testing the with statement"
code = "from __future__ import with_statement\n" code = "from __future__ import with_statement\n"
code += "with self.fs.open('f.txt','w-') as testfile:\n" code += "with self.fs.open('f.txt','w-') as testfile:\n"
...@@ -688,7 +753,7 @@ class TestS3FS(TestOSFS): ...@@ -688,7 +753,7 @@ class TestS3FS(TestOSFS):
code = compile(code,"<string>",'exec') code = compile(code,"<string>",'exec')
self.assertRaises(ValueError,eval,code,globals(),locals()) self.assertRaises(ValueError,eval,code,globals(),locals())
self.assertEquals(self.fs.getcontents('f.txt'),contents) self.assertEquals(self.fs.getcontents('f.txt'),contents)
import rpcfs import rpcfs
......
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