🟧 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.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
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
Release files for auserial 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| auserial-1.0.1.tar.gz | 18.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| auserial-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.6 kB
Release files / auserial-1.0.1.tar.gz
| Download URL | auserial-1.0.1.tar.gz |
|---|---|
| Size | 18.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ff5ca0f48e09071305c777b1b3f192c4e29e0ab94ca92aec94f6cc607ae6cd12
|
|
BLAKE2b-256 checksum How to use checksums |
69b3e05839a4000725a5b61a3b95266f97eaa93801f61ae0954b74dcba54ce75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / auserial-1.0.1-py3-none-any.whl
| Download URL | auserial-1.0.1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e7a5bb5018ee5781aa8ab09b048b5660db3c8d0f9c0ea16f1a11169e36301ca4
|
|
BLAKE2b-256 checksum How to use checksums |
9280f3f4a532807f3e98fe981c0a5ca70edee509373f2f803152caf607436bb4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|