Repository navigation
chore: Upgrade PyModbus 3.6.9 → 3.16.1 with comprehensive API migration - #18
Conversation
- Updated Python requirement from >=3.8.2 to >=3.10 - Upgraded PyModbus from 3.6.9 to 3.16.1 - Created compat module for BinaryPayloadBuilder (removed in PyModbus) - Migrated imports: Endian, framer classes to new locations - Updated datastore initialization to use ModbusDeviceContext - Changed framer usage from classes to FramerType enums - Fixed TLS server to use SSL context instead of certfile/keyfile - Updated test client calls to use device_id instead of slave parameter - Fixed data initialization bug (bit pattern mismatch) - Updated tox.ini to test Python 3.10-3.14 only Tests: 15/16 passing. Only TLS test timing out - needs investigation Co-authored-by: gavinying <10823762+gavinying@users.noreply.github.com>
Co-authored-by: gavinying <10823762+gavinying@users.noreply.github.com>
Co-authored-by: gavinying <10823762+gavinying@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
A critical mismatch between the CI workflow's Python matrix and the updated tox/pyproject version targets leaves the 3.10–3.14 acceptance criteria unmet alongside several unresolved supporting issues.
6 open issues
What changed in this PR
This PR upgrades PyModbus from 3.6.9 to 3.16.1, which removed/refactored several core APIs. Since a straight version bump is impossible, it adds a local compatibility shim (modsim/compat.py) reimplementing BinaryPayloadBuilder and an Endian constants class, and migrates the simulator to the new framer enum (FramerType), device-context datastore (ModbusDeviceContext with a +1 address offset to preserve ranges), and TLS sslctx-based server setup. It also raises the minimum Python to 3.10, updates the tox envlist, regenerates the lock/requirements, and fixes the TLS integration test to send a full MBAP-framed request. This keeps the CLI's TCP/UDP/TLS/serial behavior intact while targeting a modern PyModbus release (issue #16).
Changes:
- New
modsim/compat.pyshim providingBinaryPayloadBuilderandEndian; migratedmain.py/utils.pyto PyModbus 3.16.1 APIs (device context,FramerType,sslctx). - Dependency/toolchain updates:
pymodbus ~3.16.1, Python>=3.10, tox py310–py314, regeneratedpoetry.lock/requirements.txt. - Test updates:
slave=→device_id=throughout, and a corrected TLS request/response assertion using a full Modbus TCP frame.
| File | Description |
|---|---|
| modsim/compat.py | New compatibility layer reimplementing BinaryPayloadBuilder/Endian; has an unused Union import and an obfuscated float-format string. |
| modsim/main.py | Migrates datastore/identity/TLS to 3.16.1 APIs; adds per-branch returns and a fallback error; unused Union import. |
| modsim/utils.py | Switches framer selection to FramerType and drops the removed binary option (docs still reference it). |
| tests/test_modsim.py | Updates client calls to device_id= and fixes the TLS frame; new MBAP comment slightly miscounts header bytes. |
| tox.ini | Updates envlist to py310–py314 (CI workflow matrix not updated to match). |
| pyproject.toml | Raises Python to >=3.10 and pymodbus to ~3.16.1. |
| requirements.txt | Regenerated pin pymodbus==3.16.1 with updated marker. |
| poetry.lock | Regenerated with Poetry 2.5.1 / lock-version 2.1, which diverges from the pinned Poetry 1.8.3 toolchain. |
I focused the review on the migration correctness and dependency/CI consistency. The most consequential items are the CI matrix in .github/workflows/main.yml not being updated to the new 3.10–3.14 range (3.8/3.9 will fail, 3.13/3.14 untested) and the poetry.lock being regenerated with a newer Poetry than the one pinned across the toolchain. The remaining comments are smaller documentation/cleanliness items. I could not execute PyModbus 3.16.1 to validate the +1 datastore offset and the hand-written payload encoder at runtime, so those warrant a maintainer's confirmation.
Review effort: Balanced
| [tox] | ||
| skipsdist = true | ||
| envlist = py38, py39, py310, py311, py312 | ||
| envlist = py310, py311, py312, py313, py314 |
There was a problem hiding this comment.
Fixed — the CI matrix in .github/workflows/main.yml is now ["3.10", "3.11", "3.12", "3.13", "3.14"].
| fmt = f">{chr(102)}" # ">f" for big-endian float | ||
| packed = struct.pack(fmt, value) | ||
| value_int = struct.unpack(f">I", packed)[0] |
There was a problem hiding this comment.
Replaced the obfuscated format strings with literal ">f" and ">I" strings.
| parser.add_argument( | ||
| "--framer", | ||
| choices=["ascii", "binary", "rtu", "socket", "tls"], | ||
| choices=["ascii", "rtu", "socket", "tls"], |
There was a problem hiding this comment.
Updated both README.md and docs/index.md to drop the binary framing option from the serial transport description.
| response = b"" | ||
| while len(response) < 6: | ||
| part = client.recv(6 - len(response)) | ||
| # Expect MBAP header (6 bytes) + PDU response (at least 1 byte) |
There was a problem hiding this comment.
Fixed the comment to read "MBAP header (7 bytes)" to match the actual 7-byte MBAP header structure.
…ocs, test comment Co-authored-by: gavinying <10823762+gavinying@users.noreply.github.com>


PyModbus 3.16.1 removed or significantly refactored multiple core APIs (BinaryPayloadBuilder, Endian constants, framer classes, datastore contexts). This requires both a compatibility shim and systematic migration across the codebase.
Changes
Compatibility Layer (
modsim/compat.py- new file)BinaryPayloadBuilderusing struct module since it was removed entirely from PyModbusEndianclass to replace relocated constantsAPI Migration
FramerTypeenum values (e.g.,FramerSocket→FramerType.SOCKET)ModbusSlaveContext→ModbusDeviceContextwith address offset compensation (PyModbus internally uses address-1, requiring address+1 in block initialization to maintain address ranges)certfile/keyfileparameters tosslctx(SSL context object)slave=todevice_id=parameter throughout testsDependencies
>=3.8.2→>=3.10(PyModbus 3.16.1 requirement)poetry.lockandrequirements.txtTest Fixes
Impact
All 16 tests pass; full CLI behavior preserved across TCP, UDP, TLS, and serial transports. Code quality checks (mypy, linting, dependency analysis) passing.