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.
@@ -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
1
+ Metadata-Version: 2.4
2
2
  Name: pyModbusTCP
3
- Version: 0.3.0
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.8, 3.9, 3.10, 3.11 and 3.12.
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
 
@@ -15,7 +15,7 @@ Since version 0.1.0, a server is also available.
15
15
  Tests
16
16
  -----
17
17
 
18
- The module is currently test on Python 3.8, 3.9, 3.10, 3.11 and 3.12.
18
+ The module is currently test on Python 3.10, 3.11, 3.12, 3.13 and 3.14.
19
19
 
20
20
  For Linux, Mac OS and Windows.
21
21
 
@@ -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.send(frame)
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')
@@ -1,7 +1,7 @@
1
1
  """ pyModbusTCP package constants definition """
2
2
 
3
3
  # Package version
4
- VERSION = '0.3.0'
4
+ VERSION = '0.3.1'
5
5
  # Modbus/TCP
6
6
  MODBUS_PORT = 502
7
7
  # Modbus function code
@@ -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
1
+ Metadata-Version: 2.4
2
2
  Name: pyModbusTCP
3
- Version: 0.3.0
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.8, 3.9, 3.10, 3.11 and 3.12.
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