Skip to main content

Python bindings for io_uring with dynamic buffer size adjustment

Project description

pyuring

pyuring is a Python library for performing file I/O using the Linux io_uring kernel interface.

io_uring submits I/O operations to the kernel through a shared-memory ring buffer instead of issuing individual system calls per operation. This reduces per-operation syscall overhead, which is especially noticeable in workloads with many small or concurrent I/O operations.

pyuring exposes io_uring to Python through a C shared library (liburingwrap.so, built on top of liburing) and ctypes bindings. You do not need to understand the ring buffer mechanics to use the high-level API — but if you want direct control over submission and completion queues, that is available too.

Requires: Linux kernel 5.15+, Python 3.8+

Install

pip install pyuring

On glibc x86_64 Linux, pip installs a manylinux wheel that includes a pre-built liburingwrap.so — no separate liburing package is needed. For other platforms or source builds, see docs/INSTALLATION.md.

What pyuring provides

High-level file I/O helpers

The simplest way to use pyuring. copy, write, and write_many handle queue depth tuning, buffer management, and the io_uring pipeline internally.

import pyuring as iou

# Copy a file
iou.copy("/tmp/src.dat", "/tmp/dst.dat")

# Write a new file (100 MiB of data)
iou.write("/tmp/new.dat", total_mb=100)

# Write multiple files into a directory
iou.write_many("/tmp/out", nfiles=10, mb_per_file=100)

The mode parameter controls how queue depth and buffer size are tuned:

mode Behavior
"auto" (default) Starts with the default block size and increases it as the operation progresses. Uses the dynamic buffer C path.
"safe" Conservative settings: queue depth capped at 16 (copy) or 128 (write), block size capped at 1 MiB (copy) or 4 KiB (write).
"fast" Aggressive settings: queue depth at least 64 (copy) or 256 (write), block size at least 1 MiB (copy) or 64 KiB (write).

You can track progress or cancel an operation cooperatively using progress_cb:

def on_progress(done_bytes, total_bytes):
    print(f"{done_bytes} / {total_bytes} bytes")
    return False  # return True to cancel (raises UringError with errno.ECANCELED)

iou.copy("/tmp/src.dat", "/tmp/dst.dat", progress_cb=on_progress)

UringCtx — direct ring control

UringCtx wraps a single io_uring instance. Use it when you need to submit and receive completions manually, register fixed file descriptors or buffers, or configure the ring with specific setup flags.

import os
import pyuring as iou

with iou.UringCtx(entries=64) as ctx:
    fd = os.open("/tmp/data.bin", os.O_RDONLY)

    # Synchronous read (submits one SQE and waits for its CQE internally)
    data = ctx.read(fd, length=4096, offset=0)

    # Asynchronous: submit a read, then wait for its completion separately
    buf = bytearray(4096)
    ctx.read_async(fd, buf, offset=0, user_data=42)
    ctx.submit()
    user_data, result = ctx.wait_completion()
    # result is the number of bytes read, or a negative errno on error

You can pass IORING_SETUP_* flags to tune the ring at creation time:

ctx = iou.UringCtx(
    entries=128,
    setup_flags=iou.IORING_SETUP_SINGLE_ISSUER | iou.IORING_SETUP_COOP_TASKRUN,
)

UringAsync — asyncio integration

UringAsync integrates UringCtx with an asyncio event loop. It registers the ring's completion queue file descriptor (ring_fd) with the loop's reader, so await ua.wait_completion() returns as soon as a CQE is available — without blocking the event loop thread.

import asyncio
import pyuring as iou
from pyuring import UringAsync

async def main():
    with iou.UringCtx(entries=64) as ctx:
        async with UringAsync(ctx) as ua:
            fd = os.open("/tmp/data.bin", os.O_RDONLY)
            buf = bytearray(4096)
            ctx.read_async(fd, buf, user_data=1)
            ctx.submit()
            user_data, result = await ua.wait_completion()

asyncio.run(main())

BufferPool — native buffer management

BufferPool allocates and manages a set of fixed-size buffers in native memory. Use it with read_async / write_async when you want to avoid Python object allocation per I/O operation.

with iou.BufferPool.create(initial_count=8, initial_size=4096) as pool:
    ptr, size = pool.get_ptr(0)  # raw pointer to buffer slot 0
    ctx.read_async(fd, (ptr, size), user_data=0)
    ctx.submit()
    ctx.wait_completion()
    data = pool.get(0)  # read the result as bytes

Kernel capability probe

Before using a specific io_uring opcode, you can check at runtime whether the running kernel supports it. This is useful because opcode availability depends on the kernel version.

from pyuring import opcode_supported, require_opcode_supported, IORING_OP_SPLICE

# Returns True/False
if iou.opcode_supported(iou.IORING_OP_SPLICE):
    ...

# Raises UringError(errno.EOPNOTSUPP) if not supported
require_opcode_supported(iou.IORING_OP_SPLICE, "my_splice_op")

Error handling

All errors from the native layer raise UringError, which is a subclass of OSError. It carries three fields:

Field Content
errno The kernel errno value (same meaning as in os / OSError).
operation The name of the C wrapper function that failed (e.g. "uring_copy_path").
detail An optional string with additional context, such as search paths when liburingwrap.so cannot be found.
import errno
from pyuring import UringError

try:
    iou.copy("/tmp/src.dat", "/tmp/dst.dat")
except UringError as e:
    if e.errno == errno.ENOENT:
        print("source file not found")
    elif e.errno == errno.ECANCELED:
        print("cancelled by progress callback")
    print(f"failed in: {e.operation}")

Further reading

Document Contents
docs/USAGE.md Full API reference for all classes and functions
docs/INSTALLATION.md Build from source, liburing options, platform notes
docs/BENCHMARKS.md How to run the included benchmarks
docs/TESTING.md How to run the test suite

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyuring-0.3.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.6 kB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.9 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

pyuring-0.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (201.4 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file pyuring-0.3.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 988055e199ecdd46387e0b0ebf4a059346768641d7480e26e4c1882f4b8487f1
MD5 59b135fff7a2e65cb248d8dd4880e311
BLAKE2b-256 a27af27fba76e7d8eaf6e0427a1bfc293d25429e8e81fd5107168907d88c8754

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 56c78f2c3a96c3ac1b85a50d640e294abb9c4a48444560654e56ceff256d8305
MD5 08866416eb5efbeb402a319517006213
BLAKE2b-256 ea52d3e2b035ed71ffe77193dd3720fe5c4d560c4caea5b7c6828c3d9692770c

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9cd2bc8761aa23bfcda8f915bdc1b8cdc36a0a897a5f661caef0063e840f1245
MD5 674cf0f36312cf8ab3afb9ceed558568
BLAKE2b-256 d64ca142ea438e8062a92cf08b545549fd55fb22f9cdf5856a0ea9f4bd67991d

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 286c3d93428d72276b2e2508968b1468e1f5ba2e2254add2b5ec794d4097532b
MD5 0bd477da1541d2f6225f104a3da84519
BLAKE2b-256 91114b896cb64b8357ba535cb85c302690ce15988b564ef775fcbcfa3cf30fdd

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b8e691c03c9d6a8ebe29283179f85d4e61347e73dbc6b1f00060e9e5f2acf88d
MD5 b22c3fb5eb6f29ba82ddd0e3b0ca58f3
BLAKE2b-256 2d2e830626e08b30f264ea328efc5633aed1451f36679f25f2605d264a2bc531

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0cebc3801b02f3d5e3925b6c818d61fcca368223ea5ccbbb28e6e029598af7e9
MD5 049698acccf81c9e8f406ab432b2fc45
BLAKE2b-256 3458862fd16d621982af5c8bfd86c4dab8eca1d347c4e4a7738ba269579e1c0a

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 5b0c95543ffc510d95b5b91b8ced1259f353b581c72247ec5a3cce7281db190b
MD5 fd77096c323fcbc26fa3ced039fca1c1
BLAKE2b-256 4a982c7c449cb4164c7216397b5d84e183959432db77a3f160f18912b765fd29

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7843e05279433cc603f4b267339294aed97ded1fc08a0ee92b416731ee3e422d
MD5 f33294cfa48e1fb63e31d7a5cc504eea
BLAKE2b-256 1d7cde16dae8003be12c56aeeabcdee9c39db4c47721b3fcd6f0cafbe97b4a76

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7406387d389ae4691110c05cf6ffd40626450fb8cb01b60867804842232bde77
MD5 a22d9f027e127b47f9d0b07c5c6dd673
BLAKE2b-256 7bb2a8a0927deae5cbae0a6038e37602b3afc62fa4c4d0d91ac0f421e9d5dcc5

See more details on using hashes here.

File details

Details for the file pyuring-0.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyuring-0.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8cc4066b419822ab7f2fab91074b6fbe7420465ad46e196188da69afc1b2af1a
MD5 e3346fc66ef320b745c42052dfff25ce
BLAKE2b-256 8e7cac1e24288a058e60b2394dcf1bcd0dd3a07b4f5f4b5701d25cca72e22765

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page