From 13753ac8c2eb75a9142b400fd009cce2b2dc628c Mon Sep 17 00:00:00 2001 From: rfkelly0 Date: Tue, 5 May 2009 06:04:37 +0000 Subject: 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 --- fs/base.py | 525 +++++++++++++++++++++++++++++-------------------------------- 1 file 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 "" % (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 -- cgit v1.2.1