diff options
author | Terence Honles <terence@honles.com> | 2012-05-07 15:17:14 -0700 |
---|---|---|
committer | Terence Honles <terence@honles.com> | 2012-05-07 15:17:28 -0700 |
commit | 2fbfe661a605b0878443558d4a080a10cf53ba20 (patch) | |
tree | fbf7776931a7902505e84d9dfb7a038e0c667a0d | |
parent | 5e9a1154cbda06c8ff39c80dcbd1fd70744140c5 (diff) | |
parent | 2f0e5b5f1a5ee11a958acc8acdc47b187a76ec1b (diff) | |
download | fusepy-2fbfe661a605b0878443558d4a080a10cf53ba20.tar.gz |
bumping major version due to paths and metadata as unicode nowv2.0
-rw-r--r-- | README.rst | 29 | ||||
-rwxr-xr-x | examples/context.py | 26 | ||||
-rwxr-xr-x | examples/loopback.py | 49 | ||||
-rwxr-xr-x | examples/memory.py | 77 | ||||
-rwxr-xr-x | examples/memory3.py | 128 | ||||
-rwxr-xr-x | examples/sftp.py | 54 | ||||
-rw-r--r-- | fuse.py | 12 | ||||
-rwxr-xr-x | setup.py | 5 |
8 files changed, 132 insertions, 248 deletions
@@ -4,16 +4,13 @@ fusepy ``fusepy`` is a Python module that provides a simple interface to FUSE_ and MacFUSE_. It's just one file and is implemented using ctypes. -The official version of ``fusepy`` is hosted on `Google Code`_, but was split -into 3 seperate files: fuse24.py, fuse.py, and fuse3.py. These versions were -for <Python2.5, <Python3.x, and Python3.x respectively. Unfortunately they were -not all maintained, and installing the package in Python3 did not work. +The original version of ``fusepy`` was hosted on `Google Code`_, but is now +`officially hosted on GitHub`_. -This repo mereges bits from all 3 files and combines them into one file. The -file is written in 2x syntax, but trying to pay attention to bytes and other -changes 3x would care about. The only incompatible changes between 2x and 3x -are the change in syntax for number literals and exceptions. These issues are -fixed using the 2to3 tool when installing the package, or runnning:: +``fusepy`` is written in 2x syntax, but trying to pay attention to bytes and +other changes 3x would care about. The only incompatible changes between 2x and +3x are the change in syntax for number literals and exceptions. These issues +are fixed using the 2to3 tool when installing the package, or runnning:: 2to3 -f numliterals -f except -w fuse.py @@ -39,9 +36,13 @@ fusepy requires FUSE 2.6 (or later) and runs on: .. _FUSE: http://fuse.sourceforge.net/ .. _MacFUSE: http://code.google.com/p/macfuse/ .. _`Google Code`: http://code.google.com/p/fusepy/ -.. _memory: http://github.com/terencehonles/fusepy/tree/master/examples/memory.py -.. _loopback: http://github.com/terencehonles/fusepy/tree/master/examples/loopback.py -.. _context: http://github.com/terencehonles/fusepy/tree/master/examples/context.py -.. _sftp: http://github.com/terencehonles/fusepy/tree/master/examples/sftp.py + +.. _officially hosted on GitHub: source_ .. _download: https://github.com/terencehonles/fusepy/zipball/master -.. _source: http://github.com/terencehonles/fusepy/tree/master/ +.. _source: http://github.com/terencehonles/fusepy + +.. examples +.. _memory: http://github.com/terencehonles/fusepy/blob/master/examples/memory.py +.. _loopback: http://github.com/terencehonles/fusepy/blob/master/examples/loopback.py +.. _context: http://github.com/terencehonles/fusepy/blob/master/examples/context.py +.. _sftp: http://github.com/terencehonles/fusepy/blob/master/examples/sftp.py diff --git a/examples/context.py b/examples/context.py index 2609aa0..aa8ab76 100755 --- a/examples/context.py +++ b/examples/context.py @@ -9,8 +9,8 @@ from fuse import FUSE, FuseOSError, Operations, LoggingMixIn, fuse_get_context class Context(LoggingMixIn, Operations): - """Example filesystem to demonstrate fuse_get_context()""" - + 'Example filesystem to demonstrate fuse_get_context()' + def getattr(self, path, fh=None): uid, gid, pid = fuse_get_context() if path == '/': @@ -28,17 +28,20 @@ class Context(LoggingMixIn, Operations): raise FuseOSError(ENOENT) st['st_ctime'] = st['st_mtime'] = st['st_atime'] = time() return st - + def read(self, path, size, offset, fh): uid, gid, pid = fuse_get_context() + encoded = lambda x: ('%s\n' % x).encode('utf-8') + if path == '/uid': - return '%s\n' % uid + return encoded(uid) elif path == '/gid': - return '%s\n' % gid + return encoded(gid) elif path == '/pid': - return '%s\n' % pid - return '' - + return encoded(pid) + + raise RuntimeError('unexpected path: %r' % path) + def readdir(self, path, fh): return ['.', '..', 'uid', 'gid', 'pid'] @@ -54,8 +57,9 @@ class Context(LoggingMixIn, Operations): statfs = None -if __name__ == "__main__": +if __name__ == '__main__': if len(argv) != 2: - print 'usage: %s <mountpoint>' % argv[0] + print('usage: %s <mountpoint>' % argv[0]) exit(1) - fuse = FUSE(Context(), argv[1], foreground=True)
\ No newline at end of file + + fuse = FUSE(Context(), argv[1], foreground=True, ro=True) diff --git a/examples/loopback.py b/examples/loopback.py index 5ce16ed..a7b8275 100755 --- a/examples/loopback.py +++ b/examples/loopback.py @@ -12,87 +12,88 @@ import os from fuse import FUSE, FuseOSError, Operations, LoggingMixIn -class Loopback(LoggingMixIn, Operations): +class Loopback(LoggingMixIn, Operations): def __init__(self, root): self.root = realpath(root) self.rwlock = Lock() - + def __call__(self, op, path, *args): return super(Loopback, self).__call__(op, self.root + path, *args) - + def access(self, path, mode): if not os.access(path, mode): raise FuseOSError(EACCES) - + chmod = os.chmod chown = os.chown - + def create(self, path, mode): return os.open(path, os.O_WRONLY | os.O_CREAT, mode) - + def flush(self, path, fh): return os.fsync(fh) def fsync(self, path, datasync, fh): return os.fsync(fh) - + def getattr(self, path, fh=None): st = os.lstat(path) return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime', 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid')) - + getxattr = None - + def link(self, target, source): return os.link(source, target) - + listxattr = None mkdir = os.mkdir mknod = os.mknod open = os.open - + def read(self, path, size, offset, fh): with self.rwlock: os.lseek(fh, offset, 0) return os.read(fh, size) - + def readdir(self, path, fh): return ['.', '..'] + os.listdir(path) readlink = os.readlink - + def release(self, path, fh): return os.close(fh) - + def rename(self, old, new): return os.rename(old, self.root + new) - + rmdir = os.rmdir - + def statfs(self, path): stv = os.statvfs(path) return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree', 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag', 'f_frsize', 'f_namemax')) - + def symlink(self, target, source): return os.symlink(source, target) - + def truncate(self, path, length, fh=None): with open(path, 'r+') as f: f.truncate(length) - + unlink = os.unlink utimens = os.utime - + def write(self, path, data, offset, fh): with self.rwlock: os.lseek(fh, offset, 0) return os.write(fh, data) - -if __name__ == "__main__": + +if __name__ == '__main__': if len(argv) != 3: - print 'usage: %s <root> <mountpoint>' % argv[0] + print('usage: %s <root> <mountpoint>' % argv[0]) exit(1) - fuse = FUSE(Loopback(argv[1]), argv[2], foreground=True)
\ No newline at end of file + + fuse = FUSE(Loopback(argv[1]), argv[2], foreground=True) diff --git a/examples/memory.py b/examples/memory.py index 246b305..a103185 100755 --- a/examples/memory.py +++ b/examples/memory.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +import logging + from collections import defaultdict from errno import ENOENT from stat import S_IFDIR, S_IFLNK, S_IFREG @@ -8,18 +10,20 @@ from time import time from fuse import FUSE, FuseOSError, Operations, LoggingMixIn +if not hasattr(__builtins__, 'bytes'): + bytes = str class Memory(LoggingMixIn, Operations): - """Example memory filesystem. Supports only one level of files.""" - + 'Example memory filesystem. Supports only one level of files.' + def __init__(self): self.files = {} - self.data = defaultdict(str) + self.data = defaultdict(bytes) self.fd = 0 now = time() self.files['/'] = dict(st_mode=(S_IFDIR | 0755), st_ctime=now, - st_mtime=now, st_atime=now, st_nlink=2) - + st_mtime=now, st_atime=now, st_nlink=2) + def chmod(self, path, mode): self.files[path]['st_mode'] &= 0770000 self.files[path]['st_mode'] |= mode @@ -28,96 +32,105 @@ class Memory(LoggingMixIn, Operations): def chown(self, path, uid, gid): self.files[path]['st_uid'] = uid self.files[path]['st_gid'] = gid - + def create(self, path, mode): self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1, - st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) + st_size=0, st_ctime=time(), st_mtime=time(), + st_atime=time()) + self.fd += 1 return self.fd - + def getattr(self, path, fh=None): if path not in self.files: raise FuseOSError(ENOENT) - st = self.files[path] - return st - + + return self.files[path] + def getxattr(self, path, name, position=0): attrs = self.files[path].get('attrs', {}) + try: return attrs[name] except KeyError: return '' # Should return ENOATTR - + def listxattr(self, path): attrs = self.files[path].get('attrs', {}) return attrs.keys() - + def mkdir(self, path, mode): self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2, - st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) + st_size=0, st_ctime=time(), st_mtime=time(), + st_atime=time()) + self.files['/']['st_nlink'] += 1 - + def open(self, path, flags): self.fd += 1 return self.fd - + def read(self, path, size, offset, fh): return self.data[path][offset:offset + size] - + def readdir(self, path, fh): return ['.', '..'] + [x[1:] for x in self.files if x != '/'] - + def readlink(self, path): return self.data[path] - + def removexattr(self, path, name): attrs = self.files[path].get('attrs', {}) + try: del attrs[name] except KeyError: pass # Should return ENOATTR - + def rename(self, old, new): self.files[new] = self.files.pop(old) - + def rmdir(self, path): self.files.pop(path) self.files['/']['st_nlink'] -= 1 - + def setxattr(self, path, name, value, options, position=0): # Ignore options attrs = self.files[path].setdefault('attrs', {}) attrs[name] = value - + def statfs(self, path): return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) - + def symlink(self, target, source): self.files[target] = dict(st_mode=(S_IFLNK | 0777), st_nlink=1, - st_size=len(source)) + st_size=len(source)) + self.data[target] = source - + def truncate(self, path, length, fh=None): self.data[path] = self.data[path][:length] self.files[path]['st_size'] = length - + def unlink(self, path): self.files.pop(path) - + def utimens(self, path, times=None): now = time() atime, mtime = times if times else (now, now) self.files[path]['st_atime'] = atime self.files[path]['st_mtime'] = mtime - + def write(self, path, data, offset, fh): self.data[path] = self.data[path][:offset] + data self.files[path]['st_size'] = len(self.data[path]) return len(data) -if __name__ == "__main__": +if __name__ == '__main__': if len(argv) != 2: - print 'usage: %s <mountpoint>' % argv[0] + print('usage: %s <mountpoint>' % argv[0]) exit(1) - fuse = FUSE(Memory(), argv[1], foreground=True)
\ No newline at end of file + + logging.getLogger().setLevel(logging.DEBUG) + fuse = FUSE(Memory(), argv[1], foreground=True) diff --git a/examples/memory3.py b/examples/memory3.py deleted file mode 100755 index e5cbad7..0000000 --- a/examples/memory3.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python - -from fuse3 import FUSE, Operations, LoggingMixIn - -from collections import defaultdict -from errno import ENOENT -from stat import S_IFDIR, S_IFLNK, S_IFREG -from sys import argv, exit -from time import time - -import logging - - -class Memory(LoggingMixIn, Operations): - """Example memory filesystem. Supports only one level of files.""" - - def __init__(self): - self.files = {} - self.data = defaultdict(bytearray) - self.fd = 0 - now = time() - self.files['/'] = dict(st_mode=(S_IFDIR | 0o755), st_ctime=now, - st_mtime=now, st_atime=now, st_nlink=2) - - def chmod(self, path, mode): - self.files[path]['st_mode'] &= 0o770000 - self.files[path]['st_mode'] |= mode - return 0 - - def chown(self, path, uid, gid): - self.files[path]['st_uid'] = uid - self.files[path]['st_gid'] = gid - - def create(self, path, mode): - self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1, - st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) - self.fd += 1 - return self.fd - - def getattr(self, path, fh=None): - if path not in self.files: - raise OSError(ENOENT, '') - st = self.files[path] - return st - - def getxattr(self, path, name, position=0): - attrs = self.files[path].get('attrs', {}) - try: - return attrs[name] - except KeyError: - return '' # Should return ENOATTR - - def listxattr(self, path): - attrs = self.files[path].get('attrs', {}) - return attrs.keys() - - def mkdir(self, path, mode): - self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2, - st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) - self.files['/']['st_nlink'] += 1 - - def open(self, path, flags): - self.fd += 1 - return self.fd - - def read(self, path, size, offset, fh): - return bytes(self.data[path][offset:offset + size]) - - def readdir(self, path, fh): - return ['.', '..'] + [x[1:] for x in self.files if x != '/'] - - def readlink(self, path): - return self.data[path].decode('utf-8') - - def removexattr(self, path, name): - attrs = self.files[path].get('attrs', {}) - try: - del attrs[name] - except KeyError: - pass # Should return ENOATTR - - def rename(self, old, new): - self.files[new] = self.files.pop(old) - - def rmdir(self, path): - self.files.pop(path) - self.files['/']['st_nlink'] -= 1 - - def setxattr(self, path, name, value, options, position=0): - # Ignore options - attrs = self.files[path].setdefault('attrs', {}) - attrs[name] = value - - def statfs(self, path): - return dict(f_bsize=512, f_blocks=4096, f_bavail=2048) - - def symlink(self, target, source): - source = source.encode('utf-8') - self.files[target] = dict(st_mode=(S_IFLNK | 0o777), st_nlink=1, - st_size=len(source)) - self.data[target] = bytearray(source) - - def truncate(self, path, length, fh=None): - del self.data[path][length:] - self.files[path]['st_size'] = length - - def unlink(self, path): - self.files.pop(path) - - def utimens(self, path, times=None): - now = time() - atime, mtime = times if times else (now, now) - self.files[path]['st_atime'] = atime - self.files[path]['st_mtime'] = mtime - - def write(self, path, data, offset, fh): - del self.data[path][offset:] - self.data[path].extend(data) - self.files[path]['st_size'] = len(self.data[path]) - return len(data) - - -if __name__ == "__main__": - if len(argv) != 2: - print('usage: %s <mountpoint>' % argv[0]) - exit(1) - logging.getLogger().setLevel(logging.DEBUG) - fuse = FUSE(Memory(), argv[1], foreground=True)
\ No newline at end of file diff --git a/examples/sftp.py b/examples/sftp.py index 019fb29..6f512bd 100755 --- a/examples/sftp.py +++ b/examples/sftp.py @@ -5,44 +5,26 @@ from time import time from paramiko import SSHClient -from fuse import FUSE, Operations +from fuse import FUSE, Operations, LoggingMixIn -class SFTP(Operations): - """A simple SFTP filesystem. Requires paramiko: - http://www.lag.net/paramiko/ - - You need to be able to login to remote host without entering a password. - """ +class SFTP(LoggingMixIn, Operations): + ''' + A simple SFTP filesystem. Requires paramiko: http://www.lag.net/paramiko/ + + You need to be able to login to remote host without entering a password. + ''' + def __init__(self, host, path='.'): self.client = SSHClient() self.client.load_system_host_keys() self.client.connect(host) self.sftp = self.client.open_sftp() self.root = path - - def __del__(self): - self.sftp.close() - self.client.close() - - def __call__(self, op, path, *args): - print '->', op, path, args[0] if args else '' - ret = '[Unhandled Exception]' - try: - ret = getattr(self, op)(self.root + path, *args) - return ret - except OSError, e: - ret = str(e) - raise - except IOError, e: - ret = str(e) - raise OSError(*e.args) - finally: - print '<-', op - + def chmod(self, path, mode): return self.sftp.chmod(path, mode) - + def chown(self, path, uid, gid): return self.sftp.chown(path, uid, gid) @@ -52,6 +34,10 @@ class SFTP(Operations): f.close() return 0 + def destroy(self, path): + self.sftp.close() + self.client.close() + def getattr(self, path, fh=None): st = self.sftp.lstat(path) return dict((key, getattr(st, key)) for key in ('st_atime', 'st_gid', @@ -68,7 +54,8 @@ class SFTP(Operations): return buf def readdir(self, path, fh): - return ['.', '..'] + [name.encode('utf-8') for name in self.sftp.listdir(path)] + return ['.', '..'] + [name.encode('utf-8') + for name in self.sftp.listdir(path)] def readlink(self, path): return self.sftp.readlink(path) @@ -97,10 +84,11 @@ class SFTP(Operations): f.write(data) f.close() return len(data) - -if __name__ == "__main__": + +if __name__ == '__main__': if len(argv) != 3: - print 'usage: %s <host> <mountpoint>' % argv[0] + print('usage: %s <host> <mountpoint>' % argv[0]) exit(1) - fuse = FUSE(SFTP(argv[1]), argv[2], foreground=True, nothreads=True)
\ No newline at end of file + + fuse = FUSE(SFTP(argv[1]), argv[2], foreground=True, nothreads=True) @@ -1,4 +1,5 @@ -# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> +# Copyright (c) 2012 Terence Honles <terence@honles.com> (maintainer) +# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> (author) # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -40,7 +41,9 @@ except ImportError: newfunc.keywords = keywords return newfunc -if not hasattr(__builtins__, 'basestring'): +try: + basestring +except NameError: basestring = str class c_timespec(Structure): @@ -543,10 +546,9 @@ class FUSE(object): return retsize def listxattr(self, path, namebuf, size): - ret = '\x00'.join(self.operations('listxattr', path) or '') \ - .encode(self.encoding) + attrs = self.operations('listxattr', path.decode(self.encoding)) or '' - buf = create_string_buffer(ret) + buf = create_string_buffer('\x00'.join(attrs).encode(self.encoding)) bufsize = len(buf) if namebuf: if bufsize > size: return -ERANGE @@ -13,14 +13,17 @@ with open('README') as readme: setup( name = 'fusepy', - version = '1.2', + version = '2.0', description = 'Simple ctypes bindings for FUSE', long_description = documentation, author = 'Giorgos Verigakis', author_email = 'verigak@gmail.com', + maintainer = 'Terence Honles', + maintainer_email = 'terence@honles.com', license = 'ISC', py_modules=['fuse'], + url = 'http://github.com/terencehonles/fusepy', use_2to3 = True, # only use the following fixers (everything else is already compatible) |