Truly async serial port for Linux/macOS using epoll/kqueue
Project description
🟧 AUSerial
Truly async serial port for Linux/macOS using epoll/kqueue
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.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
termiosandadd_reader, which require an epoll/kqueue-compatible file descriptor. Windows needs a different implementation (IOCP). - A single call to
write()issues oneos.write— short writes are returned as-is (caller retries with the remainder if needed).
Examples
More usage patterns live in examples/.
License
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file auserial-0.3.0.tar.gz.
File metadata
- Download URL: auserial-0.3.0.tar.gz
- Upload date:
- Size: 12.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef1da2b7a732215f22956b5ce70c11066eff7367d253533b3be71f8edab85494
|
|
| MD5 |
1859b41702b155dc05d23e2c81d8b5b4
|
|
| BLAKE2b-256 |
ed47ba3ad5e572ae39eea5ce74a4450784b0123b7b1f3060ba3bfe844c30c201
|
File details
Details for the file auserial-0.3.0-py3-none-any.whl.
File metadata
- Download URL: auserial-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3943984989fe0a9cd433d440e735be47e21168a4672dd82cc4c92e98407bcfdd
|
|
| MD5 |
8dc1fcdd63bee222488f18f16b3b97e9
|
|
| BLAKE2b-256 |
a195fbd6c9f862212a62c9f1f957c0bdf3c579c9df439eefe94f251f4cce3bf7
|