pyModbusTCP 0.3.0__tar.gz → 0.3.1__tar.gz
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.
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/CHANGES +6 -0
- {pyModbusTCP-0.3.0/pyModbusTCP.egg-info → pymodbustcp-0.3.1}/PKG-INFO +11 -3
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/README.rst +1 -1
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP/client.py +1 -1
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP/constants.py +1 -1
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP/server.py +7 -7
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1/pyModbusTCP.egg-info}/PKG-INFO +11 -3
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/tests/test_client_server.py +30 -1
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/LICENSE +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/MANIFEST.in +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP/__init__.py +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP/utils.py +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP.egg-info/SOURCES.txt +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP.egg-info/dependency_links.txt +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/pyModbusTCP.egg-info/top_level.txt +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/setup.cfg +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/setup.py +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/tests/test_client.py +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/tests/test_server.py +0 -0
- {pyModbusTCP-0.3.0 → pymodbustcp-0.3.1}/tests/test_utils.py +0 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
Revision history for pyModbusTCP
|
|
2
2
|
|
|
3
|
+
0.3.1 2026-09-29
|
|
4
|
+
|
|
5
|
+
- fix ModbusServer: debug messages now include OSError exceptions produced by getpeername (thanks to Pär Åhlund).
|
|
6
|
+
- fix ModbusClient: prevent incomplete frame transmission by using sendall (thanks to aamir-azeez).
|
|
7
|
+
- updated compatibility test (python versions): remove 3.8/3.9, add 3.13/3.14.
|
|
8
|
+
|
|
3
9
|
0.3.0 2024-09-04
|
|
4
10
|
|
|
5
11
|
- pyModbusTCP.client: now use the standard logging method as in the server part.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyModbusTCP
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A simple Modbus/TCP library for Python
|
|
5
5
|
Home-page: https://github.com/sourceperl/pyModbusTCP
|
|
6
6
|
Author: Loic Lefebvre
|
|
@@ -8,6 +8,14 @@ Author-email: loic.celine@free.fr
|
|
|
8
8
|
License: MIT
|
|
9
9
|
Platform: any
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Dynamic: author
|
|
12
|
+
Dynamic: author-email
|
|
13
|
+
Dynamic: description
|
|
14
|
+
Dynamic: home-page
|
|
15
|
+
Dynamic: license
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
Dynamic: platform
|
|
18
|
+
Dynamic: summary
|
|
11
19
|
|
|
12
20
|
.. |badge_tests| image:: https://github.com/sourceperl/pyModbusTCP/actions/workflows/tests.yml/badge.svg?branch=master
|
|
13
21
|
:target: https://github.com/sourceperl/pyModbusTCP/actions/workflows/tests.yml
|
|
@@ -26,7 +34,7 @@ Since version 0.1.0, a server is also available.
|
|
|
26
34
|
Tests
|
|
27
35
|
-----
|
|
28
36
|
|
|
29
|
-
The module is currently test on Python 3.
|
|
37
|
+
The module is currently test on Python 3.10, 3.11, 3.12, 3.13 and 3.14.
|
|
30
38
|
|
|
31
39
|
For Linux, Mac OS and Windows.
|
|
32
40
|
|
|
@@ -775,7 +775,7 @@ class ModbusClient:
|
|
|
775
775
|
raise ModbusClient._NetworkError(MB_SOCK_CLOSE_ERR, 'try to send on a close socket')
|
|
776
776
|
# send
|
|
777
777
|
try:
|
|
778
|
-
self._sock.
|
|
778
|
+
self._sock.sendall(frame)
|
|
779
779
|
except socket.timeout:
|
|
780
780
|
self._sock.close()
|
|
781
781
|
raise ModbusClient._NetworkError(MB_TIMEOUT_ERR, 'timeout error')
|
|
@@ -824,14 +824,14 @@ class ModbusServer:
|
|
|
824
824
|
self.request.settimeout(1.0)
|
|
825
825
|
|
|
826
826
|
def handle(self):
|
|
827
|
-
# try/except end current thread on ModbusServer._InternalError or socket.error
|
|
827
|
+
# try/except: end current thread on ModbusServer._InternalError, OSError or socket.error
|
|
828
828
|
# this also close the current TCP session associated with it
|
|
829
|
-
# init and update server info structure
|
|
830
|
-
session_data = ModbusServer.SessionData()
|
|
831
|
-
(session_data.client.address, session_data.client.port) = self.request.getpeername()
|
|
832
|
-
# debug message
|
|
833
|
-
logger.debug('accept new connection from %r', session_data.client)
|
|
834
829
|
try:
|
|
830
|
+
# init and update server info structure
|
|
831
|
+
session_data = ModbusServer.SessionData()
|
|
832
|
+
(session_data.client.address, session_data.client.port) = self.request.getpeername()
|
|
833
|
+
# debug message
|
|
834
|
+
logger.debug('accept new connection from %r', session_data.client)
|
|
835
835
|
# main processing loop
|
|
836
836
|
while True:
|
|
837
837
|
# init session data for new request
|
|
@@ -846,7 +846,7 @@ class ModbusServer:
|
|
|
846
846
|
self.server.engine(session_data)
|
|
847
847
|
# send the tx pdu with the last rx mbap (only length field change)
|
|
848
848
|
self._send_all(session_data.response.raw)
|
|
849
|
-
except (ModbusServer.Error, socket.error) as e:
|
|
849
|
+
except (ModbusServer.Error, OSError, socket.error) as e:
|
|
850
850
|
# debug message
|
|
851
851
|
logger.debug('Exception during request handling: %r', e)
|
|
852
852
|
# on main loop except: exit from it and cleanly close the current socket
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyModbusTCP
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A simple Modbus/TCP library for Python
|
|
5
5
|
Home-page: https://github.com/sourceperl/pyModbusTCP
|
|
6
6
|
Author: Loic Lefebvre
|
|
@@ -8,6 +8,14 @@ Author-email: loic.celine@free.fr
|
|
|
8
8
|
License: MIT
|
|
9
9
|
Platform: any
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Dynamic: author
|
|
12
|
+
Dynamic: author-email
|
|
13
|
+
Dynamic: description
|
|
14
|
+
Dynamic: home-page
|
|
15
|
+
Dynamic: license
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
Dynamic: platform
|
|
18
|
+
Dynamic: summary
|
|
11
19
|
|
|
12
20
|
.. |badge_tests| image:: https://github.com/sourceperl/pyModbusTCP/actions/workflows/tests.yml/badge.svg?branch=master
|
|
13
21
|
:target: https://github.com/sourceperl/pyModbusTCP/actions/workflows/tests.yml
|
|
@@ -26,7 +34,7 @@ Since version 0.1.0, a server is also available.
|
|
|
26
34
|
Tests
|
|
27
35
|
-----
|
|
28
36
|
|
|
29
|
-
The module is currently test on Python 3.
|
|
37
|
+
The module is currently test on Python 3.10, 3.11, 3.12, 3.13 and 3.14.
|
|
30
38
|
|
|
31
39
|
For Linux, Mac OS and Windows.
|
|
32
40
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
""" Test of pyModbusTCP client-server interaction """
|
|
2
2
|
|
|
3
|
+
import socket
|
|
3
4
|
import unittest
|
|
5
|
+
from unittest.mock import Mock
|
|
4
6
|
from random import randint, getrandbits, choice
|
|
5
7
|
from string import ascii_letters
|
|
6
8
|
from pyModbusTCP.server import ModbusServer, DeviceIdentification
|
|
7
9
|
from pyModbusTCP.client import ModbusClient, DeviceIdentificationResponse
|
|
8
10
|
from pyModbusTCP.constants import SUPPORTED_FUNCTION_CODES, \
|
|
9
|
-
EXP_NONE, EXP_ILLEGAL_FUNCTION, EXP_DATA_ADDRESS, EXP_DATA_VALUE, MB_NO_ERR, MB_EXCEPT_ERR
|
|
11
|
+
EXP_NONE, EXP_ILLEGAL_FUNCTION, EXP_DATA_ADDRESS, EXP_DATA_VALUE, MB_NO_ERR, MB_EXCEPT_ERR, \
|
|
12
|
+
MB_SEND_ERR, MB_TIMEOUT_ERR
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
# some const
|
|
@@ -34,6 +37,32 @@ class TestClientServer(unittest.TestCase):
|
|
|
34
37
|
self.client.close()
|
|
35
38
|
self.server.stop()
|
|
36
39
|
|
|
40
|
+
def test_request_with_short_send(self):
|
|
41
|
+
"""Complete requests when a socket send accepts only a prefix."""
|
|
42
|
+
self.client.timeout = 1.0
|
|
43
|
+
self.assertTrue(self.client.open())
|
|
44
|
+
sock = self.client._sock
|
|
45
|
+
self.client._sock = Mock(wraps=sock)
|
|
46
|
+
self.client._sock.send.side_effect = lambda data: sock.send(data[:3])
|
|
47
|
+
self.assertEqual(self.client.read_holding_registers(0), [0])
|
|
48
|
+
registers = list(range(MAX_WRITABLE_REGS))
|
|
49
|
+
self.assertTrue(self.client.write_multiple_registers(0, registers))
|
|
50
|
+
self.assertEqual(self.client.read_holding_registers(0, len(registers)), registers)
|
|
51
|
+
|
|
52
|
+
def test_request_send_errors(self):
|
|
53
|
+
"""Send failures keep their error codes and close the connection."""
|
|
54
|
+
for error, code in [(socket.timeout(), MB_TIMEOUT_ERR), (OSError(), MB_SEND_ERR)]:
|
|
55
|
+
with self.subTest(error=type(error).__name__):
|
|
56
|
+
self.assertTrue(self.client.open())
|
|
57
|
+
sock = Mock(wraps=self.client._sock)
|
|
58
|
+
sock.send.side_effect = error
|
|
59
|
+
sock.sendall.side_effect = error
|
|
60
|
+
self.client._sock = sock
|
|
61
|
+
self.assertIsNone(self.client.read_holding_registers(0))
|
|
62
|
+
self.assertEqual(self.client.last_error, code)
|
|
63
|
+
self.assertFalse(self.client.is_open)
|
|
64
|
+
self.assertEqual(sock.send.call_count + sock.sendall.call_count, 1)
|
|
65
|
+
|
|
37
66
|
def test_default_startup_values(self):
|
|
38
67
|
"""Some read at random address to test startup values."""
|
|
39
68
|
for addr in [randint(0, 0xffff) for _ in range(100)]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|