Commit aece1874 by willmcgugan

Refinements to the memoryfs class, and added a 'walk' generator to the base class

parent 36b626d8
...@@ -9,7 +9,7 @@ import fs ...@@ -9,7 +9,7 @@ import fs
class BrowseFrame(wx.Frame): class BrowseFrame(wx.Frame):
def __init__(self, fs): def __init__(self, fs):
wx.Frame.__init__(self, None) wx.Frame.__init__(self, None)
self.fs = fs self.fs = fs
...@@ -44,7 +44,7 @@ class BrowseFrame(wx.Frame): ...@@ -44,7 +44,7 @@ class BrowseFrame(wx.Frame):
if not self.fs.isdir(path): if not self.fs.isdir(path):
return return
if item_data['expanded']: if item_data['expanded']:
return return
paths = [(self.fs.isdir(p), p) for p in self.fs.listdir(path, absolute=True)] paths = [(self.fs.isdir(p), p) for p in self.fs.listdir(path, absolute=True)]
...@@ -55,16 +55,16 @@ class BrowseFrame(wx.Frame): ...@@ -55,16 +55,16 @@ class BrowseFrame(wx.Frame):
return return
paths.sort(key=lambda p:(not p[0], p[1].lower())) paths.sort(key=lambda p:(not p[0], p[1].lower()))
for is_dir, new_path in paths: for is_dir, new_path in paths:
name = fs.pathsplit(new_path)[-1] name = fs.pathsplit(new_path)[-1]
if not is_dir and name.endswith('.txt'): if not is_dir and name.endswith('.txt'):
txt = self.fs.open(new_path).read(50) txt = self.fs.open(new_path).readline()[:50].rstrip()
name += " - "+txt name += " - "+txt
new_item = self.tree.AppendItem(item_id, name, data=wx.TreeItemData({'path':new_path, 'expanded':False})) new_item = self.tree.AppendItem(item_id, name, data=wx.TreeItemData({'path':new_path, 'expanded':False}))
if is_dir: if is_dir:
......
...@@ -13,7 +13,8 @@ error_msgs = { ...@@ -13,7 +13,8 @@ error_msgs = {
"LISTDIR_FAILED" : "Unable to get directory listing: %(path)s", "LISTDIR_FAILED" : "Unable to get directory listing: %(path)s",
"NO_SYS_PATH" : "No mapping to OS filesytem: %(path)s,", "NO_SYS_PATH" : "No mapping to OS filesytem: %(path)s,",
"DIR_EXISTS" : "Directory exists (try allow_recreate=True): %(path)s", "DIR_EXISTS" : "Directory exists (try allow_recreate=True): %(path)s",
"OPEN_FAILED" : "Unable to open file: %(path)s" "OPEN_FAILED" : "Unable to open file: %(path)s",
"FILE_LOCKED" : "File is locked: %(path)s",
} }
error_codes = error_msgs.keys() error_codes = error_msgs.keys()
...@@ -29,9 +30,9 @@ class FSError(Exception): ...@@ -29,9 +30,9 @@ class FSError(Exception):
def __str__(self): def __str__(self):
msg = self.msg % dict((k, str(v)) for k,v in self.__dict__.iteritems()) msg = self.msg % dict((k, str(v)) for k, v in self.__dict__.iteritems())
return '%s %s' % (self.code, msg) return '%s. %s' % (self.code, msg)
class NullFile: class NullFile:
...@@ -224,17 +225,40 @@ class FS(object): ...@@ -224,17 +225,40 @@ class FS(object):
elif absolute: elif absolute:
paths = [self.abspath(pathjoin(path, p)) for p in paths] paths = [self.abspath(pathjoin(path, p)) for p in paths]
return paths return paths
def walk_files(self, path="/", wildcard=None, dir_wildcard=None):
dirs = [path]
files = []
while dirs:
path = dirs.pop()
for path in self.listdir(path, full=True):
if self.isdir(path):
if dir_wildcard is not None:
if fnmatch.fnmatch(path, dir_wilcard):
dirs.append(path)
else:
dirs.append(path)
else:
if wildcard is not None:
if fnmatch.fnmatch(path, wildcard):
yield path
else:
yield path
class SubFS(FS): class SubFS(FS):
def __init__(self, parent, sub_dir): def __init__(self, parent, sub_dir):
self.parent = parent self.parent = parent
self.sub_dir = parent.abspath(sub_dir) self.sub_dir = parent.abspath(sub_dir)
#print "sub_dir", self.sub_dir
def __str__(self): def __str__(self):
return "<SubFS \"%s\" of %s>" % (self.sub_dir, self.parent) return "<SubFS \"%s\" of %s>" % (self.sub_dir, self.parent)
...@@ -242,7 +266,6 @@ class SubFS(FS): ...@@ -242,7 +266,6 @@ class SubFS(FS):
def _delegate(self, dirname): def _delegate(self, dirname):
delegate_path = pathjoin(self.sub_dir, resolvepath(makerelative(dirname))) delegate_path = pathjoin(self.sub_dir, resolvepath(makerelative(dirname)))
#print "delegate path", delegate_path
return delegate_path return delegate_path
def getsyspath(self, pathname): def getsyspath(self, pathname):
...@@ -274,7 +297,6 @@ class OSFS(FS): ...@@ -274,7 +297,6 @@ class OSFS(FS):
raise FSError("NO_DIR", expanded_path, msg="Root path is not a directory: %(path)s") raise FSError("NO_DIR", expanded_path, msg="Root path is not a directory: %(path)s")
self.root_path = normpath(os.path.abspath(expanded_path)) self.root_path = normpath(os.path.abspath(expanded_path))
#print "Root path", self.root_path
def __str__(self): def __str__(self):
return "<OSFS \"%s\">" % self.root_path return "<OSFS \"%s\">" % self.root_path
...@@ -320,7 +342,7 @@ class OSFS(FS): ...@@ -320,7 +342,7 @@ class OSFS(FS):
try: try:
paths = os.listdir(self.getsyspath(path)) paths = os.listdir(self.getsyspath(path))
except IOError, e: except (OSError, IOError), e:
raise FSError("LISTDIR_FAILED", path, details=e, msg="Unable to get directory listing: %(path)s - (%(details)s)") raise FSError("LISTDIR_FAILED", path, details=e, msg="Unable to get directory listing: %(path)s - (%(details)s)")
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)
...@@ -470,9 +492,11 @@ class MountFS(FS): ...@@ -470,9 +492,11 @@ class MountFS(FS):
if __name__ == "__main__": if __name__ == "__main__":
osfs = OSFS("~/") osfs = OSFS("~/projects")
print osfs print osfs
#print osfs
for filename in osfs.walk_files("/prettycharts", "*.pov"):
print filename
import browsewin import browsewin
browsewin.browse(osfs) browsewin.browse(osfs)
......
#!/usr/bin/env python #!/usr/bin/env python
import os
from fs import FS, pathsplit, _iteratepath, FSError, print_fs from fs import FS, pathsplit, _iteratepath, FSError, print_fs
try: try:
...@@ -7,199 +8,209 @@ try: ...@@ -7,199 +8,209 @@ try:
except ImportError: except ImportError:
from StringIO import StringIO from StringIO import StringIO
def _check_mode(mode, mode_chars):
for c in mode_chars:
if c not in mode:
return False
return True
class MemoryFile(object): class MemoryFile(object):
def __init__(self, path, memory_fs, value, mode): def __init__(self, path, memory_fs, value, mode):
self.path = path self.path = path
self.memory_fs = memory_fs self.memory_fs = memory_fs
self.mode = mode self.mode = mode
def check_mode(mode_chars):
for c in mode_chars:
if c not in mode:
return False
return True
self.mem_file = None self.mem_file = None
if check_mode('w'): if _check_mode(mode, 'wa'):
if check_mode('+'): self.mem_file = StringIO()
self.mem_file.write(value)
elif _check_mode(mode, 'w'):
self.mem_file = StringIO()
elif _check_mode(mode, 'ra'):
self.mem_file = StringIO()
self.mem_file.write(value)
elif _check_mode(mode, 'r'):
self.mem_file = StringIO(value)
else:
if value is not None:
self.mem_file = StringIO(value) self.mem_file = StringIO(value)
else: else:
self.mem_file = StringIO() self.mem_file = StringIO()
elif check_mode('r'): assert self.mem_file is not None, "self.mem_file should have a value"
if check_mode('+'):
self.mem_file = StringIO(value)
else:
self.mem_file = StringIO(value)
if check_mode('a'):
self.mem_file.seek(0, os.SEEK_END)
self.closed = False self.closed = False
def __del__(self): def __del__(self):
if not self.closed: if not self.closed:
self.close() self.close()
def flush(self): def flush(self):
pass pass
def __iter__(self): def __iter__(self):
return iter(self.mem_file) return iter(self.mem_file)
def next(self): def next(self):
return self.mem_file.next() return self.mem_file.next()
def readline(self, *args, **kwargs): def readline(self, *args, **kwargs):
return self.mem_file.readline(*args, **kwargs) return self.mem_file.readline(*args, **kwargs)
def close(self): def close(self):
value = self.mem_file.getvalue() if not self.closed:
if 'w' in self.mode: value = self.mem_file.getvalue()
self.memory_fs._on_close_memory_file(self.path, value) self.memory_fs._on_close_memory_file(self.path, value)
self.mem_file.close() self.mem_file.close()
self.closed = True self.closed = True
def read(self, size=None): def read(self, size=None):
if size is None:
size = -1
return self.mem_file.read(size) return self.mem_file.read(size)
def seek(self, *args, **kwargs): def seek(self, *args, **kwargs):
return self.mem_file.seek(*args, **kwargs) return self.mem_file.seek(*args, **kwargs)
def tell(self): def tell(self):
return self.mem_file.tell() return self.mem_file.tell()
def truncate(self, *args, **kwargs): def truncate(self, *args, **kwargs):
return self.mem_file.truncate(*args, **kwargs) return self.mem_file.truncate(*args, **kwargs)
def write(self, data): def write(self, data):
return self.mem_file.write(data) return self.mem_file.write(data)
def writelines(self, *args, **kwargs): def writelines(self, *args, **kwargs):
return self.mem_file.writelines(*args, **kwargs) return self.mem_file.writelines(*args, **kwargs)
class MemoryFS(FS):
class DirEntry(object):
class MemoryFS(FS):
class DirEntry(object):
def __init__(self, type, name, contents=None): def __init__(self, type, name, contents=None):
self.type = type self.type = type
self.name = name self.name = name
self.permissions = None self.permissions = None
if contents is None and type == "dir": if contents is None and type == "dir":
contents = {} contents = {}
self.contents = contents self.contents = contents
self.num_reading = 0
self.num_writing = 0
self.data = None self.data = None
self.locks = 0 self.locks = 0
def lock(self): def lock(self):
return
self.locks += 1 self.locks += 1
def unlock(self): def unlock(self):
return
self.locks -=1 self.locks -=1
assert self.locks >=0, "Lock / Unlock mismatch!" assert self.locks >=0, "Lock / Unlock mismatch!"
def desc_contents(self): def desc_contents(self):
if self.isfile(): if self.isfile():
return "<file %s>"%self.name return "<file %s>"%self.name
elif self.isdir(): elif self.isdir():
return "<dir %s>"%"".join( "%s: %s"% (k, v.desc_contents()) for k, v in self.contents.iteritems()) return "<dir %s>"%"".join( "%s: %s"% (k, v.desc_contents()) for k, v in self.contents.iteritems())
def isdir(self): def isdir(self):
return self.type == "dir" return self.type == "dir"
def isfile(self): def isfile(self):
return self.type == "file" return self.type == "file"
def islocked(self): def islocked(self):
return self.locks > 0 return self.locks > 0
def __str__(self): def __str__(self):
return "%s: %s" % (self.name, self.desc_contents()) return "%s: %s" % (self.name, self.desc_contents())
class FileEntry(object): class FileEntry(object):
def __init__(self): def __init__(self):
self.memory_file = None self.memory_file = None
self.value = "" self.value = ""
def _make_dir_entry(self, *args, **kwargs): def _make_dir_entry(self, *args, **kwargs):
return self.dir_entry_factory(*args, **kwargs) return self.dir_entry_factory(*args, **kwargs)
def __init__(self): def __init__(self):
self.dir_entry_factory = MemoryFS.DirEntry self.dir_entry_factory = MemoryFS.DirEntry
self.root = self._make_dir_entry('dir', 'root') self.root = self._make_dir_entry('dir', 'root')
def _get_dir_entry(self, dirpath): def _get_dir_entry(self, dirpath):
current_dir = self.root current_dir = self.root
for path_component in _iteratepath(dirpath): for path_component in _iteratepath(dirpath):
dir_entry = current_dir.contents.get(path_component, None) dir_entry = current_dir.contents.get(path_component, None)
if dir_entry is None: if dir_entry is None:
return None return None
current_dir = dir_entry current_dir = dir_entry
return current_dir return current_dir
def getsyspath(self, pathname): def getsyspath(self, pathname):
raise FSError("NO_SYS_PATH", pathname, msg="This file-system has no syspath") raise FSError("NO_SYS_PATH", pathname, msg="This file-system has no syspath")
def isdir(self, path): def isdir(self, path):
dir_item = self._get_dir_entry(self._resolve(path)) dir_item = self._get_dir_entry(self._resolve(path))
if dir_item is None: if dir_item is None:
return False return False
return dir_item.isdir() return dir_item.isdir()
def isfile(self, path): def isfile(self, path):
dir_item = self._get_dir_entry(self._resolve(path)) dir_item = self._get_dir_entry(self._resolve(path))
if dir_item is None: if dir_item is None:
return False return False
return dir_item.isfile() return dir_item.isfile()
def exists(self, path): def exists(self, path):
return self._getdir(path) is not None return self._getdir(path) is not None
def mkdir(self, dirname, mode=0777, recursive=False, allow_recreate=False): def mkdir(self, dirname, mode=0777, recursive=False, allow_recreate=False):
fullpath = dirname fullpath = dirname
dirpath, dirname = pathsplit(dirname) dirpath, dirname = pathsplit(dirname)
if recursive: if recursive:
parent_dir = self._get_dir_entry(dirpath) parent_dir = self._get_dir_entry(dirpath)
if parent_dir is not None: if parent_dir is not None:
if parent_dir.isfile(): if parent_dir.isfile():
raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s") raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s")
else: else:
if not allow_recreate: if not allow_recreate:
if dirname in parent_dir.contents: if dirname in parent_dir.contents:
raise FSError("NO_DIR", dirname, msg="Can not create a directory that already exists (try allow_recreate=True): %(path)s") raise FSError("NO_DIR", 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]:
dir_item = current_dir.contents.get(path_component, None) dir_item = current_dir.contents.get(path_component, None)
...@@ -208,8 +219,8 @@ class MemoryFS(FS): ...@@ -208,8 +219,8 @@ class MemoryFS(FS):
if not dir_item.isdir(): if not dir_item.isdir():
raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s") raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s")
current_dir = dir_item.contents current_dir = dir_item.contents
current_dir = self.root current_dir = self.root
for path_component in _iteratepath(dirpath): for path_component in _iteratepath(dirpath):
dir_item = current_dir.contents.get(path_component, None) dir_item = current_dir.contents.get(path_component, None)
if dir_item is None: if dir_item is None:
...@@ -218,114 +229,145 @@ class MemoryFS(FS): ...@@ -218,114 +229,145 @@ class MemoryFS(FS):
current_dir = new_dir current_dir = new_dir
else: else:
current_dir = dir_item current_dir = dir_item
parent_dir = current_dir parent_dir = current_dir
else: else:
parent_dir = self._get_dir_entry(dirpath) parent_dir = self._get_dir_entry(dirpath)
if parent_dir is None: if parent_dir is None:
raise FSError("NO_DIR", dirname, msg="Could not make dir, as parent dir does not exist: %(path)s") raise FSError("NO_DIR", dirname, msg="Could not make dir, as parent dir does not exist: %(path)s")
dir_item = parent_dir.contents.get(dirname, None) dir_item = parent_dir.contents.get(dirname, None)
if dir_item is not None: if dir_item is not None:
if dir_item.isdir(): if dir_item.isdir():
if not allow_recreate: if not allow_recreate:
raise FSError("DIR_EXISTS", dirname) raise FSError("DIR_EXISTS", dirname)
else: else:
raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s") raise FSError("NO_DIR", dirname, msg="Can not create a directory, because path references a file: %(path)s")
if dir_item is None: if dir_item is None:
parent_dir.contents[dirname] = self._make_dir_entry("dir", dirname) parent_dir.contents[dirname] = self._make_dir_entry("dir", dirname)
return self return self
def _lock_dir(self, dirpath): def _lock_dir_entry(self, path):
dir_entry = self._get_dir_entry(dirpath) dir_entry = self._get_dir_entry(path)
dir_entry.lock() dir_entry.lock()
def _unlock_dir(self, dirpath): def _unlock_dir_entry(self, path):
dir_entry = self._get_dir_entry(dirpath) dir_entry = self._get_dir_entry(path)
dir_entry.unlock() dir_entry.unlock()
def _is_dir_locked(self, dirpath): def _is_dir_locked(self, path):
dir_entry = self._get_dir_entry(dirpath) dir_entry = self._get_dir_entry(path)
return dir_entry.islocked() return dir_entry.islocked()
def open(self, path, mode="r", **kwargs): def open(self, path, mode="r", **kwargs):
filepath, filename = pathsplit(path) filepath, filename = pathsplit(path)
dir_entry = self._get_dir_entry(path)
parent_dir_entry = self._get_dir_entry(filepath) parent_dir_entry = self._get_dir_entry(filepath)
if parent_dir_entry is None or not parent_dir_entry.isdir(): if parent_dir_entry is None or not parent_dir_entry.isdir():
raise FSError("NO_FILE", path) raise FSError("NO_FILE", path)
if 'r' in mode or 'a' in mode: if 'r' in mode or 'a' in mode:
if filename not in parent_dir_entry.contents: if filename not in parent_dir_entry.contents:
raise FSError("NO_FILE", path) raise FSError("NO_FILE", path)
self._lock_dir(filepath)
file_dir_entry = parent_dir_entry.contents[filename] file_dir_entry = parent_dir_entry.contents[filename]
if 'a' in mode and file_dir_entry.islocked():
raise FSError("FILE_LOCKED", path)
self._lock_dir_entry(path)
mem_file = MemoryFile(path, self, file_dir_entry.data, mode) mem_file = MemoryFile(path, self, file_dir_entry.data, mode)
file_dir_entry.num_reading += 1
return mem_file return mem_file
elif 'w' in mode: elif 'w' in mode:
if filename not in parent_dir_entry.contents:
if filename not in parent_dir_entry.contents:
file_dir_entry = self._make_dir_entry("file", filename) file_dir_entry = self._make_dir_entry("file", filename)
parent_dir_entry.contents[filename] = file_dir_entry parent_dir_entry.contents[filename] = file_dir_entry
else: else:
file_dir_entry = parent_dir_entry.contents[fielname] file_dir_entry = parent_dir_entry.contents[filename]
if file_dir_entry.islocked():
raise FSError("FILE_LOCKED", path)
self._lock_dir_entry(path)
mem_file = MemoryFile(path, self, None, mode) mem_file = MemoryFile(path, self, None, mode)
file_dir_entry.num_writing = 1
return mem_file return mem_file
if parent_dir_entry is None: if parent_dir_entry is None:
raise FSError("NO_FILE", path) raise FSError("NO_FILE", path)
def _on_close_memory_file(self, path, value): def _on_close_memory_file(self, path, value):
filepath, filename = pathsplit(path) filepath, filename = pathsplit(path)
self._unlock_dir(filepath) dir_entry = self._get_dir_entry(path)
dir_entry = self._get_dir_entry(path)
dir_entry.data = value dir_entry.data = value
self._unlock_dir_entry(path)
def listdir(self, path="/", wildcard=None, full=False, absolute=False, hidden=False, dirs_only=False, files_only=False): def listdir(self, path="/", wildcard=None, full=False, absolute=False, hidden=False, dirs_only=False, files_only=False):
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
paths = dir_entry.contents.keys() paths = dir_entry.contents.keys()
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 ishidden(self, pathname): def ishidden(self, pathname):
return False return False
if __name__ == "__main__":
def main():
mem_fs = MemoryFS() mem_fs = MemoryFS()
mem_fs.mkdir('test/test2', recursive=True) mem_fs.mkdir('test/test2', recursive=True)
mem_fs.mkdir('test/A', recursive=True) mem_fs.mkdir('test/A', recursive=True)
mem_fs.mkdir('test/A/B', recursive=True) mem_fs.mkdir('test/A/B', recursive=True)
mem_fs.open("test/readme.txt", 'w').write("Hello, World!") mem_fs.open("test/readme.txt", 'w').write("Hello, World!")
mem_fs.open("test/readme.txt", 'wa').write("\nSecond Line")
print mem_fs.open("test/readme.txt", 'r').read()
f1 = mem_fs.open("/test/readme.txt", 'r')
f2 = mem_fs.open("/test/readme.txt", 'r')
print f1.read(10)
print f2.read(10)
f1.close()
f2.close()
f3 = mem_fs.open("/test/readme.txt", 'w')
#print mem_fs.listdir('test') #print mem_fs.listdir('test')
#print mem_fs.isdir("test/test2") #print mem_fs.isdir("test/test2")
#print mem_fs.root #print mem_fs.root
print_fs(mem_fs) print_fs(mem_fs)
from browsewin import browse from browsewin import browse
browse(mem_fs) browse(mem_fs)
\ No newline at end of file
if __name__ == "__main__":
main()
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