Skip to main content

Truly async serial port for Linux/macOS using epoll/kqueue

Project description

🟧 AUSerial

Truly async serial port for Linux/macOS using epoll/kqueue

PyPI Downloads Platform Python AsyncIO License

Why AUSerial?

AUSerial (Async Unix Serial) is a minimal, dependency-free async serial port for asyncio applications. It relies only on the standard library (os, termios, asyncio) and plugs directly into the event loop via add_reader / add_writer — which under the hood use epoll (Linux) or kqueue (macOS).

Comparison to existing librairies

Library Backend Cost
pyserial Blocking reads Freezes the event loop
aioserial run_in_executor around pyserial One thread per I/O operation
pyserial-asyncio Transport/Protocol callback API Verbose, subclass boilerplate
AUSerial Direct add_reader / add_writer Zero threads, zero polling

Features

  • 🪶 ~80 lines, no external dependencies — just the standard library
  • Truly non-blocking — no thread pool, no busy loop
  • 🔒 Concurrency-safe — internal locks prevent concurrent read/write conflicts
  • 🧹 Clean resource management — async context manager + idempotent close()
  • 🧯 Proper error propagation through Futures (no silent failures)
  • 🧵 Pending operations are cancelled cleanly on close

Installation

pip install auserial

Or from source:

git clone https://github.com/papyDoctor/auserial.git
cd auserial
pip install -e .

Quick Start

import asyncio
from auserial import AUSerial

async def main():
    async with AUSerial("/dev/ttyUSB0") as serial:
        await serial.write(b"AT\r\n")
        data = await serial.read()
        print(f"Received: {data!r}")

asyncio.run(main())

Custom baudrate

import termios
from auserial import AUSerial

async with AUSerial("/dev/ttyUSB0", baudrate=termios.B9600) as serial:
    ...

Timeout

import asyncio

from auserial import AUSerial


async def main():
    async with AUSerial("/dev/cu.usbmodem21301") as serial:
        await serial.write(b"AT\r\n")
        try:
            data = await asyncio.wait_for(serial.read(), timeout=1.0)
        except TimeoutError:
            print("No response within 1s")
        else:
            print(f"Received: {data!r}")


asyncio.run(main())

Discovering ports

from auserial import list_ports

for p in list_ports():
    print(p.path, p.description, p.hwid)
# /dev/cu.usbmodem21301  Raspberry Pi Pico  USB VID:PID=2E8A:0008 SER=E660B4400765AB25

list_ports() is synchronous and returns list[PortInfo]. On Linux it reads USB metadata from /sys/class/tty/<name>/device/. On macOS it parses ioreg output and links each /dev/cu.* to its USB ancestor (Bluetooth and debug consoles are filtered out). Pure stdlib, no extra dependency.

API

Method / function Description
AUSerial(path, baudrate=...) Opens the tty in non-blocking mode
await serial.open() Binds the instance to the current event loop
await serial.read(n_bytes=64) Waits until data is available, returns bytes
await serial.read_until(terminator=b"\n") Reads until the terminator is found, returns bytes
await serial.write(data) Waits until writable, returns bytes written
serial.close() Cancels pending I/O and closes the fd
list_ports() -> list[PortInfo] Enumerate available serial ports (sync)
PortInfo(path, description, hwid) NamedTuple returned by list_ports()

The AUSerial class also implements __aenter__ / __aexit__, so async with is the recommended usage pattern.

Limitations

  • Unix-only. Relies on termios and add_reader, which require an epoll/kqueue-compatible file descriptor. Windows needs a different implementation (IOCP).
  • A single call to write() issues one os.write — short writes are returned as-is (caller retries with the remainder if needed).

Examples

More usage patterns live in examples/.

License

MIT

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

auserial-1.0.1.tar.gz (18.7 kB view details)

Uploaded Source

Built Distribution

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

auserial-1.0.1-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file auserial-1.0.1.tar.gz.

File metadata

  • Download URL: auserial-1.0.1.tar.gz
  • Upload date:
  • Size: 18.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for auserial-1.0.1.tar.gz
Algorithm Hash digest
SHA256 ff5ca0f48e09071305c777b1b3f192c4e29e0ab94ca92aec94f6cc607ae6cd12
MD5 1c703ff333d2430b0e411c3d55a369ec
BLAKE2b-256 69b3e05839a4000725a5b61a3b95266f97eaa93801f61ae0954b74dcba54ce75

See more details on using hashes here.

File details

Details for the file auserial-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: auserial-1.0.1-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.14.3

File hashes

Hashes for auserial-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e7a5bb5018ee5781aa8ab09b048b5660db3c8d0f9c0ea16f1a11169e36301ca4
MD5 3674ed8375d5c9aa12b9b76a9514143e
BLAKE2b-256 9280f3f4a532807f3e98fe981c0a5ca70edee509373f2f803152caf607436bb4

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