Commit 4f40e842 by willmcgugan

Work in progress. More tests. Move helper function in to new helpers.py file.

parent a6c0aa73
#!/usr/bin/env python #!/usr/bin/env python
from helpers import *
import os import os
import os.path import os.path
import shutil import shutil
import fnmatch import fnmatch
from itertools import chain
import datetime import datetime
try: try:
import threading import threading
...@@ -20,6 +20,7 @@ error_msgs = { ...@@ -20,6 +20,7 @@ error_msgs = {
# OperationFailedError # OperationFailedError
"LISTDIR_FAILED" : "Unable to get directory listing: %(path)s", "LISTDIR_FAILED" : "Unable to get directory listing: %(path)s",
"MAKEDIR_FAILED" : "Unable to create directory: %(path)s",
"DELETE_FAILED" : "Unable to delete file: %(path)s", "DELETE_FAILED" : "Unable to delete file: %(path)s",
"RENAME_FAILED" : "Unable to rename file: %(path)s", "RENAME_FAILED" : "Unable to rename file: %(path)s",
"OPEN_FAILED" : "Unable to open file: %(path)s", "OPEN_FAILED" : "Unable to open file: %(path)s",
...@@ -86,6 +87,25 @@ class ResourceNotFoundError(FSError): pass ...@@ -86,6 +87,25 @@ class ResourceNotFoundError(FSError): pass
class SystemError(FSError): pass class SystemError(FSError): pass
class ResourceInvalid(FSError): pass class ResourceInvalid(FSError): pass
def silence_fserrors(f, *args, **kwargs):
try:
return f(*args, **kwargs)
except FSError:
return None
def _iteratepath(path, numsplits=None):
path = resolvepath(path)
if not path:
return []
if numsplits == None:
return filter(lambda p:bool(p), path.split('/'))
else:
return filter(lambda p:bool(p), path.split('/', numsplits))
class NullFile(object): class NullFile(object):
...@@ -136,139 +156,6 @@ class NullFile(object): ...@@ -136,139 +156,6 @@ class NullFile(object):
pass pass
def isabsolutepath(path):
"""Returns True if a given path is absolute.
>>> isabsolutepath("a/b/c")
False
>>> isabsolutepath("/foo/bar")
True
"""
if path:
return path[0] in '\\/'
return False
def normpath(path):
"""Normalizes a path to be in the formated expected by FS objects.
Returns a new path string.
>>> normpath(r"foo\\bar\\baz")
'foo/bar/baz'
"""
return path.replace('\\', '/')
def pathjoin(*paths):
"""Joins any number of paths together. Returns a new path string.
paths -- An iterable of path strings
>>> pathjoin('foo', 'bar', 'baz')
'foo/bar/baz'
>>> pathjoin('foo/bar', '../baz')
'foo/baz'
"""
absolute = False
relpaths = []
for p in paths:
if p:
if p[0] in '\\/':
del relpaths[:]
absolute = True
relpaths.append(p)
pathstack = []
for component in chain(*(normpath(path).split('/') for path in relpaths)):
if component == "..":
if not pathstack:
raise PathError("INVALID_PATH", repr(paths), msg="relative path is invalid")
sub = pathstack.pop()
elif component == ".":
pass
elif component:
pathstack.append(component)
if absolute:
return "/" + "/".join(pathstack)
else:
return "/".join(pathstack)
def pathsplit(path):
"""Splits a path on a path separator. Returns a tuple containing the path up
to that last separator and the remaining path component.
>>> pathsplit("foo/bar")
('foo', 'bar')
>>> pathsplit("foo/bar/baz")
('foo/bar', 'baz')
"""
split = normpath(path).rsplit('/', 1)
if len(split) == 1:
return ('', split[0])
return tuple(split)
def resolvepath(path):
"""Normalises the path and removes any relative path components.
path -- A path string
>>> resolvepath(r"foo\\bar\\..\\baz")
'foo/baz'
"""
return pathjoin(path)
def makerelative(path):
"""Makes a path relative by removing initial separator.
path -- A path
>>> makerelative("/foo/bar")
'foo/bar'
"""
path = normpath(path)
if path.startswith('/'):
return path[1:]
return path
def makeabsolute(path):
"""Makes a path absolute by adding a separater at the beginning of the path.
path -- A path
>>> makeabsolute("foo/bar/baz")
'/foo/bar/baz'
"""
path = normpath(path)
if not path.startswith('/'):
return '/'+path
return path
def _iteratepath(path, numsplits=None):
path = resolvepath(path)
if not path:
return []
if numsplits == None:
return filter(lambda p:bool(p), path.split('/'))
else:
return filter(lambda p:bool(p), path.split('/', numsplits))
def print_fs(fs, path="/", max_levels=5, indent=' '*2): def print_fs(fs, path="/", max_levels=5, indent=' '*2):
"""Prints a filesystem listing to stdout (including sub dirs). Useful as a debugging aid. """Prints a filesystem listing to stdout (including sub dirs). Useful as a debugging aid.
Be careful about printing a OSFS, or any other large filesystem. Be careful about printing a OSFS, or any other large filesystem.
...@@ -352,7 +239,7 @@ class FS(object): ...@@ -352,7 +239,7 @@ class FS(object):
raise NoSysPathError("NO_SYS_PATH", path) raise NoSysPathError("NO_SYS_PATH", path)
return None return None
def open(self, path, mode="r", buffering=-1, **kwargs): def open(self, path, mode="r", **kwargs):
raise UnsupportedError("UNSUPPORTED") raise UnsupportedError("UNSUPPORTED")
def safeopen(self, *args, **kwargs): def safeopen(self, *args, **kwargs):
...@@ -404,7 +291,7 @@ class FS(object): ...@@ -404,7 +291,7 @@ class FS(object):
""" """
raise UnsupportedError("UNSUPPORTED") raise UnsupportedError("UNSUPPORTED")
def makedir(self, path, mode=0777, recursive=False): def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
raise UnsupportedError("UNSUPPORTED") raise UnsupportedError("UNSUPPORTED")
def remove(self, path): def remove(self, path):
...@@ -443,7 +330,7 @@ class FS(object): ...@@ -443,7 +330,7 @@ class FS(object):
else: else:
return "OS file, maps to %s" % sys_path return "OS file, maps to %s" % sys_path
def open(self, path, mode="r", buffering=-1, **kwargs): def open(self, path, mode="r", **kwargs):
raise UNSUPPORTED_ERROR("UNSUPPORTED") raise UNSUPPORTED_ERROR("UNSUPPORTED")
def opendir(self, path): def opendir(self, path):
...@@ -556,7 +443,7 @@ class FS(object): ...@@ -556,7 +443,7 @@ class FS(object):
raise OperationFailedError("GETSIZE_FAILED", path) raise OperationFailedError("GETSIZE_FAILED", path)
return size return size
def copyfile(self, src, dst, overwrite=False, chunk_size=1024*16384): def copy(self, src, dst, overwrite=False, chunk_size=1024*16384):
"""Copies a file from src to dst. """Copies a file from src to dst.
...@@ -569,13 +456,14 @@ class FS(object): ...@@ -569,13 +456,14 @@ class FS(object):
""" """
src_syspath = self.getsyspath(src, allow_none=True) if self.isdir(dst):
dst_syspath = self.getsyspath(dst, allow_none=True) dst = pathjoin( getroot(dst), getresource(src) )
if not self.isdir(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 self.isdir(dst):
raise ResourceInvalid("WRONG_TYPE", dst, msg="Source is not a file: %(path)s") src_syspath = self.getsyspath(src, allow_none=True)
dst_syspath = self.getsyspath(dst, allow_none=True)
if src_syspath is not None and dst_syspath is not None: if src_syspath is not None and dst_syspath is not None:
shutil.copyfile(src_syspath, dst_syspath) shutil.copyfile(src_syspath, dst_syspath)
...@@ -586,7 +474,7 @@ class FS(object): ...@@ -586,7 +474,7 @@ class FS(object):
if not overwrite: if not overwrite:
if self.exists(dst): if self.exists(dst):
raise OperationFailedError("COPYFILE_FAILED", src, dst, msg="Destination file exists: %(path2)s") raise OperationFailedError("COPYFILE_FAILED", src, dst, msg="Destination file exists: %(path2)s")
dst_file = self.open(src, "wb") dst_file = self.open(dst, "wb")
while True: while True:
chunk = src_file.read(chunk_size) chunk = src_file.read(chunk_size)
...@@ -599,7 +487,7 @@ class FS(object): ...@@ -599,7 +487,7 @@ class FS(object):
if dst_file is not None: if dst_file is not None:
dst_file.close() dst_file.close()
def movefile(self, src, dst): def move(self, src, dst):
"""Moves a file from one location to another. """Moves a file from one location to another.
...@@ -612,16 +500,64 @@ class FS(object): ...@@ -612,16 +500,64 @@ class FS(object):
dst_syspath = self.getsyspath(dst, allow_none=True) dst_syspath = self.getsyspath(dst, allow_none=True)
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.isdir(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 self.isdir(dst):
raise ResourceInvalid("WRONG_TYPE", dst, msg="Source is not a file: %(path)s")
shutil.move(src_syspath, dst_syspath) shutil.move(src_syspath, dst_syspath)
else: else:
self.copyfile(src, dst) self.copy(src, dst)
self.remove(src) self.remove(src)
def movedir(self, src, dst):
"""Moves a directory from one location to another.
src -- Source directory path
dst -- Destination directory path
"""
src_syspath = self.getsyspath(src, allow_none=True)
dst_syspath = self.getsyspath(dst, allow_none=True)
if not self.isdir(src):
raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s")
if not self.isdir(dst):
raise ResourceInvalid("WRONG_TYPE", dst, msg="Source is not a dst: %(path)s")
if src_syspath is not None and dst_syspath is not None:
shutil.move(src_syspath, dst_syspath)
else:
movefile = self.move
silence_fserrors(self.makedir, dst)
for dirname, filename in self.walk(src):
silence_fserrors(self.makedir, dirname)
src_filename = pathjoin(dirname, filename)
dst_filename = pathjoin(dst, dirname)
movefile(src_filename, dst_filename)
def isdirempty(self, path):
"""Return True if a path contains no files.
path -- Path of a directory
"""
path = normpath(path)
iter_dir = iter(self.listdir(path))
try:
iter_dir.next()
except StopIteration:
return True
return False
class SubFS(FS): class SubFS(FS):
"""A SubFS represents a sub directory of another filesystem object. """A SubFS represents a sub directory of another filesystem object.
...@@ -646,11 +582,11 @@ class SubFS(FS): ...@@ -646,11 +582,11 @@ class SubFS(FS):
def _delegate(self, path): def _delegate(self, path):
return pathjoin(self.sub_dir, resolvepath(makerelative(path))) return pathjoin(self.sub_dir, resolvepath(makerelative(path)))
def getsyspath(self, path): def getsyspath(self, path, allow_none=False):
return self.parent.getsyspath(self._delegate(path)) return self.parent.getsyspath(self._delegate(path), allow_none=allow_none)
def open(self, path, mode="r", buffering=-1, **kwargs): def open(self, path, mode="r", **kwargs):
return self.parent.open(self._delegate(path), mode, buffering) return self.parent.open(self._delegate(path), mode)
def exists(self, path): def exists(self, path):
return self.parent.exists(self._delegate(path)) return self.parent.exists(self._delegate(path))
...@@ -667,7 +603,7 @@ class SubFS(FS): ...@@ -667,7 +603,7 @@ class SubFS(FS):
return self.parent.isdir(self._delegate(path)) return self.parent.isdir(self._delegate(path))
def isfile(self, path): def isfile(self, path):
return self.parent.isdir(self._delegate(path)) return self.parent.isfile(self._delegate(path))
def ishidden(self, path): def ishidden(self, path):
return self.parent.ishidden(self._delegate(path)) return self.parent.ishidden(self._delegate(path))
...@@ -689,8 +625,8 @@ class SubFS(FS): ...@@ -689,8 +625,8 @@ class SubFS(FS):
return paths return paths
def makedir(self, path, mode=0777, recursive=False): def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
return self.parent.makedir(self._delegate(path), mode=mode, recursive=recursive) return self.parent.makedir(self._delegate(path), mode=mode, recursive=recursive, allow_recreate=allow_recreate)
def remove(self, path): def remove(self, path):
return self.parent.remove(self._delegate(path)) return self.parent.remove(self._delegate(path))
...@@ -715,5 +651,7 @@ if __name__ == "__main__": ...@@ -715,5 +651,7 @@ if __name__ == "__main__":
fs1 = osfs.OSFS('~/') fs1 = osfs.OSFS('~/')
fs2 = fs1.opendir("projects").opendir('prettycharts') fs2 = fs1.opendir("projects").opendir('prettycharts')
#browsewin.browse(fs1) #browsewin.browse(fs1)
browsewin.browse(fs2) browsewin.browse(fs2)
\ No newline at end of file
from itertools import chain
def isabsolutepath(path):
"""Returns True if a given path is absolute.
>>> isabsolutepath("a/b/c")
False
>>> isabsolutepath("/foo/bar")
True
"""
if path:
return path[0] in '\\/'
return False
def normpath(path):
"""Normalizes a path to be in the formated expected by FS objects.
Returns a new path string.
>>> normpath(r"foo\\bar\\baz")
'foo/bar/baz'
"""
return path.replace('\\', '/')
def pathjoin(*paths):
"""Joins any number of paths together. Returns a new path string.
paths -- An iterable of path strings
>>> pathjoin('foo', 'bar', 'baz')
'foo/bar/baz'
>>> pathjoin('foo/bar', '../baz')
'foo/baz'
"""
absolute = False
relpaths = []
for p in paths:
if p:
if p[0] in '\\/':
del relpaths[:]
absolute = True
relpaths.append(p)
pathstack = []
for component in chain(*(normpath(path).split('/') for path in relpaths)):
if component == "..":
if not pathstack:
raise ValueError("Relative path is invalid")
sub = pathstack.pop()
elif component == ".":
pass
elif component:
pathstack.append(component)
if absolute:
return "/" + "/".join(pathstack)
else:
return "/".join(pathstack)
def pathsplit(path):
"""Splits a path on a path separator. Returns a tuple containing the path up
to that last separator and the remaining path component.
>>> pathsplit("foo/bar")
('foo', 'bar')
>>> pathsplit("foo/bar/baz")
('foo/bar', 'baz')
"""
split = normpath(path).rsplit('/', 1)
if len(split) == 1:
return ('', split[0])
return tuple(split)
def getroot(path):
return pathsplit(path)[0]
def getresourcename(path):
return pathsplit(path)[1]
def resolvepath(path):
"""Normalises the path and removes any relative path components.
path -- A path string
>>> resolvepath(r"foo\\bar\\..\\baz")
'foo/baz'
"""
return pathjoin(path)
def makerelative(path):
"""Makes a path relative by removing initial separator.
path -- A path
>>> makerelative("/foo/bar")
'foo/bar'
"""
path = normpath(path)
if path.startswith('/'):
return path[1:]
return path
def makeabsolute(path):
"""Makes a path absolute by adding a separater at the beginning of the path.
path -- A path
>>> makeabsolute("foo/bar/baz")
'/foo/bar/baz'
"""
path = normpath(path)
if not path.startswith('/'):
return '/'+path
return path
def issamedir(path1, path2):
dirname1, name1 = pathsplit(path1)
dirname2, name2 = pathsplit(path2)
return dirname1 == dirname2
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import os import os
import datetime import datetime
from fs import _iteratepath from fs import _iteratepath
from fs import *
try: try:
from cStringIO import StringIO from cStringIO import StringIO
...@@ -95,6 +96,9 @@ class MemoryFS(FS): ...@@ -95,6 +96,9 @@ class MemoryFS(FS):
class DirEntry(object): class DirEntry(object):
def __init__(self, type, name, contents=None): def __init__(self, type, name, contents=None):
assert type in ("dir", "file"), "Type must be dir or file!"
self.type = type self.type = type
self.name = name self.name = name
self.permissions = None self.permissions = None
...@@ -214,7 +218,7 @@ class MemoryFS(FS): ...@@ -214,7 +218,7 @@ class MemoryFS(FS):
else: else:
if not allow_recreate: if not allow_recreate:
if dirname in parent_dir.contents: if dirname in parent_dir.contents:
raise ResourceNotFoundError("NO_DIR", dirname, msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s") raise OperationFailedError("MAKEDIR_FAILED", dirname, msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s")
current_dir = self.root current_dir = self.root
for path_component in _iteratepath(dirpath)[:-1]: for path_component in _iteratepath(dirpath)[:-1]:
...@@ -354,7 +358,7 @@ class MemoryFS(FS): ...@@ -354,7 +358,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 not recursive and len(dir_entry.contents): if dir_entry.contents:
raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s") raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s")
if recursive: if recursive:
...@@ -372,6 +376,8 @@ class MemoryFS(FS): ...@@ -372,6 +376,8 @@ class MemoryFS(FS):
self._lock.release() self._lock.release()
def rename(self, src, dst): def rename(self, src, dst):
if not issamedir(src, dst):
raise ValueError("Destination path must the same directory (user the move method for moving to a different directory)")
self._lock.acquire() self._lock.acquire()
try: try:
dir_entry = self._get_dir_entry(src) dir_entry = self._get_dir_entry(src)
......
#!/usr/bin/env python #!/usr/bin/env python
from fs import FS, FSError, pathjoin, pathsplit, print_fs, _iteratepath, normpath, makeabsolute, makerelative, _synchronize from fs import *
from objecttree import ObjectTree from objecttree import ObjectTree
from memoryfs import MemoryFS from memoryfs import MemoryFS
...@@ -124,6 +124,17 @@ class MountFS(FS): ...@@ -124,6 +124,17 @@ class MountFS(FS):
finally: finally:
self._lock.release() self._lock.release()
def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
path = normpath(path)
self._lock.acquire()
try:
fs, mount_path, delegate_path = self._delegate(path)
if fs is self:
raise UnsupportedError("UNSUPPORTED", msg="Can only makedir for mounted paths" )
return fs.makedir(delegate_path, mode, recursive=recursive, allow_recreate=allow_recreate)
finally:
self._lock.release()
def open(self, path, mode="r", **kwargs): def open(self, path, mode="r", **kwargs):
self._lock.acquire() self._lock.acquire()
...@@ -163,6 +174,20 @@ class MountFS(FS): ...@@ -163,6 +174,20 @@ class MountFS(FS):
finally: finally:
self._lock.release() self._lock.release()
def remove(self, path):
self._lock.acquire()
try:
path = normpath(path)
fs, mount_path, delegate_path = self._delegate(path)
if fs is None:
raise ResourceNotFoundError("NO_FILE", path)
if fs is self:
raise UnsupportedError("UNSUPPORTED", msg="Can only remove paths within a mounted dir" )
return fs.remove(delegate_path)
finally:
self._lock.release()
def removedir(self, path, recursive=False): def removedir(self, path, recursive=False):
self._lock.acquire() self._lock.acquire()
...@@ -171,27 +196,26 @@ class MountFS(FS): ...@@ -171,27 +196,26 @@ class MountFS(FS):
path = normpath(path) path = normpath(path)
fs, mount_path, delegate_path = self._delegate(path) fs, mount_path, delegate_path = self._delegate(path)
if fs is not self: if fs is None or fs is self:
return fs.removedir(delegate_path, path) raise OperationFailedError("REMOVEDIR_FAILED", path, msg="Can not removedir for an un-mounted path")
object = self.mount_tree.get(path, None)
if object is None or not isinstance(object, dict):
raise ResourceNotFound("NO_DIR", path)
if not recursive and len(object): if not fs.isdirempty(delegate_path):
raise OperationFailedError("REMOVEDIR_FAILED", path) raise OperationFailedError("REMOVEDIR_FAILED", "Directory is not empty: %(path)s")
del self.mount_tree[delegate_path] return fs.removedir(delegate_path, recursive)
finally: finally:
self._lock.release() self._lock.release()
def rename(self, src, dst): def rename(self, src, dst):
if not issamedir(src, dst):
raise ValueError("Destination path must the same directory (user the move method for moving to a different directory)")
self._lock.acquire() self._lock.acquire()
try: try:
fs1, mount_path1, delegate_path1 = self._delegate(path) fs1, mount_path1, delegate_path1 = self._delegate(src)
fs2, mount_path2, delegate_path2 = self._delegate(path) fs2, mount_path2, delegate_path2 = self._delegate(dst)
if fs1 is not fs2: if fs1 is not fs2:
raise OperationFailedError("RENAME_FAILED", src) raise OperationFailedError("RENAME_FAILED", src)
...@@ -273,123 +297,11 @@ class MountFS(FS): ...@@ -273,123 +297,11 @@ class MountFS(FS):
size = self.mount_tree[path].info_callable(path).get("size", None) size = self.mount_tree[path].info_callable(path).get("size", None)
return size return size
return fs.getinfo(delegate_path).getsize() return fs.getinfo(delegate_path).get("size", None)
except: except:
self._lock.release() self._lock.release()
#
#class MountFS(FS):
#
# class Mount(object):
# def __init__(self, path, memory_fs, value, mode):
# self.path = path
# memory_fs._on_close_memory_file(path, self)
# self.fs = None
#
# def __str__(self):
# return "Mount pont: %s, %s" % (self.path, str(self.fs))
#
# def get_mount(self, path, memory_fs, value, mode):
#
# dir_entry = memory_fs._get_dir_entry(path)
# if dir_entry is None or dir_entry.data is None:
# return MountFS.Mount(path, memory_fs, value, mode)
# else:
# return dir_entry.data
#
# def __init__(self):
# self.mounts = {}
# self.mem_fs = MemoryFS(file_factory=self.get_mount)
#
# def _delegate(self, path):
# path_components = list(_iteratepath(path))
#
# current_dir = self.mem_fs.root
# for i, path_component in enumerate(path_components):
#
# if current_dir is None:
# return None, None, None
#
# if '.mount' in current_dir.contents:
# break
#
# dir_entry = current_dir.contents.get(path_component, None)
# current_dir = dir_entry
# else:
# i = len(path_components)
#
# if '.mount' in current_dir.contents:
#
# mount_point = '/'.join(path_components[:i])
# mount_filename = pathjoin(mount_point, '.mount')
#
# mount = self.mem_fs.open(mount_filename, 'r')
# delegate_path = '/'.join(path_components[i:])
# return mount.fs, mount_point, delegate_path
#
# return self, "", path
#
# def desc(self, path):
# fs, mount_path, delegate_path = self._delegate(path)
# if fs is self:
# return "Mount dir"
#
# return "Mounted dir, maps to path %s on %s" % (delegate_path, str(fs))
#
# def isdir(self, path):
# fs, mount_path, delegate_path = self._delegate(path)
# if fs is None:
# return False
#
# if fs is self:
# return True
# else:
# return fs.isdir(delegate_path)
#
# def listdir(self, path="/", wildcard=None, full=False, absolute=False, hidden=False, dirs_only=False, files_only=False):
# fs, mount_path, delegate_path = self._delegate(path)
#
# if fs is None:
# raise ResourceNotFoundError("NO_DIR", path)
#
# if fs is self:
# if files_only:
# return []
# return self.mem_fs.listdir(path,
# wildcard=wildcard,
# full=full,
# absolute=absolute,
# hidden=hidden,
# dirs_only=True,
# files_only=False)
# else:
# paths = fs.listdir(delegate_path,
# wildcard=wildcard,
# full=full,
# absolute=absolute,
# hidden=hidden,
# dirs_only=dirs_only,
# files_only=files_only)
# if full or absolute:
# if full:
# mount_path = makeabsolute(mount_path)
# else:
# mount_path = makerelative(mount_path)
# paths = [pathjoin(mount_path, path) for path in paths]
#
# return paths
#
# def mount(self, name, path, fs):
# self.mem_fs.makedir(path, recursive=True)
# mount_filename = pathjoin(path, '.mount')
# mount = self.mem_fs.open(mount_filename, 'w')
# mount.name = name
# mount.fs = fs
#
# self.mounts[name] = (path, fs)
if __name__ == "__main__": if __name__ == "__main__":
help(MountFS) help(MountFS)
......
...@@ -101,7 +101,7 @@ class MultiFS(FS): ...@@ -101,7 +101,7 @@ class MultiFS(FS):
try: try:
fs = self._delegate_search(path) fs = self._delegate_search(path)
if fs is not None: if fs is not None:
return fs.getsyspath(path, allow_none) return fs.getsyspath(path, allow_none=allow_none)
raise ResourceNotFoundError("NO_RESOURCE", path) raise ResourceNotFoundError("NO_RESOURCE", path)
finally: finally:
self._lock.release() self._lock.release()
...@@ -120,12 +120,12 @@ class MultiFS(FS): ...@@ -120,12 +120,12 @@ class MultiFS(FS):
self._lock.release() self._lock.release()
def open(self, path, mode="r", buffering=-1, **kwargs): def open(self, path, mode="r",**kwargs):
self._lock.acquire() self._lock.acquire()
try: try:
for fs in self: for fs in self:
if fs.exists(path): if fs.exists(path):
fs_file = fs.open(path, mode, buffering, **kwargs) fs_file = fs.open(path, mode, **kwargs)
return fs_file return fs_file
raise ResourceNotFoundError("NO_FILE", path) raise ResourceNotFoundError("NO_FILE", path)
...@@ -206,6 +206,8 @@ class MultiFS(FS): ...@@ -206,6 +206,8 @@ class MultiFS(FS):
self._lock.release() self._lock.release()
def rename(self, src, dst): def rename(self, src, dst):
if not issamedir(src, dst):
raise ValueError("Destination path must the same directory (user the move method for moving to a different directory)")
self._lock.acquire() self._lock.acquire()
try: try:
for fs in self: for fs in self:
......
...@@ -23,11 +23,11 @@ class OSFS(FS): ...@@ -23,11 +23,11 @@ class OSFS(FS):
sys_path = os.path.join(self.root_path, makerelative(self._resolve(path))) sys_path = os.path.join(self.root_path, makerelative(self._resolve(path)))
return sys_path return sys_path
def open(self, path, mode="r", buffering=-1, **kwargs): def open(self, path, mode="r", **kwargs):
try: try:
f = open(self.getsyspath(path), mode, buffering) f = open(self.getsyspath(path), mode, kwargs.get("buffering", -1))
except IOError, e: except IOError, e:
raise OperationFailedError("OPEN_FAILED", path, details=e, msg=str(details)) raise OperationFailedError("OPEN_FAILED", path, details=e, msg=str(e))
return f return f
...@@ -54,19 +54,21 @@ class OSFS(FS): ...@@ -54,19 +54,21 @@ class OSFS(FS):
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)
def makedir(self, path, mode=0777, recursive=False): def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
sys_path = self.getsyspath(path) sys_path = self.getsyspath(path)
try: try:
if recursive: if recursive:
os.makedirs(sys_path, mode) os.makedirs(sys_path, mode)
else: else:
if not allow_recreate and self.exists(path):
raise OperationFailedError("MAKEDIR_FAILED", dirname, msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s")
try: try:
os.mkdir(sys_path, mode) os.mkdir(sys_path, mode)
except OSError, e: except OSError, e:
raise FSError("NO_DIR", sys_path) raise OperationFailedError("MAKEDIR_FAILED", path)
except OSError, e: except OSError, e:
raise FSError("OS_ERROR", sys_path, details=e) raise OperationFailedError("MAKEDIR_FAILED", path, details=e)
def remove(self, path): def remove(self, path):
sys_path = self.getsyspath(path) sys_path = self.getsyspath(path)
...@@ -90,6 +92,8 @@ class OSFS(FS): ...@@ -90,6 +92,8 @@ class OSFS(FS):
raise OperationFailedError("REMOVEDIR_FAILED", path, details=e) raise OperationFailedError("REMOVEDIR_FAILED", path, details=e)
def rename(self, src, dst): def rename(self, src, dst):
if not issamedir(src, dst):
raise ValueError("Destination path must the same directory (user the move method for moving to a different directory)")
path_src = self.getsyspath(src) path_src = self.getsyspath(src)
path_dst = self.getsyspath(dst) path_dst = self.getsyspath(dst)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import unittest import unittest
import fs import fs
from helpers import *
import shutil import shutil
class TestHelpers(unittest.TestCase): class TestHelpers(unittest.TestCase):
...@@ -46,10 +47,10 @@ class TestHelpers(unittest.TestCase): ...@@ -46,10 +47,10 @@ class TestHelpers(unittest.TestCase):
result = testpaths[-1] result = testpaths[-1]
self.assertEqual(fs.pathjoin(*paths), result) self.assertEqual(fs.pathjoin(*paths), result)
self.assertRaises(fs.PathError, fs.pathjoin, "../") self.assertRaises(ValueError, fs.pathjoin, "../")
self.assertRaises(fs.PathError, fs.pathjoin, "./../") self.assertRaises(ValueError, fs.pathjoin, "./../")
self.assertRaises(fs.PathError, fs.pathjoin, "a/b", "../../..") self.assertRaises(ValueError, fs.pathjoin, "a/b", "../../..")
self.assertRaises(fs.PathError, fs.pathjoin, "a/b/../../../d") self.assertRaises(ValueError, fs.pathjoin, "a/b/../../../d")
def test_makerelative(self): def test_makerelative(self):
tests = [ ("/a/b", "a/b"), tests = [ ("/a/b", "a/b"),
...@@ -132,7 +133,6 @@ class TestObjectTree(unittest.TestCase): ...@@ -132,7 +133,6 @@ class TestObjectTree(unittest.TestCase):
self.assertEqual(right, "f/g") self.assertEqual(right, "f/g")
import tempfile import tempfile
import osfs import osfs
import os import os
...@@ -148,7 +148,7 @@ class TestOSFS(unittest.TestCase): ...@@ -148,7 +148,7 @@ class TestOSFS(unittest.TestCase):
shutil.rmtree(self.temp_dir) shutil.rmtree(self.temp_dir)
def check(self, p): def check(self, p):
return os.path.exists(os.path.join(self.temp_dir, p)) return os.path.exists(os.path.join(self.temp_dir, makerelative(p)))
def test_makedir(self): def test_makedir(self):
check = self.check check = self.check
...@@ -256,6 +256,35 @@ class TestOSFS(unittest.TestCase): ...@@ -256,6 +256,35 @@ class TestOSFS(unittest.TestCase):
size = self.fs.getsize("info.txt") size = self.fs.getsize("info.txt")
self.assertEqual(size, len(test_str)) self.assertEqual(size, len(test_str))
def test_movefile(self):
check = self.check
contents = "If the implementation is hard to explain, it's a bad idea."
def makefile(path):
f = self.fs.open(path, "wb")
f.write(contents)
f.close()
def checkcontents(path):
f = self.fs.open(path, "rb")
check_contents = f.read()
f.close()
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.move("foo/bar/a.txt", "foo/b.txt")
self.assert_(not check("foo/bar/a.txt"))
self.assert_(check("foo/b.txt"))
self.assert_(checkcontents("foo/b.txt"))
self.fs.move("foo/b.txt", "c.txt")
fs.print_fs(self.fs)
self.assert_(not check("foo/b.txt"))
self.assert_(check("/c.txt"))
self.assert_(checkcontents("/c.txt"))
class TestSubFS(TestOSFS): class TestSubFS(TestOSFS):
def setUp(self): def setUp(self):
...@@ -269,8 +298,9 @@ class TestSubFS(TestOSFS): ...@@ -269,8 +298,9 @@ class TestSubFS(TestOSFS):
shutil.rmtree(self.temp_dir) shutil.rmtree(self.temp_dir)
def check(self, p): def check(self, p):
p = os.path.join("foo/bar", p) p = os.path.join("foo/bar", makerelative(p))
return os.path.exists(os.path.join(self.temp_dir, p)) full_p = os.path.join(self.temp_dir, p)
return os.path.exists(full_p)
import memoryfs import memoryfs
...@@ -286,6 +316,22 @@ class TestMemoryFS(TestOSFS): ...@@ -286,6 +316,22 @@ class TestMemoryFS(TestOSFS):
return self.fs.exists(p) return self.fs.exists(p)
import mountfs
class TestMountFS(TestOSFS):
def setUp(self):
self.mount_fs = mountfs.MountFS()
self.mem_fs = memoryfs.MemoryFS()
self.mount_fs.mountdir("mounted/memfs", self.mem_fs)
self.fs = self.mount_fs.opendir("mounted/memfs")
def tearDown(self):
pass
def check(self, p):
return self.mount_fs.exists(os.path.join("mounted/memfs", makerelative(p)))
if __name__ == "__main__": if __name__ == "__main__":
#t = TestFS() #t = TestFS()
#t.setUp() #t.setUp()
......
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