Skip to main content

Composable I/O primitives for async Python

Project description

comio – Composable I/O

I/O primitives for async & sync Python. Designed to be used as:

import comio as io          # async
import comio.sync as io     # sync

Giving you io.Reader, io.Writer, io.Listener, etc. — a Go-like DX for Python.

Install

pip install comio

Usage

Async

Implement a Reader / Writer

Any object with the right method is a valid io.Reader or io.Writer — no base class needed.

import json
from dataclasses import dataclass
import comio as io

@dataclass
class Page:
    items: list
    next_cursor: object

class AsyncJsonL:
    def __init__(self, f):
        self.f = f

    async def read(self, *, cursor=None, n=None) -> Page:
        self.f.seek(cursor or 0)
        line = self.f.readline()
        if line == "":
            return Page([], io.EOF)
        return Page(items=[json.loads(line)], next_cursor=self.f.tell())

    async def write(self, item: dict) -> None:
        self.f.write(json.dumps(item) + "\n")

Read pages with scroll

reader = AsyncJsonL(open("data.jsonl"))

async for page in io.scroll(reader):
    print(page.items)

Drain everything with read_all

items = await io.read_all(reader)

Resume from a cursor

items = await io.read_all(reader, cursor=saved_cursor)

Normalize a Reader into a Listener with as_listener

listener = io.as_listener(reader)

async for item in listener.listen():
    process(item)

Buffered copy: Listener → Batcher

await io.copy(listener, batcher, n=100)  # flush every 100 items

Streaming pipe with backpressure

import comio as io
from comio import pipe, PipeConfig

listener = io.as_listener(reader)

async def transform(item):
    return {**item, "processed": True}

await pipe(listener, writer, transform, cfg=PipeConfig(buffer=10))

Glue pipes with asyncio queues

import asyncio
from comio import pipe
from comio.adapters.queue import QueueListener, QueueWriter

q: asyncio.Queue = asyncio.Queue()
glue_src = QueueListener(q)
glue_dst = QueueWriter(q)

async def transform(item):
    return {**item, "step": 1}

async def enrich(item):
    return {**item, "step": 2}

# pipe1: listener -> transform -> queue
await pipe(listener, glue_dst, transform)
glue_src.close()

# pipe2: queue -> enrich -> final destination
await pipe(glue_src, writer, enrich)

Sync

import json
from dataclasses import dataclass
import comio.sync as io

@dataclass
class Page:
    items: list
    next_cursor: object

class JsonL:
    def __init__(self, f):
        self.f = f

    def read(self, *, cursor=None, n=None) -> Page:
        self.f.seek(cursor or 0)
        line = self.f.readline()
        if line == "":
            return Page([], io.EOF)
        return Page(items=[json.loads(line)], next_cursor=self.f.tell())

    def write(self, item: dict) -> None:
        self.f.write(json.dumps(item) + "\n")
reader = JsonL(open("data.jsonl"))

for page in io.scroll(reader):
    print(page.items)

items = io.read_all(reader)

listener = io.as_listener(reader)

for item in listener.listen():
    process(item)

Protocols

Async (comio)

Protocol Method Description
Reader[T] read Pull a page of items
Listener[T] listen Push items as an async iterator
Writer[T] write Accept a single item
Batcher[T] batch Accept a sequence of items
Handler[I,O] __call__ Transform a single item

Sync (comio.sync)

Protocol Method Description
Reader[T] read Pull a page of items
Listener[T] listen Push items as an iterator
Writer[T] write Accept a single item
Batcher[T] batch Accept a sequence of items

Adapters

Adapter Module Implements
QueueListener comio.adapters.queue Listener
QueueWriter comio.adapters.queue Writer, Batcher
Stdin comio.adapters.std Listener
Stdout comio.adapters.std Writer, Batcher
TextFileReader comio.adapters.text_file Reader
TextFileWriter comio.adapters.text_file Writer, Batcher

Project details


Download files

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

Source Distribution

comio-0.3.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

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

comio-0.3.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file comio-0.3.0.tar.gz.

File metadata

  • Download URL: comio-0.3.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for comio-0.3.0.tar.gz
Algorithm Hash digest
SHA256 8acbfbcc98bed19f633159f2ac36a2d157d94f7c0ed12fe8097f5d4810b2fe5f
MD5 3d925b7d3aa19d30579476205bd02df5
BLAKE2b-256 48082a12794bf3e9c25fb826c6ef9d15a3fe964a387faebbc9173af9d1118c5a

See more details on using hashes here.

File details

Details for the file comio-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: comio-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for comio-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b044acd38eddad6cb9313111c80dc49b637ef1b91167330ff96820f34860d83
MD5 b5f6c92d436c0fb61575bd74f2a00ca2
BLAKE2b-256 53333c60619c2bf9f0127a53478e3a36351da8ad7d2966a20064310abc2d07f3

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