ProvenPin Install

aiofiles async file IO coordinated by anyio under pytest-asyncio: working versions (tested 2026-10-15)

Write and read files with aiofiles, fan out concurrent reads with an anyio task group, serialize appends with anyio.Lock, and test it all offline with pytest-asyncio.

passed6 passed · 0 failed2026-10-15 18:10 UTC
Status
passed
Last passed
2026-10-15 18:10 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions4
aiofiles@25.1.0anyio@4.15.1pytest-asyncio@1.4.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38783276983

The code

Exactly the files that passed. Your agent can pull them with get_recipe("aiofiles-anyio-temp-io") (install), or see them on GitHub.

requirements.txt
aiofiles==25.1.0
anyio==4.15.1
pytest-asyncio==1.4.0
pytest==9.1.1
io_utils.py
import aiofiles
import anyio


async def write_lines(path, lines):
    async with aiofiles.open(path, "w", encoding="utf-8") as f:
        for line in lines:
            await f.write(f"{line}\n")


async def read_lines(path):
    async with aiofiles.open(path, encoding="utf-8") as f:
        return [line.rstrip("\n") async for line in f]


async def read_many(paths):
    results = {}

    async def read_one(p):
        results[str(p)] = await read_lines(p)

    async with anyio.create_task_group() as tg:
        for p in paths:
            tg.start_soon(read_one, p)
    return results


async def append_locked(path, lines):
    lock = anyio.Lock()

    async def append(line):
        async with lock:
            async with aiofiles.open(path, "a", encoding="utf-8") as f:
                await f.write(f"{line}\n")

    async with anyio.create_task_group() as tg:
        for line in lines:
            tg.start_soon(append, line)
tests/test_io_utils.py
import aiofiles
import anyio
import pytest
from io_utils import append_locked, read_lines, read_many, write_lines


async def test_roundtrip_write_then_read(tmp_path):
    p = tmp_path / "a.txt"
    await write_lines(p, ["x", "y"])
    assert await read_lines(p) == ["x", "y"]


async def test_concurrent_reads_with_task_group(tmp_path):
    paths = []
    for i in range(5):
        p = tmp_path / f"{i}.txt"
        await write_lines(p, [f"line-{i}"])
        paths.append(p)
    got = await read_many(paths)
    assert len(got) == 5 and got[str(paths[3])] == ["line-3"]


async def test_write_after_close_raises(tmp_path):
    f = await aiofiles.open(tmp_path / "b.txt", "w", encoding="utf-8")
    await f.write("hello")
    await f.close()
    with pytest.raises(ValueError):
        await f.write("again")


async def test_anyio_lock_serializes_appends(tmp_path):
    p = tmp_path / "log.txt"
    await append_locked(p, [str(n) for n in range(50)])
    assert sorted(await read_lines(p), key=int) == [str(n) for n in range(50)]


async def test_anyio_path_interops_with_aiofiles(tmp_path):
    p = tmp_path / "c.txt"
    await anyio.Path(p).write_text("from anyio")
    assert await read_lines(p) == ["from anyio"]


async def test_task_group_wraps_errors_in_exception_group(tmp_path):
    with pytest.raises(ExceptionGroup):
        await read_many([tmp_path / "missing.txt"])
pytest.ini
[pytest]
pythonpath = .
asyncio_mode = auto
asyncio_default_fixture_loop_scope = function

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.