summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrfkelly0 <rfkelly0@67cdc799-7952-0410-af00-57a81ceafa0f>2009-05-05 06:04:37 +0000
committerrfkelly0 <rfkelly0@67cdc799-7952-0410-af00-57a81ceafa0f>2009-05-05 06:04:37 +0000
commit13753ac8c2eb75a9142b400fd009cce2b2dc628c (patch)
treef4264d1e176cf9bff7d1e55a3f3de5387fe2e9c2
parentb247de98737b4e993b191838a5de492d5a5a1019 (diff)
downloadpyfilesystem-13753ac8c2eb75a9142b400fd009cce2b2dc628c.tar.gz
More explicit documentation on base FS class, plus using new exception classes
git-svn-id: http://pyfilesystem.googlecode.com/svn/branches/rfk-ideas@128 67cdc799-7952-0410-af00-57a81ceafa0f
-rw-r--r--fs/base.py525
1 files changed, 253 insertions, 272 deletions
diff --git a/fs/base.py b/fs/base.py
index d138a5a..d472dde 100644
--- a/fs/base.py
+++ b/fs/base.py
@@ -1,8 +1,15 @@
#!/usr/bin/env python
+"""
+
+ fs.base: base class defining the FS abstraction.
+
+This module defines the most basic filesystem abstraction, the FS class.
+Instances of FS represent a filesystem containing files and directories
+that can be queried and manipulated. To implement a new kind of filesystem,
+start by sublcassing the base FS class.
+
+"""
-from helpers import *
-import os
-import os.path
import shutil
import fnmatch
import datetime
@@ -11,98 +18,18 @@ try:
except ImportError:
import dummy_threading as threading
import dummy_threading
-
try:
import cPickle as pickle
except ImportError:
import pickle
-error_msgs = {
-
- "UNKNOWN_ERROR" : "No information on error: %(path)s",
-
- # UnsupportedError
- "UNSUPPORTED" : "Action is unsupported by this filesystem.",
-
- # OperationFailedError
- "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",
- "RENAME_FAILED" : "Unable to rename file: %(path)s",
- "OPEN_FAILED" : "Unable to open file: %(path)s",
- "DIR_EXISTS" : "Directory exists (try allow_recreate=True): %(path)s",
- "REMOVE_FAILED" : "Unable to remove file: %(path)s",
- "REMOVEDIR_FAILED" : "Unable to remove dir: %(path)s",
- "GETSIZE_FAILED" : "Unable to retrieve size of resource: %(path)s",
- "COPYFILE_FAILED" : "Unable to copy file: %(path)s",
- "READ_FAILED" : "Unable to read from file: %(path)s",
- "XATTR_FAILED" : "Unable to access extended-attribute: %(path)s",
-
- # NoSysPathError
- "NO_SYS_PATH" : "No mapping to OS filesytem: %(path)s,",
-
- # PathError
- "INVALID_PATH" : "Path is invalid: %(path)s",
-
- # ResourceLockedError
- "FILE_LOCKED" : "File is locked: %(path)s",
- "DIR_LOCKED" : "Dir is locked: %(path)s",
-
- # ResourceNotFoundError
- "NO_DIR" : "Directory does not exist: %(path)s",
- "NO_FILE" : "No such file: %(path)s",
- "NO_RESOURCE" : "No path to: %(path)s",
-
- # ResourceInvalid
- "WRONG_TYPE" : "Resource is not the type that was expected: %(path)s",
-
- # SystemError
- "OS_ERROR" : "Non specific OS error: %(path)s",
-}
-
-error_codes = error_msgs.keys()
-
-class FSError(Exception):
-
- """A catch all exception for FS objects."""
-
- def __init__(self, code, path=None, path2=None, msg=None, details=None):
- """A unified exception class that represents Filesystem errors.
- code -- A short identifier for the error
- path -- A path associated with the error
- msg -- An textual description of the error
- details -- Any additional details associated with the error
-
- """
-
- self.code = code
- self.msg = msg or error_msgs.get(code, error_msgs['UNKNOWN_ERROR'])
- self.path = path
- self.path2 = path2
- self.details = details
-
- def __str__(self):
- if self.details is None:
- msg = self.msg % dict((k, str(v)) for k, v in self.__dict__.iteritems())
- else:
- msg = self.msg % dict((k, str(v)) for k, v in self.__dict__.iteritems())
- msg += ", "+str(self.details)
-
- return '%s. %s' % (self.code, msg)
+from helpers import *
+from errors import *
-class UnsupportedError(FSError): pass
-class OperationFailedError(FSError): pass
-class NoSysPathError(FSError): pass
-class PathError(FSError): pass
-class ResourceLockedError(FSError): pass
-class ResourceNotFoundError(FSError): pass
-class DestinationExistsError(FSError): pass
-class SystemError(FSError): pass
-class ResourceInvalid(FSError): pass
def silence_fserrors(f, *args, **kwargs):
- """Perform a function call and return None if any FSError exceptions are thrown/
+ """Perform a function call and return None if FSError is thrown
f -- Function to call
args -- Parameters to f
@@ -114,15 +41,15 @@ def silence_fserrors(f, *args, **kwargs):
except FSError:
return None
+
class NullFile(object):
+ """A NullFile is a file object that has no functionality.
- """A NullFile is a file object that has no functionality. Null files are
- returned by the 'safeopen' method in FS objects when the file does not exist.
- This can simplify code by negating the need to check if a file exists,
- or handling exceptions.
+ Null files are returned by the 'safeopen' method in FS objects when the
+ file doesn't exist. This can simplify code by negating the need to check
+ if a file exists, or handling exceptions.
"""
-
def __init__(self):
self.closed = False
@@ -164,13 +91,16 @@ class NullFile(object):
def print_fs(fs, path="/", max_levels=5, indent=' '*2):
- """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.
- Without max_levels set, this function will traverse the entire directory tree.
+ """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 - without max_levels set, this function will traverse the
+ entire directory tree.
fs -- A filesystem object
path -- Path of root to list (default "/")
- max_levels -- Maximum levels of dirs to list (default 5), set to None for no maximum
+ max_levels -- Maximum levels of dirs to list (default 5), set to None
+ for no maximum
indent -- String to indent each directory level (default two spaces)
"""
@@ -198,6 +128,7 @@ def print_fs(fs, path="/", max_levels=5, indent=' '*2):
def _synchronize(func):
+ """Decorator to synchronize a method on self._lock."""
def acquire_lock(self, *args, **kwargs):
self._lock.acquire()
try:
@@ -208,26 +139,51 @@ def _synchronize(func):
return acquire_lock
-
class FS(object):
-
- """The base class for Filesystem objects. An instance of a class derived from FS is an abstraction
- on some kind of filesytem, such as the OS filesystem or a zip file.
+ """The base class for Filesystem abstraction objects.
+
+ An instance of a class derived from FS is an abstraction on some kind
+ of filesytem, such as the OS filesystem or a zip file.
+
+ The following is the minimal set of methods that must be provided by
+ a new FS subclass:
+
+ * open -- open a file for reading/writing (like python's open() func)
+ * isfile -- check whether a path exists and is a file
+ * isdir -- check whether a path exists and is a directory
+ * listdir -- list the contents of a directory
+ * makedir -- create a new directory
+ * remove -- remove an existing file
+ * removedir -- remove an existing directory
+ * rename -- atomically rename a file or directory
+ * getinfo -- return information about the path e.g. size, mtime
+
+ The following methods have a sensible default implementation, but FS
+ subclasses are welcome to override them if a more efficient implementation
+ can be provided:
+
+ * getsyspath -- get a file's name in the local filesystem, if possible
+ * exists -- check whether a path exists as file or directory
+ * copy -- copy a file to a new location
+ * move -- move a file to a new location
+ * copydir -- recursively copy a directory to a new location
+ * movedir -- recursively move a directory to a new location
"""
- def __init__(self, thread_syncronize=False):
- """The baseclass for Filesystem objects.
+ def __init__(self, thread_synchronize=False):
+ """The base class for Filesystem objects.
thread_synconize -- If True, a lock object will be created for the
object, otherwise a dummy lock will be used.
"""
- if thread_syncronize:
+ if thread_synchronize:
self._lock = threading.RLock()
else:
self._lock = dummy_threading.RLock()
+
def __getstate__(self):
# Locks can't be pickled, so instead we just indicate the
# type of lock that should be there. None == no lock,
@@ -251,27 +207,20 @@ class FS(object):
else:
self._lock = dummy_threading.RLock()
- def _resolve(self, pathname):
- resolved_path = resolvepath(pathname)
- return resolved_path
-
- def _abspath(self, pathname):
- pathname = normpath(pathname)
-
- if not pathname.startswith('/'):
- return pathjoin('/', pathname)
- return pathname
def getsyspath(self, path, allow_none=False):
- """Returns the system path (a path recognised by the operating system) if present.
- If the path does not map to a system path (and allow_none is False) then a NoSysPathError exception is thrown.
+ """Returns the system path (a path recognised by the OS) if present.
+
+ If the path does not map to a system path (and allow_none is False)
+ then a NoSysPathError exception is thrown.
path -- A path within the filesystem
- allow_none -- If True, this method can return None if there is no system path
+ allow_none -- If True, this method should return None if there is no
+ system path, rather than raising NoSysPathError
"""
if not allow_none:
- raise NoSysPathError("NO_SYS_PATH", path)
+ raise NoSysPathError(path=path)
return None
def hassyspath(self, path):
@@ -282,94 +231,141 @@ class FS(object):
"""
return self.getsyspath(path, None) is not None
+
def open(self, path, mode="r", **kwargs):
- """Opens a file.
+ """Open a the given path as a file-like object.
path -- Path to file that should be opened
- mode -- Mode of file to open, identical too the mode string used in
- 'file' and 'open' builtins
- kwargs -- Additional (optional) keyword parameters that may be required to open the file
+ mode -- Mode of file to open, identical to the mode string used
+ in 'file' and 'open' builtins
+ kwargs -- Additional (optional) keyword parameters that may
+ be required to open the file
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("open file")
def safeopen(self, *args, **kwargs):
- """Like 'open', but will return a NullFile if the file could not be opened."""
+ """Like 'open', but returns a NullFile if the file could't be opened."""
try:
f = self.open(*args, **kwargs)
except ResourceNotFoundError:
return NullFile()
return f
- def exists(self, path):
- """Returns True if the path references a valid resource.
-
- path -- A path to test
- """
- raise UnsupportedError("UNSUPPORTED")
+ def exists(self, path):
+ """Returns True if the path references a valid resource."""
+ return self.isfile(path) or self.isdir(path)
def isdir(self, path):
"""Returns True if a given path references a directory."""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("check for directory")
def isfile(self, path):
"""Returns True if a given path references a file."""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("check for file")
- def ishidden(self, path):
- """Returns True if the given path is hidden."""
- return path.startswith('.')
def listdir(self, path="./",
wildcard=None,
full=False,
absolute=False,
- hidden=True,
dirs_only=False,
files_only=False):
- """Lists all the files and directories in a path. Returns a list of paths.
+ """Lists all the files and directories in a path.
path -- Root of the path to list
- wildcard -- Only returns paths that match this wildcard, default does no matching
+ wildcard -- Only returns paths that match this wildcard
full -- Returns a full path
absolute -- Returns an absolute path
- hidden -- If True, return hidden files
dirs_only -- If True, only return directories
files_only -- If True, only return files
+ The directory contents are returned as a list of paths. If the
+ given path is not found then ResourceNotFoundError is raised;
+ if it exists but is not a directory, ResourceInvalidError is raised.
+
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("list directory")
+
+ def _listdir_helper(self, path, entries,
+ wildcard=None,
+ full=False,
+ absolute=False,
+ dirs_only=False,
+ files_only=False):
+ """A helper method called by listdir method that applies filtering.
+
+ Given the path to a directory and a list of the names of entries within
+ that directory, this method applies the semantics of the listdir()
+ keyword arguments. An appropriately modified and filtered list of
+ directory entries is returned.
+
+ """
+ if dirs_only and files_only:
+ raise ValueError("dirs_only and files_only can not both be True")
+
+ if wildcard is not None:
+ match = fnmatch.fnmatch
+ entries = [p for p in entries if match(p, wildcard)]
+
+ if dirs_only:
+ entries = [p for p in entries if self.isdir(pathjoin(path, p))]
+ elif files_only:
+ entries = [p for p in entries if self.isfile(pathjoin(path, p))]
+
+ if full:
+ entries = [pathjoin(path, p) for p in entries]
+ elif absolute:
+ entries = [abspath(pathjoin(path, p)) for p in entries]
- def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
- """Make a directory on the file system.
+ return entries
+
+
+ def makedir(self, path, recursive=False, allow_recreate=False):
+ """Make a directory on the filesystem.
path -- Path of directory
- mode -- Permissions
recursive -- If True, also create intermediate directories
- allow_recreate -- If True, then re-creating a directory wont throw an exception
+ allow_recreate -- If True, re-creating a directory wont be an error
- """
+ The following errors can be raised by this method:
+ * DestinationExistsError, if path is already a directory and
+ allow_recreate is False
+ * ParentDirectoryMissingError, if a containing directory is missing
+ and recursive is False
+ * ResourceInvalidError, if path is an existing file
- raise UnsupportedError("UNSUPPORTED")
+ """
+ raise UnsupportedError("make directory")
def remove(self, path):
- """Remove a resource from the filesystem.
+ """Remove a file from the filesystem.
path -- Path of the resource to remove
+ This method can raise the following errors:
+ * ResourceNotFoundError, if the path does not exist
+ * ResourceInvalidError, if the path is a directory
+
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("remove resource")
def removedir(self, path, recursive=False, force=False):
- """Remove a directory
+ """Remove a directory from the filesystem
path -- Path of the directory to remove
- recursive -- If True, then blank parent directories will be removed
+ recursive -- If True, then empty parent directories will be removed
force -- If True, any directory contents will be removed
+ This method can raise the following errors:
+ * ResourceNotFoundError, if the path does not exist
+ * ResourceInvalidError, if the path is not a directory
+ * DirectoryNotEmptyError, if the directory is not empty and
+ force is False
+
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("remove directory")
def rename(self, src, dst):
"""Renames a file or directory
@@ -378,7 +374,7 @@ class FS(object):
dst -- New name (not a path)
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("rename resource")
def getinfo(self, path):
"""Returns information for a path as a dictionary.
@@ -386,27 +382,29 @@ class FS(object):
path -- A path to retrieve information for
"""
- raise UnsupportedError("UNSUPPORTED")
+ raise UnsupportedError("get resource info")
+
def desc(self, path):
- """Returns short descriptive text regarding a path. For use as a debugging aid.
+ """Returns short descriptive text regarding a path.
path -- A path to describe
+ This is mainly for use as a debugging aid.
+
"""
if not self.exists(path):
return "No description available"
-
try:
sys_path = self.getsyspath(path)
except NoSysPathError:
return "No description available"
-
if self.isdir(path):
return "OS dir, maps to %s" % sys_path
else:
return "OS file, maps to %s" % sys_path
+
def getcontents(self, path):
"""Returns the contents of a file as a string.
@@ -422,7 +420,7 @@ class FS(object):
if f is not None:
f.close()
- def createfile(self, path, data):
+ def createfile(self, path, data=""):
"""A convenience method to create a new file from a string.
path -- Path of the file to create
@@ -445,53 +443,10 @@ class FS(object):
"""
if not self.exists(path):
- raise ResourceNotFoundError("NO_DIR", path)
-
+ raise DirectoryNotFoundError(path)
sub_fs = SubFS(self, path)
return sub_fs
- def _listdir_helper(self, path, paths, wildcard, full, absolute, hidden, dirs_only, files_only):
- """A helper function called by listdir method that applies filtering."""
-
- if dirs_only and files_only:
- raise ValueError("dirs_only and files_only can not both be True")
-
- if wildcard is not None:
- match = fnmatch.fnmatch
- paths = [p for p in paths if match(p, wildcard)]
-
- if not hidden:
- paths = [p for p in paths if not self.ishidden(p)]
-
- if dirs_only:
- paths = [p for p in paths if self.isdir(pathjoin(path, p))]
- elif files_only:
- paths = [p for p in paths if self.isfile(pathjoin(path, p))]
-
- if full:
- paths = [pathjoin(path, p) for p in paths]
- elif absolute:
- paths = [self._abspath(pathjoin(path, p)) for p in paths]
-
- return paths
-
-
- def walkfiles(self, path="/", wildcard=None, dir_wildcard=None, search="breadth" ):
- """Like the 'walk' method, but just yields files.
-
- path -- Root path to start walking
- wildcard -- If given, only return files that match this wildcard
- dir_wildcard -- If given, only walk in to directories that match this wildcard
- search -- A string that identifies the method used to walk the directories,
- can be 'breadth' for a breadth first search, or 'depth' for a depth first
- search. Use 'depth' if you plan to create / delete files as you go.
-
- """
-
- for path, files in self.walk(path, wildcard, dir_wildcard, search):
- for f in files:
- yield pathjoin(path, f)
-
def walk(self, path="/", wildcard=None, dir_wildcard=None, search="breadth"):
"""Walks a directory tree and yields the root path and contents.
@@ -543,6 +498,23 @@ class FS(object):
raise ValueError("Search should be 'breadth' or 'depth'")
+ def walkfiles(self, path="/", wildcard=None, dir_wildcard=None, search="breadth" ):
+ """Like the 'walk' method, but just yields files.
+
+ path -- Root path to start walking
+ wildcard -- If given, only return files that match this wildcard
+ dir_wildcard -- If given, only walk in to directories that match this wildcard
+ search -- A string that identifies the method used to walk the directories,
+ can be 'breadth' for a breadth first search, or 'depth' for a depth first
+ search. Use 'depth' if you plan to create / delete files as you go.
+
+ """
+
+ for path, files in self.walk(path, wildcard, dir_wildcard, search):
+ for f in files:
+ yield pathjoin(path, f)
+
+
def getsize(self, path):
"""Returns the size (in bytes) of a resource.
@@ -552,7 +524,7 @@ class FS(object):
info = self.getinfo(path)
size = info.get('size', None)
if 'size' is None:
- raise OperationFailedError("GETSIZE_FAILED", path)
+ raise OperationFailedError("get size of resource", path)
return size
def copy(self, src, dst, overwrite=False, chunk_size=16384):
@@ -560,20 +532,22 @@ class FS(object):
src -- The source path
dst -- The destination path
- overwrite -- If True, then the destination may be overwritten
- (if a file exists at that location). If False then an exception will be
- thrown if the destination exists
- chunk_size -- Size of chunks to use in copy, if a simple copy is required
+ overwrite -- If True, then an existing file at the destination may
+ be overwritten; If False then DestinationExistsError
+ will be raised.
+ chunk_size -- Size of chunks to use if a simple copy is required
+ If the destination path exists and is a directory, the file will
+ be copied into that directory.
"""
if self.isdir(dst):
dst = pathjoin( dirname(dst), resourcename(src) )
if not self.isfile(src):
- raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s")
+ raise ResourceInvalidError(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")
+ raise DestinationExistsError(dst)
src_syspath = self.getsyspath(src, allow_none=True)
dst_syspath = self.getsyspath(dst, allow_none=True)
@@ -597,12 +571,15 @@ class FS(object):
if dst_file is not None:
dst_file.close()
+
def move(self, src, dst, overwrite=False, chunk_size=16384):
"""Moves a file from one location to another.
src -- Source path
dst -- Destination path
- overwrite -- If True, then the destination may be overwritten
+ overwrite -- If True, then an existing file at the destination path
+ will be silently overwritte; if False then an exception
+ will be raised in this case.
"""
@@ -611,61 +588,56 @@ class FS(object):
if src_syspath is not None and dst_syspath is not None:
if not self.isfile(src):
- raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a file: %(path)s")
+ raise ResourceInvalidError(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")
+ raise DestinationExistsError(dst)
shutil.move(src_syspath, dst_syspath)
else:
self.copy(src, dst, overwrite=overwrite, chunk_size=chunk_size)
self.remove(src)
- def _get_attr_path(self, path):
- if self.isdir(path):
- return pathjoin(path, '.dirxattrs')
- else:
- dir_path, file_path = pathsplit(path)
- return pathjoin(dir_path, '.xattrs.'+file_path)
-
- def _get_attr_dict(self, path):
- attr_path = self._get_attr_path(path)
- if self.exists(attr_path):
- return pickle.loads(self.getcontents(attr_path))
- else:
- return {}
-
- def _set_attr_dict(self, path, attrs):
- attr_path = self._get_attr_path(path)
- self.setcontents(self._get_attr_path(path), pickle.dumps(attrs))
-
- def setxattr(self, path, key, value):
- attrs = self._get_attr_dict(path)
- attrs[key] = value
- self._set_attr_dict(path, attrs)
-
- def getxattr(self, path, key, default):
- attrs = self._get_attr_dict(path)
- return attrs.get(key, default)
-
- def removexattr(self, path, key):
- attrs = self._get_attr_dict(path)
- try:
- del attrs[key]
- except KeyError:
- pass
- self._set_attr_dict(path, attrs)
-
- def listxattrs(self, path):
- attrs = self._get_attr_dict(path)
- return self._get_attr_dict(path).keys()
-
- def updatexattrs(self, path, update_dict):
- d = self._get_attr_dict()
- d.update( dict([(k, v) for k,v in update_dict.iteritems()]) )
- self.set_attr_dict(self, path, d)
-
- def getxattrs(self, path):
- return dict( [(k, self.getxattr(path, k)) for k in self.listxattrs(path)] )
+# def _get_attr_path(self, path):
+# if self.isdir(path):
+# return pathjoin(path, '.xattrs.')
+# else:
+# dir_path, file_path = pathsplit(path)
+# return pathjoin(dir_path, '.xattrs.'+file_path)
+#
+# def _get_attr_dict(self, path):
+# attr_path = self._get_attr_path(path)
+# if self.exists(attr_path):
+# return pickle.loads(self.getcontents(attr_path))
+# else:
+# return {}
+#
+# def _set_attr_dict(self, path, attrs):
+# attr_path = self._get_attr_path(path)
+# self.setcontents(self._get_attr_path(path), pickle.dumps(attrs))
+#
+# def setxattr(self, path, key, value):
+# attrs = self._get_attr_dict(path)
+# attrs[key] = value
+# self._set_attr_dict(path, attrs)
+#
+# def getxattr(self, path, key, default):
+# attrs = self._get_attr_dict(path)
+# return attrs.get(key, default)
+#
+# def delxattr(self, path, key):
+# attrs = self._get_attr_dict(path)
+# try:
+# del attrs[key]
+# except KeyError:
+# pass
+# self._set_attr_dict(path, attrs)
+#
+# def listxattrs(self, path):
+# attrs = self._get_attr_dict(path)
+# return self._get_attr_dict(path).keys()
+#
+# def getxattrs(self, path):
+# return dict( [(k, self.getxattr(path, k)) for k in self.listxattrs(path)] )
def movedir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
"""Moves a directory from one location to another.
@@ -678,9 +650,9 @@ class FS(object):
"""
if not self.isdir(src):
- raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s")
+ raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s")
if not overwrite and self.exists(dst):
- raise DestinationExistsError("MOVEDIR_FAILED", src, dst, msg="Destination exists: %(path2)s")
+ raise DestinationExistsError(dst)
src_syspath = self.getsyspath(src, allow_none=True)
dst_syspath = self.getsyspath(dst, allow_none=True)
@@ -715,7 +687,11 @@ class FS(object):
dst_filename = pathjoin(dst_dirpath, filename)
movefile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size)
- self.removedir(dirname)
+# attr_path = self._get_attr_path(dirname)
+# if self.exists(attr_path):
+# self.move(attr_path,self._get_attr_path(dst_dirname),overwrite=True)
+#
+# self.removedir(dirname)
@@ -730,9 +706,9 @@ class FS(object):
"""
if not self.isdir(src):
- raise ResourceInvalid("WRONG_TYPE", src, msg="Source is not a dst: %(path)s")
+ raise ResourceInvalidError(src, msg="Source is not a directory: %(path)s")
if not overwrite and self.exists(dst):
- raise DestinationExistsError("COPYDIR_FAILED", dst, msg="Destination exists: %(path)s")
+ raise DestinationExistsError(dst)
def copyfile_noerrors(src, dst, overwrite):
try:
@@ -758,6 +734,10 @@ class FS(object):
dst_filename = pathjoin(dst_dirpath, filename)
copyfile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size)
+# attr_path = self._get_attr_path(dirname)
+# if self.exists(attr_path):
+# self.copy(attr_path,self._get_attr_path(dst_dirname),overwrite=True)
+
def isdirempty(self, path):
"""Return True if a path contains no files.
@@ -775,12 +755,12 @@ class FS(object):
-
class SubFS(FS):
-
"""A SubFS represents a sub directory of another filesystem object.
- SubFS objects are return by opendir, which effectively creates a 'sandbox'
- filesystem that can only access files / dirs under a root path within its 'parent' dir.
+
+ SubFS objects are returned by opendir, which effectively creates a 'sandbox'
+ filesystem that can only access files/dirs under a root path within its
+ 'parent' dir.
"""
@@ -791,7 +771,8 @@ class SubFS(FS):
def __str__(self):
return "<SubFS: %s in %s>" % (self.sub_dir, self.parent)
- __repr__ = __str__
+ def __repr__(self):
+ return str(self)
def __unicode__(self):
return unicode(self.__str__())
@@ -803,7 +784,7 @@ class SubFS(FS):
return "File in sub dir of %s"%str(self.parent)
def _delegate(self, path):
- return pathjoin(self.sub_dir, resolvepath(makerelative(path)))
+ return pathjoin(self.sub_dir, relpath(path))
def getsyspath(self, path, allow_none=False):
return self.parent.getsyspath(self._delegate(path), allow_none=allow_none)
@@ -816,7 +797,7 @@ class SubFS(FS):
def opendir(self, path):
if not self.exists(path):
- raise ResourceNotFoundError("NO_DIR", path)
+ raise DirectoryNotFoundError(path)
path = self._delegate(path)
sub_fs = self.parent.opendir(path)
@@ -831,24 +812,23 @@ class SubFS(FS):
def ishidden(self, path):
return self.parent.ishidden(self._delegate(path))
- def listdir(self, path="./", wildcard=None, full=False, absolute=False, hidden=True, dirs_only=False, files_only=False):
+ def listdir(self, path="./", wildcard=None, full=False, absolute=False, dirs_only=False, files_only=False):
paths = self.parent.listdir(self._delegate(path),
wildcard,
False,
False,
- hidden,
dirs_only,
files_only)
if absolute:
- listpath = resolvepath(path)
+ listpath = normpath(path)
paths = [makeabsolute(pathjoin(listpath, path)) for path in paths]
elif full:
- listpath = resolvepath(path)
+ listpath = normpath(path)
paths = [makerelative(pathjoin(listpath, path)) for path in paths]
return paths
- def makedir(self, path, mode=0777, recursive=False, allow_recreate=False):
+ def makedir(self, path, recursive=False, allow_recreate=False):
return self.parent.makedir(self._delegate(path), mode=mode, recursive=recursive, allow_recreate=allow_recreate)
def remove(self, path):
@@ -867,6 +847,7 @@ class SubFS(FS):
return self.parent.rename(self._delegate(src), self._delegate(dst))
+
if __name__ == "__main__":
import osfs
import browsewin