tessl install tessl/pypi-pyfuse3@3.4.0Python 3 bindings for libfuse 3 with async I/O support
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.
enable() before importing pyfuse3 main moduleSwitch 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 TrioSwitch 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 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 dataNotify 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)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 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
"""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)
"""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())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")Lock = asyncio.Lock # Alias for asyncio.LockUsage:
import pyfuse3.asyncio
# Use pyfuse3.asyncio.Lock for consistency
lock = pyfuse3.asyncio.Lock()
async def protected_operation():
async with lock:
# Critical section
passimport 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())| Feature | Trio | Asyncio |
|---|---|---|
| Error handling | Stricter, always propagates | More lenient, can be lost |
| Cancellation | Structured, reliable | Less structured |
| Task groups | Nurseries (robust) | TaskGroups (Python 3.11+) |
| Debugging | Better tools | Standard tools |
| Testing | Better pyfuse3 support | Less tested |
| Complexity | Simpler concepts | More complex |
To migrate from Trio to asyncio:
pyfuse3.asyncio.enable() before any pyfuse3 importstrio.run() with asyncio.run()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())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)Issue: Filesystem hangs
Issue: Exceptions lost
Issue: Slow performance
The asyncio compatibility layer:
trio module references with fake Trio-like objectsopen_nursery() that returns an asyncio-based nurserywait_readable() using asyncio's add_reader()enable() before importing pyfuse3enable()/disable() multiple times