Modern Python serial port library — pyserial replacement
Project description
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
Project details
Release history Release notifications | RSS feed
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 wireio-0.1.0.tar.gz.
File metadata
- Download URL: wireio-0.1.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97d6d8b0f34beac24c5b0d9f7d07bcfe5f4d9208a648e62f15a2dbf9558bb402
|
|
| MD5 |
b4877053bd9877350a8db5989766a547
|
|
| BLAKE2b-256 |
c77b5f2d14825d98f2eccda755fd2dbe6eecd335694d53a84729da2ee09cea65
|
File details
Details for the file wireio-0.1.0-py3-none-any.whl.
File metadata
- Download URL: wireio-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.7 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f345507080d7a7f5ee48ffb62aa8b50909f0a392aa07e4b28dfc3621424730eb
|
|
| MD5 |
a90fc68a60f7487e326b0385102594be
|
|
| BLAKE2b-256 |
21fda6cbbc6100f541c7c7a0840b40ac0f7f8f39f45945507476034f32b2813d
|