Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ jobs:
--cov=serialx \
-o console_output_style=count \
-p no:sugar \
--adapter-pair /dev/tnt0,/dev/tnt1,no-rts-cts,no-dtr-dsr,no-num-unwritten-bytes,no-reset-write-buffer,no-write-timeout,no-buffer-control \
--adapter-pair /dev/tnt0,/dev/tnt1,no-rts-cts,no-dtr-dsr,no-num-unwritten-bytes,no-reset-write-buffer,no-write-timeout,no-buffer-control,no-write-buffering \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down Expand Up @@ -502,7 +502,7 @@ jobs:
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.13
bun-version: 1.4.2
- name: Cache Pyodide package wheels
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
Expand Down
8 changes: 8 additions & 0 deletions docs/how-to/async-serial.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ pins = await serial.get_modem_pins()
assert pins.rts is serialx.PinState.HIGH
```

### Reconfiguring
`reconfigure_port` changes settings on the open port. Only the settings passed are
changed:

```python
await serial.reconfigure_port(baudrate=9600, parity=serialx.Parity.EVEN)
```

## Async protocols and transports
While the high-level async API is useful for simple code, libraries and other
high-performance uses should use asyncio transports and protocols. These have the
Expand Down
6 changes: 5 additions & 1 deletion docs/how-to/pyodide.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ reader, writer = await serialx.open_serial_connection(

writer.write(b"ping")
data = await reader.readexactly(4)
```
```

## Limitations
- Web Serial only accepts port settings on open. `reconfigure_port()` closes and reopens the browser port, which drops DTR and RTS and discards unread bytes. A warning is logged.
- Software flow control (`xonxoff=True`) is accepted but has no effect.
30 changes: 30 additions & 0 deletions docs/how-to/pyserial-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ are listed in the [main API documentation](../api.md). The most common ones are
| `Serial(do_not_open=False)` | — | Not supported; open explicitly via `open()` |
| `Serial(rtsdtr_on_open=X)` | `dtr_on_open=X, rts_on_open=X` | Now controlled per-pin |
| `Serial(rtsdtr_on_close=X)` | `dtr_on_close=X, rts_on_close=X` | Now controlled per-pin |
| `baudrate = X` | `reconfigure_port(baudrate=X)` | Setters are deprecated |
| `stop_bits = X` | `reconfigure_port(stopbits=X)` | Setters are deprecated |
| `data_bits = X` | `reconfigure_port(byte_size=X)` | Setters are deprecated |
| `SerialPortInfo[i]` | attribute access | Slicing `SerialPortInfo` is deprecated |
| `SerialPortInfo.description`| `SerialPortInfo.product` | |

Expand All @@ -87,6 +90,25 @@ with serialx.serial_for_url("/dev/ttyUSB0", baudrate=115200) as serial:

There is no equivalent for async code because the default `create_serial_connection` and `open_serial_connection` functions already transparently accept URIs.

### Changing port settings
Use `reconfigure_port(...)` instead of assigning to `baudrate` and other setting properties. Only the settings passed are changed:

```diff
-serial.baudrate = 9600
-serial.parity = serial.PARITY_EVEN
+serial.reconfigure_port(baudrate=9600, parity=serialx.Parity.EVEN)
```

Configuring an unopened port through its properties is deprecated. Pass settings to the `serialx.serial_for_url` constructor:

```diff
-serial = serial.Serial()
-serial.baudrate = 9600
-serial.open()
+serial = serialx.serial_for_url("/dev/ttyUSB0", baudrate=9600)
+serial.open()
```

## Constants
pyserial exposes parity, stop bit, and byte size settings as module-level constants (`serial.PARITY_NONE`, `serial.STOPBITS_ONE`, etc.). serialx replaces them with the `Parity` and `StopBits` enums. Properties like `serial.parity` and `serial.stopbits` now return enum members instead of raw strings or numbers.

Expand Down Expand Up @@ -148,6 +170,14 @@ if pins.cts is serialx.PinState.HIGH:

`set_modem_pins` accepts individual pin kwargs or a full `ModemPins` dataclass. Pins omitted from the call are left unchanged. `get_modem_pins` returns a `ModemPins` dataclass of `PinState` enum values, call `.to_bool()` on a pin for a `bool | None`.

### Changing port settings
Assigning to `transport.serial.baudrate` reconfigures the port on the event loop, which is blocking IO. Use the async method on the transport instead:

```diff
-transport.serial.baudrate = 9600
+await transport.reconfigure_port(baudrate=9600)
```

### Simplified async API
If you have existing sync code using `serial_for_url` and want to make it async, use `async_serial_for_url`. The method names match the sync API (e.g. `read`, `readexactly`, `readline`, `readuntil`, `write`, `flush`) so the migration is mostly adding `async`/`await`:

Expand Down
14 changes: 14 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,17 @@ transport, protocol = await serialx.create_serial_connection(
baudrate=115200,
)
```

## Reconfiguring a port
`reconfigure_port` changes settings on an open port. Only the settings passed are changed:

```python
serial.reconfigure_port(baudrate=9600, parity=serialx.Parity.EVEN)
```

The async APIs expose the same method as a coroutine:

```python
await serial.reconfigure_port(baudrate=9600)
await transport.reconfigure_port(baudrate=9600)
```
22 changes: 22 additions & 0 deletions serialx/async_serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,28 @@ async def set_modem_pins(
dsr=dsr,
)

async def reconfigure_port(
self,
*,
baudrate: int | None = None,
parity: Parity | str | None = None,
stopbits: StopBits | int | float | None = None,
byte_size: int | None = None,
xonxoff: bool | None = None,
rtscts: bool | None = None,
dsrdtr: bool | None = None,
) -> None:
"""Change serial port settings. Only the settings passed are changed."""
await self.transport.reconfigure_port(
baudrate=baudrate,
parity=parity,
stopbits=stopbits,
byte_size=byte_size,
xonxoff=xonxoff,
rtscts=rtscts,
dsrdtr=dsrdtr,
)

# ---- Settings (proxy to transport) ----

@property
Expand Down
Loading
Loading