diff options
-rw-r--r-- | fs/browsewin.py | 42 | ||||
-rw-r--r-- | fs/commands/fscp.py | 2 | ||||
-rw-r--r-- | fs/commands/fsls.py | 41 | ||||
-rw-r--r-- | fs/commands/fsmkdir.py | 2 | ||||
-rw-r--r-- | fs/commands/fsmount.py | 2 | ||||
-rw-r--r-- | fs/commands/fsserve.py | 2 | ||||
-rw-r--r-- | fs/commands/fstree.py | 25 | ||||
-rw-r--r-- | fs/commands/runner.py | 75 | ||||
-rw-r--r-- | fs/ftpfs.py | 12 | ||||
-rw-r--r-- | fs/opener.py | 53 | ||||
-rw-r--r-- | fs/sftpfs.py | 69 | ||||
-rw-r--r-- | fs/wrapfs/subfs.py | 8 |
12 files changed, 232 insertions, 101 deletions
diff --git a/fs/browsewin.py b/fs/browsewin.py index 1e1754f..60760c2 100644 --- a/fs/browsewin.py +++ b/fs/browsewin.py @@ -14,12 +14,13 @@ Requires wxPython. import wx import wx.gizmos -import base as fs +from fs.path import isdotfile, pathsplit +from fs.errors import FSError class InfoFrame(wx.Frame): - def __init__(self, path, desc, info): - wx.Frame.__init__(self, None, -1, style=wx.DEFAULT_FRAME_STYLE, size=(500, 500)) + def __init__(self, parent, path, desc, info): + wx.Frame.__init__(self, parent, -1, style=wx.DEFAULT_FRAME_STYLE, size=(500, 500)) self.SetTitle("FS Object info - %s (%s)" % (path, desc)) @@ -34,19 +35,22 @@ class InfoFrame(wx.Frame): self.list_ctrl.SetColumnWidth(0, 190) self.list_ctrl.SetColumnWidth(1, 300) - for key in keys: - self.list_ctrl.Append((key, str(info.get(key)))) + for key in sorted(keys, key=lambda k:k.lower()): + self.list_ctrl.Append((key, unicode(info.get(key)))) + + self.Center() class BrowseFrame(wx.Frame): - def __init__(self, fs): + def __init__(self, fs, hide_dotfiles=False): wx.Frame.__init__(self, None, size=(1000, 600)) self.fs = fs - self.SetTitle("FS Browser - "+str(fs)) + self.hide_dotfiles = hide_dotfiles + self.SetTitle("FS Browser - " + unicode(fs)) self.tree = wx.gizmos.TreeListCtrl(self, -1, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) @@ -98,7 +102,19 @@ class BrowseFrame(wx.Frame): if item_data['expanded']: return - paths = [(self.fs.isdir(p), p) for p in self.fs.listdir(path, absolute=True)] + try: + paths = ( [(True, p) for p in self.fs.listdir(path, absolute=True, dirs_only=True)] + + [(False, p) for p in self.fs.listdir(path, absolute=True, files_only=True)] ) + except FSError, e: + msg = "Failed to get directory listing for %s\n\nThe following error was reported:\n\n%s" % (path, e) + wx.MessageDialog(self, msg, "Error listing directory", wx.OK).ShowModal() + paths = [] + + + #paths = [(self.fs.isdir(p), p) for p in self.fs.listdir(path, absolute=True)] + + if self.hide_dotfiles: + paths = [p for p in paths if not isdotfile(p[1])] if not paths: #self.tree.SetItemHasChildren(item_id, False) @@ -109,7 +125,7 @@ class BrowseFrame(wx.Frame): for is_dir, new_path in paths: - name = fs.pathsplit(new_path)[-1] + name = pathsplit(new_path)[-1] new_item = self.tree.AppendItem(item_id, name, data=wx.TreeItemData({'path':new_path, 'expanded':False})) @@ -157,20 +173,22 @@ class BrowseFrame(wx.Frame): path = item_data["path"] info = self.fs.getinfo(path) - info_frame = InfoFrame(path, self.fs.desc(path), info) + info_frame = InfoFrame(self, path, self.fs.desc(path), info) info_frame.Show() + info_frame.CenterOnParent() -def browse(fs): +def browse(fs, hide_dotfiles=False): """Displays a window containing a tree control that displays an FS object. Double-click a file/folder to display extra info. :param fs: A filesystem object + :param hide_fotfiles: If True, files and folders that begin with a dot will be hidden """ app = wx.PySimpleApp() - frame = BrowseFrame(fs) + frame = BrowseFrame(fs, hide_dotfiles=True) frame.Show() app.MainLoop() diff --git a/fs/commands/fscp.py b/fs/commands/fscp.py index abae921..48978e9 100644 --- a/fs/commands/fscp.py +++ b/fs/commands/fscp.py @@ -74,7 +74,7 @@ Copy SOURCE to DESTINATION""" srcs = args[:-1] dst = args[-1] - dst_fs, dst_path = self.open_fs(dst, writeable=True, create=True) + dst_fs, dst_path = self.open_fs(dst, writeable=True, create_dir=True) if dst_path is not None and dst_fs.isfile(dst_path): self.error('Destination must be a directory\n') diff --git a/fs/commands/fsls.py b/fs/commands/fsls.py index 611d700..2cfb244 100644 --- a/fs/commands/fsls.py +++ b/fs/commands/fsls.py @@ -17,7 +17,9 @@ List contents of [PATH]""" optparse.add_option('-u', '--full', dest='fullpath', action="store_true", default=False, help="output full path", metavar="FULL") optparse.add_option('-s', '--syspath', dest='syspath', action="store_true", default=False, - help="output system path", metavar="SYSPATH") + help="output system path (if one exists)", metavar="SYSPATH") + optparse.add_option('-r', '--url', dest='url', action="store_true", default=False, + help="output URL in place of path (if one exists)", metavar="URL") optparse.add_option('-d', '--dirsonly', dest='dirsonly', action="store_true", default=False, help="list directories only", metavar="DIRSONLY") optparse.add_option('-f', '--filesonly', dest='filesonly', action="store_true", default=False, @@ -52,30 +54,36 @@ List contents of [PATH]""" if not options.dirsonly: file_paths.append(path) else: - if not options.filesonly: + if not options.filesonly: dir_paths += fs.listdir(path, wildcard=wildcard, - full=options.fullpath, + full=options.fullpath or options.url, dirs_only=True) if not options.dirsonly: file_paths += fs.listdir(path, wildcard=wildcard, - full=options.fullpath, + full=options.fullpath or options.url, files_only=True) - try: - for fs in fs_used: + for fs in fs_used: + try: fs.close() - except FSError: - pass - + except FSError: + pass + if options.syspath: - dir_paths = [fs.getsyspath(path, allow_none=True) or path for path in dir_paths] - file_paths = [fs.getsyspath(path, allow_none=True) or path for path in file_paths] + # Path without a syspath, just won't be displayed + dir_paths = filter(None, [fs.getsyspath(path, allow_none=True) for path in dir_paths]) + file_paths = filter(None, [fs.getsyspath(path, allow_none=True) for path in file_paths]) + + if options.url: + # Path without a syspath, just won't be displayed + dir_paths = filter(None, [fs.getpathurl(path, allow_none=True) for path in dir_paths]) + file_paths = filter(None, [fs.getpathurl(path, allow_none=True) for path in file_paths]) - dirs = frozenset(dir_paths) - paths = sorted(dir_paths + file_paths, key=lambda p:p.lower()) + dirs = frozenset(dir_paths) + paths = sorted(dir_paths + file_paths, key=lambda p:p.lower()) if not options.all: paths = [path for path in paths if not isdotfile(path)] @@ -129,9 +137,9 @@ List contents of [PATH]""" if options.long: for path in paths: if path in dirs: - output(self.wrap_dirname(path) + '\n') + output((self.wrap_dirname(path), '\n')) else: - output(self.wrap_filename(path) + '\n') + output((self.wrap_filename(path), '\n')) else: terminal_width = self.terminal_width @@ -158,8 +166,7 @@ List contents of [PATH]""" num_cols -= 1 num_cols = max(1, num_cols) columns = columnize(paths, num_cols) - output(condense_columns(columns)) - output('\n') + output((condense_columns(columns), '\n')) def run(): return FSls().run() diff --git a/fs/commands/fsmkdir.py b/fs/commands/fsmkdir.py index bafb42c..bb64e2c 100644 --- a/fs/commands/fsmkdir.py +++ b/fs/commands/fsmkdir.py @@ -14,7 +14,7 @@ Make a directory""" def do_run(self, options, args): for fs_url in args: - fs, path = self.open_fs(fs_url, create=True) + fs, path = self.open_fs(fs_url, create_dir=True) def run(): return FSMkdir().run() diff --git a/fs/commands/fsmount.py b/fs/commands/fsmount.py index b0c4f98..3305f7f 100644 --- a/fs/commands/fsmount.py +++ b/fs/commands/fsmount.py @@ -54,7 +54,7 @@ Mounts a file system on a system path""" if platform.system() == 'Windows': pass else: - fs, path = self.open_fs(fs_url, create=True) + fs, path = self.open_fs(fs_url, create_dir=True) if path: if not fs.isdir(path): self.error('%s is not a directory on %s' % (fs_url. fs)) diff --git a/fs/commands/fsserve.py b/fs/commands/fsserve.py index c1a0c2d..cfe42a9 100644 --- a/fs/commands/fsserve.py +++ b/fs/commands/fsserve.py @@ -8,7 +8,7 @@ from fs.utils import print_fs class FSServe(Command): - """fsserve [OPTION]... [PATH] + usage = """fsserve [OPTION]... [PATH] Serves the contents of PATH with one of a number of methods""" def get_optparse(self): diff --git a/fs/commands/fstree.py b/fs/commands/fstree.py index 166612c..7f8fe4b 100644 --- a/fs/commands/fstree.py +++ b/fs/commands/fstree.py @@ -15,6 +15,8 @@ Recursively display the contents of PATH in an ascii tree""" optparse = super(FSTree, self).get_optparse() optparse.add_option('-l', '--level', dest='depth', type="int", default=5, help="Descend only LEVEL directories deep", metavar="LEVEL") + optparse.add_option('-g', '--gui', dest='gui', action='store_true', default=False, + help="browse the tree with a gui") optparse.add_option('-a', '--all', dest='all', action='store_true', default=False, help="do not hide dot files") optparse.add_option('-d', '--dirsfirst', dest='dirsfirst', action='store_true', default=False, @@ -26,18 +28,23 @@ Recursively display the contents of PATH in an ascii tree""" if not args: args = ['.'] - for fs, path, is_dir in self.get_resources(args, single=True): - if path is not None: - fs.opendir(path) + for fs, path, is_dir in self.get_resources(args, single=True): if not is_dir: self.error(u"'%s' is not a dir\n" % path) return 1 - print_fs(fs, path or '', - file_out=self.output_file, - max_levels=options.depth, - terminal_colors=self.is_terminal(), - hide_dotfiles=not options.all, - dirs_first=options.dirsfirst) + fs.cache_hint(True) + if options.gui: + from fs.browsewin import browse + if path: + fs = fs.opendir(path) + browse(fs, hide_dotfiles=not options.all) + else: + print_fs(fs, path or '', + file_out=self.output_file, + max_levels=options.depth, + terminal_colors=self.is_terminal(), + hide_dotfiles=not options.all, + dirs_first=options.dirsfirst) def run(): return FSTree().run() diff --git a/fs/commands/runner.py b/fs/commands/runner.py index 6647aa8..bc63ca1 100644 --- a/fs/commands/runner.py +++ b/fs/commands/runner.py @@ -5,7 +5,7 @@ from fs.errors import FSError from fs.path import splitext, pathsplit, isdotfile, iswildcard import platform from collections import defaultdict - +import re if platform.system() == 'Linux' : def getTerminalSize(): @@ -51,8 +51,11 @@ class Command(object): self.encoding = getattr(self.output_file, 'encoding', 'utf-8') or 'utf-8' self.verbosity_level = 0 self.terminal_colors = not sys.platform.startswith('win') and self.is_terminal() - w, h = getTerminalSize() - self.terminal_width = w + if self.is_terminal(): + w, h = getTerminalSize() + self.terminal_width = w + else: + self.terminal_width = 80 self.name = self.__class__.__name__.lower() def is_terminal(self): @@ -74,7 +77,9 @@ class Command(object): def wrap_filename(self, fname): fname = _unicode(fname) if not self.terminal_colors: - return fname + return fname + if '://' in fname: + return fname if '.' in fname: name, ext = splitext(fname) fname = u'%s\x1b[36m%s\x1b[0m' % (name, ext) @@ -88,16 +93,35 @@ class Command(object): return text return u'\x1b[2m%s\x1b[0m' % text + def wrap_link(self, text): + if not self.terminal_colors: + return text + return u'\x1b[1;33m%s\x1b[0m' % text + + def wrap_strong(self, text): + if not self.terminal_colors: + return text + return u'\x1b[1m%s\x1b[0m' % text + def wrap_table_header(self, name): if not self.terminal_colors: return name return '\x1b[1;32m%s\x1b[0m' % name - def open_fs(self, fs_url, writeable=False, create=False): + def highlight_fsurls(self, text): + if not self.terminal_colors: + return text + re_fs = r'(\S*?://\S*)' + def repl(matchobj): + fs_url = matchobj.group(0) + return self.wrap_link(fs_url) + return re.sub(re_fs, repl, text) + + def open_fs(self, fs_url, writeable=False, create_dir=False): try: - fs, path = opener.parse(fs_url, writeable=writeable, create_dir=create) + fs, path = opener.parse(fs_url, writeable=writeable, create_dir=create_dir) except OpenerError, e: - self.error(str(e)+'\n') + self.error(str(e), '\n') sys.exit(1) fs.cache_hint(True) return fs, path @@ -168,12 +192,15 @@ class Command(object): return text - def output(self, msg, verbose=False): - if verbose and not self.verbose: - return - self.output_file.write(self.text_encode(msg)) - + def output(self, msgs, verbose=False): + if verbose and not self.verbose: + return + if isinstance(msgs, basestring): + msgs = (msgs,) + for msg in msgs: + self.output_file.write(self.text_encode(msg)) + def output_table(self, table, col_process=None, verbose=False): if verbose and not self.verbose: @@ -199,8 +226,9 @@ class Command(object): lines.append(self.text_encode('%s\n' % ' '.join(out_col).rstrip())) self.output(''.join(lines)) - def error(self, msg): - self.error_file.write('%s: %s' % (self.name, self.text_encode(msg))) + def error(self, *msgs): + for msg in msgs: + self.error_file.write('%s: %s' % (self.name, self.text_encode(msg))) def get_optparse(self): optparse = OptionParser(usage=self.usage, version=self.version) @@ -229,23 +257,23 @@ class Command(object): for line in lines: words = [] line_len = 0 - for word in line.split(): + for word in line.split(): + if word == '*': + word = ' *' if line_len + len(word) > self.terminal_width: - self.output(' '.join(words)) - self.output('\n') + self.output((self.highlight_fsurls(' '.join(words)), '\n')) del words[:] line_len = 0 words.append(word) line_len += len(word) + 1 if words: - self.output(' '.join(words)) + self.output(self.highlight_fsurls(' '.join(words))) self.output('\n') - for names, desc in opener_table: - self.output('\n') + for names, desc in opener_table: + self.output(('-' * self.terminal_width, '\n')) proto = ', '.join([n+'://' for n in names]) - self.output(self.wrap_dirname('[%s]' % proto)) - self.output('\n') + self.output((self.wrap_dirname('[%s]' % proto), '\n\n')) if not desc.strip(): desc = "No information available" wrap_line(desc) @@ -280,8 +308,7 @@ class Command(object): if options.debug: raise return 1 - - + if __name__ == "__main__": command = Command() diff --git a/fs/ftpfs.py b/fs/ftpfs.py index aa0eec4..43cbda6 100644 --- a/fs/ftpfs.py +++ b/fs/ftpfs.py @@ -1035,6 +1035,15 @@ class FTPFS(FS): pass self.closed = True + def getpathurl(self, path, allow_none=False): + path = normpath(path) + credentials = '%s:%s' % (self.user, self.passwd) + if credentials == ':': + url = 'ftp://%s%s' % (self.host.rstrip('/'), abspath(path)) + else: + url = 'ftp://%s@%s%s' % (credentials, self.host.rstrip('/'), abspath(path)) + return url + @ftperrors def open(self, path, mode='r'): mode = mode.lower() @@ -1249,6 +1258,9 @@ class FTPFS(FS): @ftperrors def desc(self, path): + url = self.getpathurl(path, allow_none=True) + if url: + return url dirlist, fname = self._check_path(path) if fname not in dirlist: raise ResourceNotFoundError(path) diff --git a/fs/opener.py b/fs/opener.py index 38b1689..68124ab 100644 --- a/fs/opener.py +++ b/fs/opener.py @@ -210,7 +210,13 @@ class Opener(object): class OSFSOpener(Opener): names = ['osfs', 'file'] - desc = "OS filesystem opener, works with any valid system path. This is the default opener and will be used if you don't indicate which opener to use." + desc = """OS filesystem opener, works with any valid system path. This is the default opener and will be used if you don't indicate which opener to use. + + examples: + * file://relative/foo/bar/baz.txt (opens a relative file) + * file:///home/user (opens a directory from a absolute path) + * osfs://~/ (open the user's home directory) + * foo/bar.baz (file:// is the default opener)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -226,7 +232,12 @@ class OSFSOpener(Opener): class ZipOpener(Opener): names = ['zip', 'zip64'] - desc = "Opens zip files. Use zip64 for > 2 megabyte zip files, if you have a 64 bit processor.\ne.g. zip://myzip" + desc = """Opens zip files. Use zip64 for > 2 megabyte zip files, if you have a 64 bit processor. + + examples: + * zip://myzip.zip (open a local zip file) + * zip://myzip.zip!foo/bar/insidezip.txt (reference a file insize myzip.zip) + * zip:ftp://ftp.example.org/myzip.zip (open a zip file stored on a ftp server)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -263,7 +274,11 @@ class ZipOpener(Opener): class RPCOpener(Opener): names = ['rpc'] - desc = "An opener for filesystems server over RPC (see the fsserve command). e.g. rpc://127.0.0.1" + desc = """An opener for filesystems server over RPC (see the fsserve command). + +examples: +rpc://127.0.0.1:8000 (opens a RPC server running on local host, port 80) +rpc://www.example.org (opens an RPC server on www.example.org, default port 80)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -284,7 +299,11 @@ class RPCOpener(Opener): class FTPOpener(Opener): names = ['ftp'] - desc = "An opener for FTP (File Transfer Protocl) servers. e.g. ftp://ftp.mozilla.org" + desc = """An opener for FTP (File Transfer Protocl) server + +examples: +* ftp://ftp.mozilla.org (opens the root of ftp.mozilla.org) +* ftp://ftp.example.org/foo/bar (opens /foo/bar on ftp.mozilla.org)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -316,7 +335,11 @@ class FTPOpener(Opener): class SFTPOpener(Opener): names = ['sftp'] - desc = "An opener for SFTP (Secure File Transfer Protocol) servers" + desc = """An opener for SFTP (Secure File Transfer Protocol) servers + +examples: +* sftp://username:password@example.org (opens sftp server example.org with username and password +* sftp://example.org (opens example.org with public key authentication)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -367,7 +390,13 @@ class SFTPOpener(Opener): class MemOpener(Opener): names = ['mem', 'ram'] - desc = """Creates an in-memory filesystem (very fast but contents will disappear on exit).""" + desc = """Creates an in-memory filesystem (very fast but contents will disappear on exit). +Useful for creating a fast temporary filesystem for serving or mounting with fsserve or fsmount. +NB: If you user fscp or fsmv to copy/move files here, you are effectively deleting them! + +examples: +* mem:// (opens a new memory filesystem) +* mem://foo/bar (opens a new memory filesystem with subdirectory /foo/bar) """ @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -380,7 +409,10 @@ class MemOpener(Opener): class DebugOpener(Opener): names = ['debug'] - desc = "For developer -- adds debugging information to output. To use prepend an exisiting opener with debug: e.g debug:ftp://ftp.mozilla.org" + desc = """For developers -- adds debugging information to output. + +example: + * debug:ftp://ftp.mozilla.org (displays details of calls made to a ftp filesystem)""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): @@ -398,7 +430,12 @@ class DebugOpener(Opener): class TempOpener(Opener): names = ['temp'] - desc = "Creates a temporary filesystem, that is erased on exit." + desc = """Creates a temporary filesystem, that is erased on exit. +Probably only useful for mounting or serving. +NB: If you user fscp or fsmv to copy/move files here, you are effectively deleting them! + +example: +* temp://""" @classmethod def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): diff --git a/fs/sftpfs.py b/fs/sftpfs.py index 5f1846f..23b174c 100644 --- a/fs/sftpfs.py +++ b/fs/sftpfs.py @@ -93,6 +93,7 @@ class SFTPFS(FS): credentials = dict(username=username, password=password, pkey=pkey) + self.credentials = credentials if encoding is None: encoding = "utf8" @@ -103,7 +104,12 @@ class SFTPFS(FS): self._tlocal = thread_local() self._transport = None self._client = None - + + self.hostname = None + if isinstance(connection, basestring): + self.hostname = connection + elif isinstance(connection, tuple): + self.hostname = '%s:%s' % connection super(SFTPFS, self).__init__() self.root_path = abspath(normpath(root_path)) @@ -124,19 +130,18 @@ class SFTPFS(FS): connection.start_client() - if (password or pkey) and not connection.is_authenticated(): - + if not connection.is_authenticated(): try: if pkey: connection.auth_publickey(username, pkey) if not connection.is_authenticated() and password: - connection.auth_password(username, password) + connection.auth_password(username, password) - if not connection.is_authenticated(): + if not connection.is_authenticated(): self._agent_auth(connection, username) - if not connection.is_authenticated(): + if not connection.is_authenticated(): connection.close() raise RemoteConnectionError('no auth') @@ -145,8 +150,10 @@ class SFTPFS(FS): raise RemoteConnectionError('SSH exception (%s)' % str(e), details=e) self._transport = connection + + def __unicode__(self): + return '<SFTPFS: %s>' % self.desc('/') - @classmethod def _agent_auth(cls, transport, username): """ @@ -213,6 +220,21 @@ class SFTPFS(FS): raise PathError(path,msg="Path is outside root: %(path)s") return npath + def getpathurl(self, path, allow_none=False): + path = self._normpath(path) + if self.hostname is None: + if allow_none: + return None + raise NoPathURLError(path=path) + username = self.credentials.get('username', '') or '' + password = self.credentials.get('password', '') or '' + credentials = ('%s:%s' % (username, password)).rstrip(':') + if credentials: + url = 'sftp://%s@%s%s' % (credentials, self.hostname.rstrip('/'), abspath(path)) + else: + url = 'sftp://%s%s' % (self.hostname.rstrip('/'), abspath(path)) + return url + @convert_os_errors def open(self,path,mode="rb",bufsize=-1): npath = self._normpath(path) @@ -234,8 +256,11 @@ class SFTPFS(FS): def desc(self, path): npath = self._normpath(path) - addr, port = self._transport.getpeername() - return u'%s on sftp://%s:%i' % (self.client.normalize(npath), addr, port) + if self.hostname: + return u'sftp://%s%s' % (self.hostname, path) + else: + addr, port = self._transport.getpeername() + return u'sftp://%s:%i%s' % (addr, port, self.client.normalize(npath)) @convert_os_errors def exists(self,path): @@ -282,10 +307,9 @@ class SFTPFS(FS): if dirs_only or files_only: attrs = self.client.listdir_attr(npath) attrs_map = dict((a.filename, a) for a in attrs) - paths = attrs_map.keys() + paths = list(attrs_map.iterkeys()) else: - paths = self.client.listdir(npath) - + paths = self.client.listdir(npath) except IOError, e: if getattr(e,"errno",None) == 2: if self.isfile(path): @@ -294,24 +318,25 @@ class SFTPFS(FS): elif self.isfile(path): raise ResourceInvalidError(path,msg="Can't list directory contents of a file: %(path)s") raise - + if attrs_map: if dirs_only: filter_paths = [] - for path, attr in attrs_map.iteritems(): + for apath, attr in attrs_map.iteritems(): if isdir(self, path, attr.__dict__): - filter_paths.append(path) + filter_paths.append(apath) paths = filter_paths elif files_only: filter_paths = [] - for path, attr in attrs_map.iteritems(): - if isfile(self, path, attr.__dict__): - filter_paths.append(path) - paths = filter_paths + for apath, attr in attrs_map.iteritems(): + if isfile(self, apath, attr.__dict__): + filter_paths.append(apath) + paths = filter_paths for (i,p) in enumerate(paths): if not isinstance(p,unicode): - paths[i] = p.decode(self.encoding) + paths[i] = p.decode(self.encoding) + return self._listdir_helper(path, paths, wildcard, full, absolute, False, False) @@ -477,7 +502,7 @@ class SFTPFS(FS): @classmethod def _extract_info(cls, stats): fromtimestamp = datetime.datetime.fromtimestamp - info = dict((k, v) for k, v in stats.iteritems() if k in cls._info_vars) + info = dict((k, v) for k, v in stats.iteritems() if k in cls._info_vars and not k.startswith('_')) info['size'] = info['st_size'] ct = info.get('st_ctime') if ct is not None: @@ -494,7 +519,7 @@ class SFTPFS(FS): def getinfo(self, path): npath = self._normpath(path) stats = self.client.stat(npath) - info = dict((k, getattr(stats, k)) for k in dir(stats) if not k.startswith('__') ) + info = dict((k, getattr(stats, k)) for k in dir(stats) if not k.startswith('_')) info['size'] = info['st_size'] ct = info.get('st_ctime', None) if ct is not None: diff --git a/fs/wrapfs/subfs.py b/fs/wrapfs/subfs.py index 6271f35..1b6b24a 100644 --- a/fs/wrapfs/subfs.py +++ b/fs/wrapfs/subfs.py @@ -30,18 +30,16 @@ class SubFS(WrapFS): return abspath(normpath(path))[len(self.sub_dir):] def __str__(self): - return "%s/%s" % (self.wrapped_fs, self.sub_dir.lstrip('/')) + return self.wrapped_fs.desc(self.sub_dir) def __unicode__(self): - return u"%s/%s" % (self.wrapped_fs, self.sub_dir.lstrip('/')) + return u'<SubFS: %s!%s>' % (self.wrapped_fs, self.sub_dir) def __repr__(self): return str(self) def desc(self, path): - #return self.wrapped_fs.desc(join(self.sub_dir, path)) - desc = "%s!%s" % (str(self), path) - return desc + return '%s!%s' % (self.wrapped_fs.desc(self.sub_dir), path) def setcontents(self, path, data, chunk_size=64*1024): path = self._encode(path) |