aiopath: Async pathlib for Python

raw JSON →
0.7.7 verified Thu Apr 16 auth: no python

aiopath provides an asynchronous implementation of Python's standard `pathlib` module, enabling non-blocking file system operations within `asyncio`, `trio`, and other `async`/`await` compatible frameworks. It mirrors `pathlib`'s API, making it familiar for users who need to perform I/O-bound tasks concurrently without blocking the event loop. The library is currently at version 0.7.7 and maintains an active development, addressing compatibility with newer Python versions and bug fixes.

pip install aiopath
error ImportError: cannot import name '_NormalAccessor' from 'pathlib' (Python 3.11)
cause An older version of `aiopath` attempted to import a private internal class from `pathlib` that was removed in Python 3.11.
fix
Upgrade aiopath to the latest version using pip install --upgrade aiopath. Version 0.7.0 and newer are designed for Python 3.11+ compatibility.
error TypeError: 'async for' requires an object with __aiter__ method, got generator
cause This error occurs when attempting to use `async for` with `AsyncPath.walk()`, indicating that the method returned a regular (synchronous) generator instead of an asynchronous one.
fix
Verify your aiopath version. If the issue persists, manually iterate using AsyncPath.iterdir() and is_dir() for a recursive async walk, or await an official fix/workaround for AsyncPath.walk() in the library.
error RuntimeWarning: coroutine '...' was never awaited
cause You called an asynchronous method on an `AsyncPath` object (e.g., `my_path.exists()`) but forgot to `await` its result.
fix
Ensure all I/O-performing methods of AsyncPath are properly awaited, like await my_path.exists() or await my_path.resolve().
breaking Older `aiopath` versions (prior to 0.7.x) are incompatible with Python 3.11+ due to reliance on internal `pathlib` APIs that were removed, leading to `ImportError`.
fix Upgrade `aiopath` to version `0.7.0` or higher to ensure compatibility with Python 3.11 and newer. For Python 3.12, version `0.7.6` or higher is recommended.
gotcha The `AsyncPath.walk()` method, as of some versions, may incorrectly return a synchronous generator, causing a `TypeError` when used with `async for`.
fix Check for updates to `aiopath` that fix `AsyncPath.walk()` to return an `AsyncGenerator`. If not fixed, consider implementing a custom async walk or using `AsyncPath.iterdir()` for recursive traversal.
gotcha Calling asynchronous methods (e.g., `resolve()`, `exists()`, `read_text()`) on `AsyncPath` objects without `await` will result in a `RuntimeWarning` (coroutine '...' was never awaited) and the operation will not execute.
fix Always prepend `await` to `AsyncPath` methods that perform I/O operations, such as `await my_path.exists()` or `await my_path.read_text()`.

This quickstart demonstrates basic asynchronous file operations using `AsyncPath`, including creating a directory, writing text to a file, reading its content, checking its properties, and cleaning up the created files and directories.

import asyncio
from aiopath import AsyncPath
from pathlib import Path
import os

async def main():
    # Ensure a directory for testing exists
    test_dir = AsyncPath("aiopath_test_dir")
    if await test_dir.exists():
        await test_dir.rmdir()
    await test_dir.mkdir()

    file_path = test_dir / "my_async_file.txt"
    content = "Hello, async world!\nThis is a test."

    # Write text asynchronously
    await file_path.write_text(content)
    print(f"Wrote to {file_path.name}:")

    # Read text asynchronously
    read_content = await file_path.read_text()
    print(f"Read from {file_path.name}:\n---\n{read_content}--- ")

    # Check if file exists and is a file
    print(f"Does {file_path.name} exist? {await file_path.exists()}")
    print(f"Is {file_path.name} a file? {await file_path.is_file()}")

    # Clean up
    await file_path.unlink()
    await test_dir.rmdir()
    print(f"Cleaned up {file_path.name} and {test_dir.name}.")

if __name__ == "__main__":
    asyncio.run(main())