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

asyncio-compatibility.mddocs/reference/

Asyncio Compatibility

The pyfuse3.asyncio module provides compatibility for using pyfuse3 with asyncio instead of Trio. While Trio is the primary and better-tested async framework for pyfuse3, asyncio support is available through this compatibility layer.

Important Notes

  • Trio is recommended: Trio support is more thoroughly tested and stable
  • Asyncio is less tested: There may be bugs specific to asyncio usage
  • Some bugs may not be fixable: Due to fundamental differences between Trio and asyncio
  • Use at your own risk: For production code, prefer Trio
  • Enable before importing pyfuse3: Must call enable() before importing pyfuse3 main module

Capabilities

Enable Asyncio Mode

Switch pyfuse3 from Trio mode to asyncio mode.

def enable() -> None:
    """
    Switch pyfuse3 to asyncio mode.

    Must be called before importing or using pyfuse3 operations.
    Replaces Trio-specific functionality with asyncio equivalents.
    
    Notes:
        - Call before any pyfuse3 imports
        - Cannot be undone without restarting process
        - Affects all pyfuse3 usage in the process
        - Not thread-safe (call from main thread before starting)
    
    Thread Safety:
        - Not thread-safe
        - Call before any async operations start
    """

Usage example:

import pyfuse3.asyncio

# Enable asyncio mode BEFORE using pyfuse3
pyfuse3.asyncio.enable()

import pyfuse3
import asyncio

# Now pyfuse3 will use asyncio instead of Trio

Disable Asyncio Mode

Switch pyfuse3 back to default Trio mode.

def disable() -> None:
    """
    Switch pyfuse3 to default (Trio) mode.

    Restores Trio as the async framework.
    Rarely needed unless switching between frameworks in same process.
    
    Notes:
        - Restores Trio mode
        - Rarely used in practice
        - Not recommended to switch modes dynamically
        - May have side effects if pyfuse3 already initialized
    
    Thread Safety:
        - Not thread-safe
    """

Usage example:

import pyfuse3.asyncio

# Enable asyncio
pyfuse3.asyncio.enable()

# ... use pyfuse3 with asyncio ...

# Switch back to Trio (rarely needed)
pyfuse3.asyncio.disable()

Wait for Readable File Descriptor

Wait for a file descriptor to become readable.

async def wait_readable(fd: FileHandleT) -> None:
    """
    Wait for file descriptor to be readable.

    Args:
        fd: File descriptor to wait on

    Notes:
        - Uses asyncio event loop's add_reader
        - Automatically cleans up when done or cancelled
        - Raises ClosedResourceError if fd closed while waiting
        - Internal function used by pyfuse3 implementation
        - Rarely needed by user code

    Thread Safety:
        - Must be called from async context
        - Not thread-safe

    Raises:
        ClosedResourceError: If fd is closed while waiting
        RuntimeError: If no event loop running
    """

This function is used internally by pyfuse3's asyncio compatibility layer.

Usage example (internal):

import pyfuse3.asyncio

async def read_when_ready(fd):
    """Wait for fd to be readable, then read."""
    await pyfuse3.asyncio.wait_readable(fd)
    # Now safe to read from fd
    data = os.read(fd, 4096)
    return data

Notify File Descriptor Closing

Notify waiting tasks that a file descriptor is being closed.

def notify_closing(fd: FileHandleT) -> None:
    """
    Notify that file descriptor is closing.

    Args:
        fd: File descriptor being closed

    Notes:
        - Cancels any wait_readable operations on fd
        - Raises ClosedResourceError in waiting tasks
        - Internal function used by pyfuse3 implementation
        - Rarely needed by user code

    Thread Safety:
        - Thread-safe
        - Can be called from any thread
    """

This function is used internally by pyfuse3's asyncio compatibility layer.

Usage example (internal):

import pyfuse3.asyncio

def close_fd(fd):
    """Close fd and notify waiters."""
    # Notify any waiting tasks
    pyfuse3.asyncio.notify_closing(fd)
    # Close fd
    os.close(fd)

Get Current Trio Token

Returns a string identifier for asyncio mode.

def current_trio_token() -> str:
    """
    Returns 'asyncio' string.

    Used for compatibility with Trio's token system.
    Returns a string instead of actual Trio token.
    
    Notes:
        - Internal compatibility function
        - Returns 'asyncio' literal string
        - Used by pyfuse3 internals
    
    Returns:
        'asyncio' string
    """

Get Current Task

Get the currently running asyncio task.

def current_task() -> Optional[asyncio.Task[Any]]:
    """
    Get current asyncio task.

    Returns:
        Current task or None if not in task context

    Notes:
        - Compatible across Python 3.7+ versions
        - Returns asyncio.Task.current_task() or asyncio.current_task()
        - Returns None if called outside task context
        - Internal utility function

    Thread Safety:
        - Thread-safe
        - Must be called from async context for non-None result
    """

Open Nursery

Create a nursery-like context manager for managing asyncio tasks.

def open_nursery() -> _Nursery:
    """
    Create nursery-like context manager for asyncio.

    Returns:
        _Nursery instance

    Notes:
        - Provides Trio-like nursery API for asyncio
        - Manages a group of concurrent tasks
        - Waits for all tasks to complete on exit
        - Exceptions propagate from tasks
        - Internal compatibility function

    Usage:
        async with pyfuse3.asyncio.open_nursery() as nursery:
            nursery.start_soon(async_func, arg1, arg2)
            nursery.start_soon(another_async_func)
    """

Nursery API

The _Nursery class provides a Trio-like nursery interface for asyncio:

class _Nursery:
    """
    Nursery-like context manager for asyncio.
    
    Provides Trio-like API for managing concurrent tasks.
    """
    
    async def __aenter__(self) -> "_Nursery":
        """Enter nursery context."""

    def start_soon(
        self,
        func: Callable[..., Any],
        *args: Iterable[Any],
        name: Optional[str] = None
    ) -> None:
        """
        Start an async function as a task.

        Args:
            func: Async function to run
            *args: Arguments to pass to func
            name: Optional task name (for debugging)
        
        Notes:
            - Starts task immediately
            - Task runs concurrently with other tasks
            - Exceptions caught and re-raised on __aexit__
            - name parameter for debugging (may be ignored)
        """

    async def __aexit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_value: Optional[BaseException],
        traceback: Optional[Any]
    ) -> None:
        """
        Exit nursery context, waiting for all tasks.
        
        Notes:
            - Waits for all tasks to complete
            - If any task raised exception, re-raises
            - If multiple tasks raised, raises one (others logged)
            - If nursery context raised, cancels tasks
        """

Usage example:

import pyfuse3.asyncio
import asyncio

pyfuse3.asyncio.enable()

async def worker(n):
    print(f"Worker {n} starting")
    await asyncio.sleep(1)
    print(f"Worker {n} done")

async def main():
    async with pyfuse3.asyncio.open_nursery() as nursery:
        nursery.start_soon(worker, 1)
        nursery.start_soon(worker, 2)
        nursery.start_soon(worker, 3)
    # All workers have completed
    print("All done")

asyncio.run(main())

Exception Classes

class ClosedResourceError(Exception):
    """
    Raised when attempting to use a closed resource.

    Raised by wait_readable when fd is closed.
    Compatible with Trio's ClosedResourceError.
    """

Usage:

import pyfuse3.asyncio

try:
    await pyfuse3.asyncio.wait_readable(fd)
except pyfuse3.asyncio.ClosedResourceError:
    print("File descriptor was closed while waiting")

Type Aliases

Lock = asyncio.Lock  # Alias for asyncio.Lock

Usage:

import pyfuse3.asyncio

# Use pyfuse3.asyncio.Lock for consistency
lock = pyfuse3.asyncio.Lock()

async def protected_operation():
    async with lock:
        # Critical section
        pass

Complete Usage Example

import asyncio
import pyfuse3
import pyfuse3.asyncio
import errno
import stat
import os

# MUST enable asyncio mode before using pyfuse3
pyfuse3.asyncio.enable()

class AsyncioFS(pyfuse3.Operations):
    def __init__(self):
        super().__init__()

    async def getattr(self, inode, ctx):
        entry = pyfuse3.EntryAttributes()
        if inode == pyfuse3.ROOT_INODE:
            entry.st_mode = stat.S_IFDIR | 0o755
            entry.st_size = 0
        else:
            raise pyfuse3.FUSEError(errno.ENOENT)

        entry.st_ino = inode
        entry.st_uid = os.getuid()
        entry.st_gid = os.getgid()
        entry.st_atime_ns = entry.st_mtime_ns = entry.st_ctime_ns = 0
        return entry

    async def lookup(self, parent_inode, name, ctx):
        raise pyfuse3.FUSEError(errno.ENOENT)

    async def opendir(self, inode, ctx):
        if inode != pyfuse3.ROOT_INODE:
            raise pyfuse3.FUSEError(errno.ENOENT)
        return inode

    async def readdir(self, fh, start_id, token):
        # Empty directory
        return

async def run_filesystem():
    fs = AsyncioFS()

    # Initialize filesystem
    options = set(pyfuse3.default_options)
    pyfuse3.init(fs, '/mnt/asynciofs', options)

    try:
        # Run main loop with asyncio
        await pyfuse3.main()
    finally:
        pyfuse3.close()

if __name__ == '__main__':
    # Run with asyncio
    asyncio.run(run_filesystem())

Comparison with Trio

Similarities

  • Both provide async/await syntax
  • Both support concurrent tasks
  • Both have context managers for task management
  • Both handle exceptions from tasks

Differences

FeatureTrioAsyncio
Error handlingStricter, always propagatesMore lenient, can be lost
CancellationStructured, reliableLess structured
Task groupsNurseries (robust)TaskGroups (Python 3.11+)
DebuggingBetter toolsStandard tools
TestingBetter pyfuse3 supportLess tested
ComplexitySimpler conceptsMore complex

When to Use Asyncio

  • Existing asyncio codebase
  • Need to integrate with asyncio libraries
  • Team familiar with asyncio
  • Python 3.11+ with TaskGroups

When to Use Trio

  • New projects
  • Production deployments
  • Need maximum stability and testing coverage
  • Want better error handling and debugging

Migration from Trio

To migrate from Trio to asyncio:

  1. Add pyfuse3.asyncio.enable() before any pyfuse3 imports
  2. Replace trio.run() with asyncio.run()
  3. Replace Trio-specific APIs with asyncio equivalents
  4. Test thoroughly (asyncio support is less tested)

Example:

# Trio version
import trio
import pyfuse3

async def main():
    fs = MyFS()
    pyfuse3.init(fs, '/mnt/myfs')
    await pyfuse3.main()

trio.run(main)
# Asyncio version
import asyncio
import pyfuse3
import pyfuse3.asyncio

pyfuse3.asyncio.enable()

async def main():
    fs = MyFS()
    pyfuse3.init(fs, '/mnt/myfs')
    await pyfuse3.main()

asyncio.run(main())

Known Issues

  • Some edge cases may not work correctly with asyncio
  • Error propagation may differ from Trio
  • Cancellation behavior may differ from Trio
  • Performance characteristics may differ
  • Less testing coverage than Trio mode

Debugging

Enable asyncio Debug Mode

import asyncio
import logging

# Enable asyncio debug mode
asyncio.run(main(), debug=True)

# Or set event loop debug
loop = asyncio.get_event_loop()
loop.set_debug(True)

# Enable logging
logging.basicConfig(level=logging.DEBUG)

Common Issues

Issue: Filesystem hangs

  • Cause: Deadlock or blocking operation
  • Solution: Check for blocking calls in async functions

Issue: Exceptions lost

  • Cause: Task exception not propagated
  • Solution: Use nursery pattern or asyncio.TaskGroup

Issue: Slow performance

  • Cause: Too few or too many worker tasks
  • Solution: Tune min_tasks and max_tasks parameters

Recommendations

  1. Use Trio for production: It's better tested and more stable
  2. Test thoroughly: If using asyncio, test all code paths
  3. Report issues: File bug reports for asyncio-specific issues
  4. Consider alternatives: If asyncio is required, consider using a Trio-asyncio bridge instead
  5. Monitor for exceptions: Asyncio may silently lose exceptions; monitor carefully
  6. Use TaskGroup: On Python 3.11+, use asyncio.TaskGroup instead of nurseries
  7. Enable debug mode: Use asyncio debug mode during development
  8. Check cancellation: Verify cancellation works correctly
  9. Test error paths: Ensure errors propagate correctly
  10. Document choice: Document why asyncio was chosen over Trio

Internal Implementation Notes

The asyncio compatibility layer:

  • Replaces trio module references with fake Trio-like objects
  • Provides open_nursery() that returns an asyncio-based nursery
  • Implements wait_readable() using asyncio's add_reader()
  • Manages task lifecycle with asyncio primitives
  • Maintains compatibility with pyfuse3's Cython code
  • Provides Lock alias for asyncio.Lock

Best Practices

  1. Enable first: Always call enable() before importing pyfuse3
  2. Don't switch: Don't call enable()/disable() multiple times
  3. Use async/await consistently: Don't mix sync and async code
  4. Handle exceptions: Always handle exceptions from tasks
  5. Use nurseries: Use nursery pattern for managing concurrent tasks
  6. Test error cases: Test error handling thoroughly
  7. Monitor tasks: Monitor task lifecycle and exceptions
  8. Use timeouts: Add timeouts to prevent hangs
  9. Log errors: Log all errors for debugging
  10. Prefer Trio: When possible, use Trio for better reliability

Performance Considerations

Task Creation Overhead

  • asyncio task creation is fast but not free
  • Reuse tasks when possible
  • Don't create excessive tasks

Context Switching

  • Context switching between tasks has cost
  • Balance number of tasks vs switching overhead
  • Profile to find optimal task count

Memory Usage

  • Each task consumes memory
  • Limit maximum number of concurrent tasks
  • Use task pools for bounded concurrency

Platform Differences

Linux

  • Full asyncio support
  • All features available

macOS

  • asyncio support through macFUSE
  • May have platform-specific issues

BSD

  • Varies by BSD variant
  • Test on specific platform

Future

  • asyncio support may improve over time
  • File bugs for asyncio-specific issues
  • Consider contributing fixes
  • Trio remains primary supported framework