Skip to main content

turbofile

Real async file I/O for Python.
A Rust core drives the best completion mechanism each OS has (io_uring on Linux, POSIX AIO on macOS) behind an aiofiles-compatible asyncio API.

CI Python 3.12+ Rust core License: MIT OR Apache-2.0

Installation · Usage · Benchmarks · Backends · Limitations · Development


Existing Python async-file libraries dispatch every call to a thread pool. turbofile submits the I/O to the kernel and completes your await when the kernel says the data moved:

  • Zero-copy reads and writes. The kernel fills the bytes object your await f.read(n) returns; writes pin your buffer and hand the kernel its pointer. No intermediate copies on the hot paths.
  • Completion batching. A burst of completions costs one event-loop wakeup: a byte on a pipe the loop watches, so the driver thread never takes the GIL to wake the loop, and the batch is drained entirely in Rust.
  • Parallel large reads. Read-all on a large file is split into 512 KiB chunks filled concurrently into one buffer, which spreads the page-cache copy across the kernel's I/O threads and beats a single blocking read.
  • Whole-file ops. read_bytes/write_bytes do open+read/write+close as a single submission: one round trip per file. Above 1 MiB, read_bytes takes the parallel-chunk fill instead, so the kernel writes the returned bytes directly with no intermediate copy.

Installation

First release pending; until it's on PyPI, build from source:

git clone https://github.com/4thel00z/turbofile
cd turbofile
make develop   # uv + maturin, builds the extension into the venv

Usage

import turbofile

async def main() -> None:
    async with turbofile.open("data.bin", "rb") as f:
        payload = await f.read()

    async with turbofile.open("log.txt", "a", encoding="utf-8") as f:
        await f.write("one line\n")

    data = await turbofile.read_bytes("data.bin")   # whole file, one op
    text = await turbofile.read_text("notes.md")

turbofile.open mirrors aiofiles.open: binary and text modes, buffering, encoding/errors/newline with full universal-newline semantics, seek/tell (text cookies follow CPython's _pyio scheme), readline, async iteration, readinto, truncate, fsync via sync. Migration is import turbofile as aiofiles for the open API.

Inside a running loop, read_bytes and write_bytes return the completion future itself rather than a coroutine, so asyncio.gather over many files schedules no task per file. They can still be awaited directly or wrapped in asyncio.create_task; outside a loop they return a coroutine, so asyncio.run(turbofile.read_bytes(path)) works too.

f.read_at(pos, n) and f.readinto_at(pos, buffer) read at a position without moving the file's own. Inside a running loop they return the completion future as well, and pages already resident settle it at once, so asyncio.gather over many positions reads them concurrently with no task per read.

Benchmarks

make bench compares against aiofiles on your machine: p50 per operation, median of three runs. On an Apple-silicon Mac (macOS 26.4, POSIX AIO backend, page-cache-hot files, mains power):

workload vs aiofiles
4 KiB read on an open file 59x
32 concurrent 4 KiB random reads 55x
200 small files read concurrently 14.0x
4 KiB whole-file read (read_bytes) 3.5x
8 MiB whole-file read (read_bytes) 1.4x
8 MiB whole-file read (open + read) 1.5x
8 MiB sequential write (1 MiB chunks) 1.0x

Both 8 MiB reads finish in about 0.31 ms, against 0.27 ms for a blocking read of the same bytes and 0.44 ms for aiofiles: at that size the copy is the floor and aiofiles pays its thread hop on top of it. The sixteen chunks copying in parallel on the kernel's AIO threads show at 64 MiB, where the parallel fill takes 2.7 ms against 8.9 ms for a read in a thread. Sequential writes await one chunk at a time, so one copy is in flight and the write row sits at the copy as well; turbofile wins where per-op overhead and concurrency dominate, which is what an asyncio application does.

Reads whose pages are already resident never leave the event-loop thread: no submission, no driver-thread hop, no completion wakeup. On Linux the kernel decides, through preadv2(RWF_NOWAIT); on macOS mincore on a mapping of the file reports which pages the buffer cache holds, and a plain pread copies them. Data that is not resident takes the async path. The table's 32 concurrent random reads take this route through read_at. make ladder isolates the read: its file_read rung is BinaryFile's positional read of a hot 4 KiB, its pread rung the blocking syscall for the same bytes.

platform file_read pread
Linux, ext4 1.3 us 1.1 us
macOS, APFS 1.2 us 0.5 us

On Linux read_bytes takes the same inline route for the whole file, open included, through openat2(RESOLVE_CACHED): 5.3 us for a hot 4 KiB file. macOS has no cached-only open, and an inline open there stalls on cold paths and endpoint-security scans (about 300 us for the first open of a file on a machine running Jamf Protect), so read_bytes keeps its open and close on the driver thread.

Benchmark on a real filesystem: tmpfs sets no FMODE_NOWAIT, which disables the fast path and makes io_uring punt every read to a kernel worker, so /tmp numbers are not comparable. make bench and make ladder take FS=<dir> and print which filesystem they measured. See perf/README.md for the analysis harness.

Backends

OS backend mechanism
Linux compio (fusion driver) io_uring, automatic polling fallback under seccomp/old kernels
macOS darwin-aio POSIX AIO (aio_read/aio_write/aio_fsync in XNU)
macOS compio (opt-in) kqueue polling driver with thread dispatch

TURBOFILE_BACKEND=compio selects the compio driver on macOS (benchmarking, or as an escape hatch). Windows (IOCP via compio) is planned.

Limitations

  • opener= and integer file descriptors are not supported.
  • Cancelling an await sends a kernel-level abort request (aio_cancel on macOS; the compio driver stops before the next chunk), best-effort by nature: an op the kernel finishes first settles with its result. Either way the CancelledError arrives only once the op has settled, so your buffer is never touched after the await raises.

Development

make develop   # build the extension into the venv (uv + maturin)
make test      # cargo test + pytest
make lint      # clippy -D warnings
make bench     # benchmark against aiofiles

License

MIT OR Apache-2.0

Metadata

Release files for turbofile 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for turbofile 0.1.5
File Size Uploaded
turbofile-0.1.5.tar.gz 60.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for turbofile 0.1.5
File Interpreter ABI Platform
turbofile-0.1.5-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 abi3 Linux glibc 2.17+ x86-64 Details
turbofile-0.1.5-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
turbofile-0.1.5-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details

Total release size: 1.5 MB

Release files / turbofile-0.1.5.tar.gz

Download URL turbofile-0.1.5.tar.gz
Size 60.2 kB
Tags Source
SHA-256 checksum
How to use checksums
3ad3924119c36c7edd78fade461f480ed7d95c98b4b5cd5f9595942c4d2be758
BLAKE2b-256 checksum
How to use checksums
70d0e6fa63257ebe4f0fd914252c93c69fedf5d6a61b202fd5756e428d9da91f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / turbofile-0.1.5-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL turbofile-0.1.5-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 502.0 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
6dddabc5c02601c6b9cb4601ae5dd446c5cccda4981d2bf4a72bc59101d05c1c
BLAKE2b-256 checksum
How to use checksums
5fdd5ec6789d3174d5a0e7e2308597c4e14e979d6126d1b83d9255b8d48e2e14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / turbofile-0.1.5-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL turbofile-0.1.5-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 498.1 kB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
b85e92c4736664e08484398504d0bacac7fe916f82f73cd01931b72128b1e294
BLAKE2b-256 checksum
How to use checksums
493009d0f77e3778c1a71cc8f7a687ff2435cd10e08d399f134a2cfd3448df1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / turbofile-0.1.5-cp312-abi3-macosx_11_0_arm64.whl

Download URL turbofile-0.1.5-cp312-abi3-macosx_11_0_arm64.whl
Size 471.4 kB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
fd5bc49fd1cb5cff6f964c357d84f72a3d231812c1ef403418ffa54fe90b67b6
BLAKE2b-256 checksum
How to use checksums
1707f31526acbc9eaa965849d2d235fd586ca887c659b6461021d6e9510ba6a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.5 This release

4 release files

0.1.4

4 release files

0.1.3

4 release files

0.1.2

4 release files

0.1.1

4 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page