modbus-crc 2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
modbus_crc/__init__.py ADDED
@@ -0,0 +1,55 @@
1
+ """
2
+ CRC-16 calculation for the Modbus protocol.
3
+
4
+ The checksum is CRC-16/MODBUS: polynomial 0xA001 (reflected 0x8005),
5
+ initial value 0xFFFF, no final XOR, transmitted low byte first.
6
+
7
+ Copyright (c) 2026 webtoucher
8
+ Distributed under the BSD 3-Clause license. See LICENSE for more info.
9
+ """
10
+
11
+ from typing import Union
12
+
13
+ __all__ = ["crc16", "add_crc", "check_crc"]
14
+
15
+ __version__ = "2.0"
16
+
17
+ BytesLike = Union[bytes, bytearray, memoryview]
18
+
19
+
20
+ def _build_crc_table(polynomial: int = 0xA001) -> tuple:
21
+ """Build the 256-entry lookup table for CRC-16/MODBUS."""
22
+ table = []
23
+ for index in range(256):
24
+ crc = index
25
+ for _ in range(8):
26
+ crc = (crc >> 1) ^ polynomial if crc & 1 else crc >> 1
27
+ table.append(crc)
28
+ return tuple(table)
29
+
30
+
31
+ _CRC_TABLE = _build_crc_table()
32
+
33
+
34
+ def _crc_register(data: BytesLike) -> int:
35
+ """Return the raw 16-bit CRC register value of data."""
36
+ crc = 0xFFFF
37
+ for byte in data:
38
+ crc = (crc >> 8) ^ _CRC_TABLE[(crc ^ byte) & 0xFF]
39
+ return crc
40
+
41
+
42
+ def crc16(data: BytesLike) -> bytes:
43
+ """Calculate CRC-16/MODBUS of data, low byte first as on the wire."""
44
+ crc = _crc_register(data)
45
+ return bytes((crc & 0xFF, crc >> 8))
46
+
47
+
48
+ def add_crc(package: BytesLike) -> bytes:
49
+ """Return package with CRC-16/MODBUS appended."""
50
+ return bytes(package) + crc16(package)
51
+
52
+
53
+ def check_crc(package: BytesLike) -> bool:
54
+ """Return True if package carries a valid trailing CRC-16/MODBUS."""
55
+ return _crc_register(package) == 0
modbus_crc/py.typed ADDED
File without changes
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: modbus-crc
3
+ Version: 2.0
4
+ Summary: CRC-16 calculation for Modbus protocol
5
+ Author-email: Alexey Kuznetsov <webtoucher@ya.ru>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/webtoucher/modbus-crc
8
+ Project-URL: Repository, https://github.com/webtoucher/modbus-crc
9
+ Project-URL: Issues, https://github.com/webtoucher/modbus-crc/issues
10
+ Keywords: modbus,crc,crc16,checksum,serial,rtu
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Operating System :: Unix
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE.md
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # Modbus CRC-16
27
+
28
+ ![License](https://img.shields.io/badge/License-BSD%203--Clause-green)
29
+ [![Downloads](https://img.shields.io/pypi/dm/modbus-crc.svg?color=orange)](https://pypi.python.org/pypi/modbus-crc)
30
+ [![Latest Version](https://img.shields.io/pypi/v/modbus-crc.svg)](https://pypi.python.org/pypi/modbus-crc)
31
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/modbus-crc.svg)](https://pypi.python.org/pypi/modbus-crc)
32
+ [![tests](https://github.com/webtoucher/modbus-crc/actions/workflows/tests.yml/badge.svg)](https://github.com/webtoucher/modbus-crc/actions/workflows/tests.yml)
33
+
34
+ CRC-16 calculation library for Modbus protocol. Tiny, dependency-free and fast: a single
35
+ 256-entry lookup table, no runtime dependencies, type hints included.
36
+
37
+ ## Algorithm
38
+
39
+ The checksum is **CRC-16/MODBUS**:
40
+
41
+ | Parameter | Value |
42
+ |-----------------------|-------------------------------------------|
43
+ | Width | 16 bits |
44
+ | Polynomial | `0x8005` (reflected: `0xA001`) |
45
+ | Initial value | `0xFFFF` |
46
+ | Reflect in / out | yes / yes |
47
+ | Final XOR | `0x0000` |
48
+ | Check (`"123456789"`) | `0x4B37`, transmitted as `b'\x37\x4B'` |
49
+ | Byte order on wire | low byte first |
50
+
51
+ ## Installation
52
+
53
+ Install it with pip:
54
+
55
+ ```shell
56
+ $ pip install modbus-crc
57
+ ```
58
+
59
+ Or you can add it as dependency in requirements.txt file of your python application:
60
+
61
+ ```
62
+ modbus-crc~=2.0
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ For signing byte package use `add_crc`:
68
+
69
+ ```python
70
+ from modbus_crc import add_crc
71
+
72
+ signed_package = add_crc(b'\x00\x12\x34\xAB\xCD\xEF')
73
+ ```
74
+
75
+ Result is `b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'`. For validation signed package use `check_crc`:
76
+
77
+ ```python
78
+ from modbus_crc import check_crc
79
+
80
+ if not check_crc(b'\x00\x12\x34\xAB\xCD\xEF\xD2\xD4'):
81
+ raise SomeException()
82
+ ```
83
+
84
+ To get the checksum alone (low byte first, exactly as it is transmitted on the wire), use `crc16`:
85
+
86
+ ```python
87
+ from modbus_crc import crc16
88
+
89
+ crc16(b'\x01\x03\x00\x00\x00\x0A') # b'\xC5\xCD'
90
+ ```
91
+
92
+ Here is a real Modbus RTU request — "read 10 holding registers from slave 1":
93
+
94
+ ```python
95
+ add_crc(b'\x01\x03\x00\x00\x00\x0A') # b'\x01\x03\x00\x00\x00\x0A\xC5\xCD'
96
+ ```
97
+
98
+ `check_crc` validates the whole signed frame, so it works with data read straight from
99
+ the serial port, without splitting off the trailing bytes:
100
+
101
+ ```python
102
+ check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCD') # True
103
+ check_crc(b'\x01\x03\x00\x00\x00\x0A\xC5\xCE') # False
104
+ ```
105
+
106
+ ## API
107
+
108
+ All three functions accept `bytes`, `bytearray` and `memoryview` (declared as `BytesLike`).
109
+
110
+ | Function | Returns | Description |
111
+ |---------------------------------|---------|-----------------------------------------------------------------|
112
+ | `crc16(data)` | `bytes` | 2-byte checksum of `data`, low byte first |
113
+ | `add_crc(package)` | `bytes` | `package` with the checksum appended; input type is normalized |
114
+ | `check_crc(package)` | `bool` | `True` if `package` carries a valid trailing checksum |
115
+
116
+ Notes:
117
+
118
+ - `add_crc` always returns `bytes`, even for `bytearray` and `memoryview` input.
119
+ - `check_crc(b'')` is `False`: an empty package has no valid checksum.
120
+ - Functions are pure: they keep no state between calls. The lookup table is built once,
121
+ at import time.
122
+
123
+ ## Compatibility
124
+
125
+ - Python **3.9 and newer**, including free-threaded builds. Tested on 3.9 and 3.15 and
126
+ in CI on every version from 3.9 to 3.15.
127
+ - No runtime dependencies.
128
+ - Type hints are shipped with the package (`py.typed`, PEP 561), so mypy and pyright
129
+ see real signatures instead of `Any`.
130
+
131
+ ## Development
132
+
133
+ ```shell
134
+ $ python -m venv .venv
135
+ $ .venv\Scripts\activate # Windows; on POSIX: source .venv/bin/activate
136
+ $ pip install -e ".[dev]" # installs the package and pytest
137
+ $ python -m pytest
138
+ ```
139
+
140
+ The test suite covers:
141
+
142
+ - canonical Modbus frames with published checksums,
143
+ - an independent bit-by-bit implementation written from the specification, used as an
144
+ oracle against the fast table-driven code (including random payloads up to 1 KiB),
145
+ - edge cases: empty package, one-byte packages, `bytearray` / `memoryview` input,
146
+ - detection of every single-byte corruption of a signed frame.
147
+
148
+ Project layout:
149
+
150
+ ```
151
+ modbus_crc/__init__.py the whole library
152
+ tests/test_modbus_crc.py test suite
153
+ pyproject.toml metadata, build config and pytest settings
154
+ .github/workflows/ CI: tests on Python 3.9-3.15, sdist/wheel build check
155
+ ```
156
+
157
+ To build and verify a release:
158
+
159
+ ```shell
160
+ $ pip install build twine
161
+ $ python -m build
162
+ $ python -m twine check dist/*
163
+ ```
164
+
165
+ ## Migrating from 1.x
166
+
167
+ The public API is unchanged, so most projects need no code changes. What did change:
168
+
169
+ - **Python 3.9+ is required.** Python 2 support and the legacy `setup.py` / `setup.cfg`
170
+ are gone; metadata now lives in `pyproject.toml` (PEP 621).
171
+ - **`LOW_BYTES` and `HIGH_BYTES` were removed.** Those were two 256-byte tables with
172
+ inverted naming that leaked from the implementation. The lookup table is now generated
173
+ at import time and is private — use the documented functions instead.
174
+ - **`add_crc` always returns `bytes`.** Previously `add_crc(bytearray(...))` returned a
175
+ `bytearray`; the result type is now stable regardless of the input type.
176
+ - **`modbus_crc.__version__` is available** and is the single source of truth for the
177
+ package version.
178
+ - **The package ships `py.typed`**, so type checkers no longer treat it as untyped.
179
+ - **Faster and clearer internals:** one table instead of two, a single 16-bit register
180
+ instead of two byte variables, no `int()` call per byte (over 20% faster per KB on
181
+ CPython 3.11).
182
+
183
+ ## License
184
+
185
+ Distributed under the BSD 3-Clause license. See [LICENSE.md](LICENSE.md) for details.
@@ -0,0 +1,7 @@
1
+ modbus_crc/__init__.py,sha256=1IrVei-2BOf9Fm9dcjp6LuscdbLr5hTITbCrXU9HrVQ,1492
2
+ modbus_crc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ modbus_crc-2.0.dist-info/licenses/LICENSE.md,sha256=Y4f1dDEJp690QgZ4S1ilY4Lbb-2re6hUdAeLA2sMXIg,1495
4
+ modbus_crc-2.0.dist-info/METADATA,sha256=u3CfUcbJ4RGcN5MEwOYE7qR6b4FCYdhfyViCp_r22Cw,6968
5
+ modbus_crc-2.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
6
+ modbus_crc-2.0.dist-info/top_level.txt,sha256=AafRKooQK4rOOe2tq8tnLq-AIG3v0zqAiYuzoSvlY6c,11
7
+ modbus_crc-2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2023, Alexey Kuznetsov
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ * Neither the names of Alexey Kuznetsov or webtoucher nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ modbus_crc