wireio
Modern Python serial port library — a drop-in replacement for pyserial.
- Python 3.10+ with full type annotations (PEP 561)
- Zero dependencies — stdlib only
- Cross-platform — Linux, macOS, Windows
- Built-in async —
AsyncSerialwith native asyncio support - Port discovery —
list_ports()enumerates available serial devices - CLI tools —
wireio-minitermandwireio-list-ports
Why wireio?
pyserial is used by 100,000+ projects but has had no release since November 2020. The single maintainer is unreachable, 275+ issues are open, and the library has no type hints, no async support, and still targets Python 2.7.
wireio is a modern replacement:
| pyserial | wireio | |
|---|---|---|
| Last release | Nov 2020 | Active |
| Dependencies | None | None |
| Python support | 2.7, 3.4–3.8 | 3.10+ (including 3.13+) |
| Type hints | No | Full PEP 561 |
| Async support | Separate package | Built-in AsyncSerial |
| API style | Old-style class | Dataclass config + enums |
Installation
pip install wireio
Quick Start
Synchronous
from wireio import Serial
with Serial("/dev/ttyUSB0", baudrate=115200) as port:
port.write(b"AT\r\n")
response = port.read_until(b"\r\n")
print(response)
Asynchronous
import asyncio
from wireio import AsyncSerial
async def main():
async with AsyncSerial("/dev/ttyUSB0", baudrate=9600) as port:
await port.write(b"hello")
data = await port.read(100)
print(data)
asyncio.run(main())
Port Discovery
from wireio import list_ports
for port in list_ports():
print(f"{port.device} — {port.description}")
Dataclass Config
from wireio import Serial, SerialConfig, Parity, StopBits
config = SerialConfig(
baudrate=115200,
parity=Parity.EVEN,
stopbits=StopBits.TWO,
timeout=1.0,
)
with Serial("/dev/ttyS0", config=config) as port:
port.write(b"data")
API Reference
Serial(port, baudrate=9600, **kwargs)
Main serial port class. Platform-specific backend is selected automatically (POSIX on Linux/macOS, Win32 on Windows).
Constructor parameters:
port— device path (/dev/ttyUSB0,COM3)baudrate— baud rate (default:9600)bytesize—ByteSize.FIVEthroughByteSize.EIGHT(default:EIGHT)parity—Parity.NONE,EVEN,ODD,MARK,SPACE(default:NONE)stopbits—StopBits.ONE,ONE_POINT_FIVE,TWO(default:ONE)timeout— read timeout in seconds (None= blocking,0= non-blocking, default:None)write_timeout— write timeout in seconds (None= blocking, default:None)flow_control—FlowControl.NONE,HARDWARE,SOFTWARE(default:NONE)xonxoff— enable XON/XOFF software flow control (default:False)rtscts— enable RTS/CTS hardware flow control (default:False)dsrdtr— enable DSR/DTR hardware flow control (default:False)inter_byte_timeout— timeout between consecutive bytes in seconds (None= disabled, default:None)config—SerialConfigobject (overrides individual params when provided)
Methods:
open()/close()— open or close the portread(size=1) -> bytes— read up tosizebytes; returns fewer if timeout expireswrite(data) -> int— write bytes, returns count writtenflush()— wait until all data transmittedread_until(delimiter=b"\n", size=0) -> bytes— read until delimiter found;size=0means no limitread_line() -> bytes— read until\nread_exactly(size) -> bytes— read exactlysizebytes, blocking until all receivedconfigure(config)— apply newSerialConfigto an open port
Properties:
is_open— whether the port is openin_waiting— bytes available in input bufferport,baudrate,bytesize,parity,stopbits,timeout,config
Supports the context manager protocol (with Serial(...) as port:).
AsyncSerial(port, baudrate=9600, **kwargs)
Async wrapper around Serial. Runs blocking I/O in a thread executor via asyncio.run_in_executor. Same constructor parameters as Serial, plus:
loop(asyncio.AbstractEventLoop | None) — event loop to use (default: current running loop)
All I/O methods are async and mirror the Serial interface:
async with AsyncSerial("/dev/ttyUSB0", baudrate=9600) as port:
await port.write(b"hello")
data = await port.read(100)
line = await port.read_line()
exact = await port.read_exactly(4)
until = await port.read_until(b"\r\n")
await port.flush()
await port.configure(SerialConfig(baudrate=115200))
Async methods: open, close, read, write, flush, read_until, read_line, read_exactly, configure
Properties (sync): port, is_open, baudrate, config
Supports the async context manager protocol (async with AsyncSerial(...) as port:).
SerialConfig
Dataclass holding all serial port settings. Pass as the config= argument to Serial or AsyncSerial, or use port.configure(config) to reconfigure an open port.
from wireio import SerialConfig, Parity, StopBits, ByteSize, FlowControl
config = SerialConfig(
baudrate=115200,
bytesize=ByteSize.EIGHT,
parity=Parity.NONE,
stopbits=StopBits.ONE,
timeout=1.0,
write_timeout=None,
flow_control=FlowControl.NONE,
inter_byte_timeout=None,
)
Fields (all optional, defaults match Serial constructor):
baudrate: int— default9600bytesize: ByteSize— defaultByteSize.EIGHTparity: Parity— defaultParity.NONEstopbits: StopBits— defaultStopBits.ONEtimeout: float | None— defaultNonewrite_timeout: float | None— defaultNoneflow_control: FlowControl— defaultFlowControl.NONExonxoff: bool— defaultFalsertscts: bool— defaultFalsedsrdtr: bool— defaultFalseinter_byte_timeout: float | None— defaultNone
SerialConfig.validate() raises ConfigError for invalid values (negative baudrate, negative timeouts, etc.).
list_ports() -> list[PortInfo]
Enumerate available serial ports. Returns PortInfo objects with:
device— device path (e.g./dev/ttyUSB0,COM3)name— short port namedescription— human-readable descriptionhwid— hardware ID stringvid,pid— USB vendor/product IDs (orNone)serial_number,manufacturer,product— USB metadata (orNone)
Enums
Parity—NONE,EVEN,ODD,MARK,SPACEStopBits—ONE,ONE_POINT_FIVE,TWOByteSize—FIVE,SIX,SEVEN,EIGHTFlowControl—NONE,HARDWARE,SOFTWARE
Exceptions
All exceptions inherit from SerialError:
SerialError— base exception for all serial port errorsPortNotFoundError— port not found or permission deniedConfigError— invalid configuration valueSerialTimeoutError— operation timed out
CLI Tools
wireio-list-ports
Print a table of available serial ports:
wireio-list-ports
# or
python -m wireio.tools.list_ports
Output example:
DEVICE DESCRIPTION HWID
-------------------------------------------------
/dev/ttyUSB0 USB Serial Device USB VID:PID=0403:6001
/dev/ttyS0 ttyS0 n/a
wireio-miniterm
Interactive serial terminal:
wireio-miniterm /dev/ttyUSB0 115200
# or
python -m wireio.tools.miniterm /dev/ttyUSB0 115200 --echo --eol crlf
Options:
port— serial port device path (required)baudrate— baud rate (default:9600)--encoding— character encoding for display and input (default:utf-8)--echo— enable local echo of typed input--eol {cr,lf,crlf}— line ending appended to transmitted lines (default:crlf)
Press Ctrl+C to exit.
Migration from pyserial
wireio is designed as a drop-in replacement for pyserial. Key differences:
| pyserial | wireio |
|---|---|
import serial |
from wireio import Serial |
serial.Serial(...) |
Serial(...) |
serial.tools.list_ports.comports() |
wireio.list_ports() |
serial.SerialException |
wireio.SerialError |
serial.serialutil.SerialTimeoutException |
wireio.SerialTimeoutError |
Separate pyserial-asyncio package |
Built-in AsyncSerial |
| No type hints | Full PEP 561 type hints |
| Python 2.7+ | Python 3.10+ |
Common patterns
# pyserial
import serial
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.write(b'hello')
data = ser.read(100)
ser.close()
# wireio (same pattern works)
from wireio import Serial
ser = Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.open()
ser.write(b'hello')
data = ser.read(100)
ser.close()
# wireio (preferred — context manager)
with Serial('/dev/ttyUSB0', baudrate=9600, timeout=1) as ser:
ser.write(b'hello')
data = ser.read(100)
License
MIT
Metadata
Release files for wireio 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wireio-0.1.0.tar.gz | 20.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wireio-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.2 kB
Release files / wireio-0.1.0.tar.gz
| Download URL | wireio-0.1.0.tar.gz |
|---|---|
| Size | 20.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
97d6d8b0f34beac24c5b0d9f7d07bcfe5f4d9208a648e62f15a2dbf9558bb402
|
|
BLAKE2b-256 checksum How to use checksums |
c77b5f2d14825d98f2eccda755fd2dbe6eecd335694d53a84729da2ee09cea65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / wireio-0.1.0-py3-none-any.whl
| Download URL | wireio-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f345507080d7a7f5ee48ffb62aa8b50909f0a392aa07e4b28dfc3621424730eb
|
|
BLAKE2b-256 checksum How to use checksums |
21fda6cbbc6100f541c7c7a0840b40ac0f7f8f39f45945507476034f32b2813d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|