tessl install tessl/pypi-pyfuse3@3.4.0Python 3 bindings for libfuse 3 with async I/O support
Python 3 bindings for libfuse 3 with asynchronous I/O support. Create full-featured Linux filesystems in Python using async/await patterns with Trio or asyncio.
pip install pyfuse3import pyfuse3
from pyfuse3 import Operations, FUSEError, EntryAttributes, FileInfo, RequestContextMinimal filesystem:
import pyfuse3
import trio
import errno
import stat
import os
class SimpleFS(pyfuse3.Operations):
async def getattr(self, inode, ctx):
entry = pyfuse3.EntryAttributes()
if inode == pyfuse3.ROOT_INODE:
entry.st_mode = stat.S_IFDIR | 0o755
entry.st_ino = inode
entry.st_uid = os.getuid()
entry.st_gid = os.getgid()
else:
raise pyfuse3.FUSEError(errno.ENOENT)
return entry
async def run_fs():
fs = SimpleFS()
pyfuse3.init(fs, '/mnt/myfs')
await pyfuse3.main()
trio.run(run_fs)See Quick Start Guide for complete setup.
| Component | Purpose |
|---|---|
| Operations | Base class for filesystem handlers (async methods) |
| EntryAttributes | File/directory metadata (inode attributes) |
| FileInfo | File handle and caching options |
| RequestContext | Caller information (uid, gid, pid, umask) |
| Lookup Counts | Kernel reference tracking for inodes |
init() and stacktrace())FUSEError exceptionstrio.from_threaddef init(ops: Operations, mountpoint: str, options: set[str] = ...) -> None: ...
async def main(min_tasks: int = 1, max_tasks: int = 99) -> None: ...
def terminate() -> None: ...
def close(unmount: bool = True) -> None: ...class Operations:
# Lookup and attributes
async def lookup(self, parent_inode: InodeT, name: FileNameT, ctx: RequestContext) -> EntryAttributes: ...
async def getattr(self, inode: InodeT, ctx: RequestContext) -> EntryAttributes: ...
async def setattr(self, inode: InodeT, attr: EntryAttributes, fields: SetattrFields, fh: Optional[FileHandleT], ctx: RequestContext) -> EntryAttributes: ...
async def forget(self, inode_list: Sequence[Tuple[InodeT, int]]) -> None: ...
# File operations
async def open(self, inode: InodeT, flags: FlagT, ctx: RequestContext) -> FileInfo: ...
async def read(self, fh: FileHandleT, off: int, size: int) -> bytes: ...
async def write(self, fh: FileHandleT, off: int, buf: bytes) -> int: ...
async def release(self, fh: FileHandleT) -> None: ...
# Directory operations
async def opendir(self, inode: InodeT, ctx: RequestContext) -> FileHandleT: ...
async def readdir(self, fh: FileHandleT, start_id: int, token: ReaddirToken) -> None: ...
async def releasedir(self, fh: FileHandleT) -> None: ...
# Entry creation/removal
async def create(self, parent_inode: InodeT, name: FileNameT, mode: ModeT, flags: FlagT, ctx: RequestContext) -> Tuple[FileInfo, EntryAttributes]: ...
async def mkdir(self, parent_inode: InodeT, name: FileNameT, mode: ModeT, ctx: RequestContext) -> EntryAttributes: ...
async def unlink(self, parent_inode: InodeT, name: FileNameT, ctx: RequestContext) -> None: ...
async def rmdir(self, parent_inode: InodeT, name: FileNameT, ctx: RequestContext) -> None: ...class EntryAttributes:
st_ino: InodeT
st_mode: ModeT
st_nlink: int
st_uid: int
st_gid: int
st_size: int
st_atime_ns: int
st_mtime_ns: int
st_ctime_ns: int
entry_timeout: Union[float, int]
attr_timeout: Union[float, int]
class FileInfo:
fh: FileHandleT
direct_io: bool
keep_cache: bool
nonseekable: bool
class RequestContext:
uid: int
gid: int
pid: int
umask: intdef invalidate_inode(inode: InodeT, attr_only: bool = False) -> None: ...
def invalidate_entry(inode_p: InodeT, name: FileNameT, deleted: InodeT = 0) -> None: ...
def invalidate_entry_async(inode_p: InodeT, name: FileNameT, deleted: InodeT = 0, ignore_enoent: bool = False) -> None: ...def readdir_reply(token: ReaddirToken, name: FileNameT, attr: EntryAttributes, next_id: int) -> bool: ...
def get_sup_groups(pid: int) -> set[int]: ...# Types
InodeT = NewType("InodeT", int)
FileHandleT = NewType("FileHandleT", int)
FileNameT = bytes
FlagT = int
ModeT = int
XAttrNameT = bytes
# Constants
ROOT_INODE: InodeT # Value: 1
ENOATTR: int # Extended attribute not found
RENAME_EXCHANGE: FlagT
RENAME_NOREPLACE: FlagT
default_options: frozenset[str] # Contains 'default_permissions'| Error | Code | Usage |
|---|---|---|
| No such file | errno.ENOENT | File/directory not found |
| Permission denied | errno.EACCES | Access not allowed |
| File exists | errno.EEXIST | Entry already exists |
| Not a directory | errno.ENOTDIR | Expected directory |
| Is a directory | errno.EISDIR | Cannot operate on directory |
| Invalid argument | errno.EINVAL | Bad parameter |
| Not implemented | errno.ENOSYS | Operation not supported |
| Directory not empty | errno.ENOTEMPTY | Cannot remove |
| No space | errno.ENOSPC | Out of space |
✅ ALWAYS:
async def lookup(self, parent_inode, name, ctx):
if name not in self.entries:
raise pyfuse3.FUSEError(errno.ENOENT) # Correct❌ NEVER:
async def lookup(self, parent_inode, name, ctx):
if name not in self.entries:
raise KeyError("Not found") # WRONG - crashes filesystem!✅ ALWAYS:
async def lookup(self, parent_inode, name, ctx):
inode = self._find(parent_inode, name)
self.lookup_counts[inode] += 1 # Track reference
return await self.getattr(inode, ctx)
async def forget(self, inode_list):
for inode, nlookup in inode_list:
self.lookup_counts[inode] -= nlookup
if self.lookup_counts[inode] == 0 and self.inodes[inode]['nlink'] == 0:
del self.inodes[inode] # Safe to delete❌ NEVER:
async def unlink(self, parent_inode, name, ctx):
inode = self._find(parent_inode, name)
del self.inodes[inode] # WRONG - kernel may still reference it!✅ ALWAYS:
def signal_handler(signum, frame):
trio.from_thread.run_sync(pyfuse3.terminate, trio_token=pyfuse3.trio_token)❌ NEVER:
def signal_handler(signum, frame):
pyfuse3.terminate() # WRONG - may deadlock!✅ ALWAYS:
async def create(self, parent_inode, name, mode, flags, ctx):
final_mode = mode & ~ctx.umask # Apply umask
entry.st_mode = stat.S_IFREG | final_mode❌ NEVER:
async def create(self, parent_inode, name, mode, flags, ctx):
entry.st_mode = stat.S_IFREG | mode # WRONG - ignores umask!| Platform | Status | Notes |
|---|---|---|
| Linux | Full support | Primary platform, all features |
| macOS | Limited | Requires macFUSE, some limitations |
| BSD | Varies | Check specific BSD variant |