or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/pyfuse3@3.4.x

docs

index.md
tile.json

tessl/pypi-pyfuse3

tessl install tessl/pypi-pyfuse3@3.4.0

Python 3 bindings for libfuse 3 with async I/O support

index.mddocs/

pyfuse3

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.

Package Information

  • Package: pyfuse3
  • Installation: pip install pyfuse3
  • Python: 3.10+
  • Async Framework: Trio (primary), asyncio (supported)
  • Platform: Linux (primary), macOS (limited), BSD (varies)
  • Requires: libfuse3 system library

Quick Start

import pyfuse3
from pyfuse3 import Operations, FUSEError, EntryAttributes, FileInfo, RequestContext

Minimal 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.

Core Concepts

Architecture Overview

ComponentPurpose
OperationsBase class for filesystem handlers (async methods)
EntryAttributesFile/directory metadata (inode attributes)
FileInfoFile handle and caching options
RequestContextCaller information (uid, gid, pid, umask)
Lookup CountsKernel reference tracking for inodes

Key Principles

  1. Async Operations: All handlers are async (except init() and stacktrace())
  2. FUSEError Only: Handlers must only raise FUSEError exceptions
  3. Lookup Count Management: Track kernel references to prevent memory leaks
  4. Thread Safety: Use async primitives; cross-thread calls need trio.from_thread

API Quick Reference

Lifecycle

def 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: ...

Core Operations

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: ...

Data Structures

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: int

Cache Control

def 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: ...

Utilities

def readdir_reply(token: ReaddirToken, name: FileNameT, attr: EntryAttributes, next_id: int) -> bool: ...
def get_sup_groups(pid: int) -> set[int]: ...

Types and Constants

# 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'

Common Error Codes

ErrorCodeUsage
No such fileerrno.ENOENTFile/directory not found
Permission deniederrno.EACCESAccess not allowed
File existserrno.EEXISTEntry already exists
Not a directoryerrno.ENOTDIRExpected directory
Is a directoryerrno.EISDIRCannot operate on directory
Invalid argumenterrno.EINVALBad parameter
Not implementederrno.ENOSYSOperation not supported
Directory not emptyerrno.ENOTEMPTYCannot remove
No spaceerrno.ENOSPCOut of space

Documentation Structure

Guides

Examples

Reference

Critical Rules for Agents

1. Exception Handling

✅ 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!

2. Lookup Count Tracking

✅ 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!

3. Thread Safety

✅ 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!

4. Umask Application

✅ 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 Support

PlatformStatusNotes
LinuxFull supportPrimary platform, all features
macOSLimitedRequires macFUSE, some limitations
BSDVariesCheck specific BSD variant

Next Steps

  1. New to pyfuse3? Start with Quick Start Guide
  2. Building a filesystem? See Real-World Scenarios
  3. Need API details? Check Operations Reference
  4. Debugging issues? Review Error Handling Guide
  5. Performance tuning? Read Performance Tuning

Additional Resources

  • GitHub: libfuse/pyfuse3
  • PyPI: pyfuse3
  • FUSE Documentation: libfuse