diff --git a/API_changes.rst b/API_changes.rst index 4370165806..651dd57591 100644 --- a/API_changes.rst +++ b/API_changes.rst @@ -10,6 +10,8 @@ Version 3.2.0 - `ReturnSlaveNoReponseCountResponse` has been corrected to `ReturnSlaveNoResponseCountResponse` - Option `--modbus-config` for REPL server renamed to `--modbus-config-path` +- client.protocol. --> client. +- client.factory. --> client. ------------- Version 3.1.0 diff --git a/examples/client_async.py b/examples/client_async.py index 04c368fcdb..e3b69b018e 100755 --- a/examples/client_async.py +++ b/examples/client_async.py @@ -119,7 +119,7 @@ async def run_async_client(client, modbus_calls=None): """Run sync client.""" _logger.info("### Client starting") await client.connect() - assert client.protocol + assert client.connected if modbus_calls: await modbus_calls(client) await client.close() diff --git a/examples/client_payload.py b/examples/client_payload.py index f29ef2d846..5c71b762b6 100755 --- a/examples/client_payload.py +++ b/examples/client_payload.py @@ -105,10 +105,10 @@ async def run_payload_calls(client): # Make sure word/byte order is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder assert ( decoder._byteorder == builder._byteorder # pylint: disable=protected-access - ) # nosec + ) assert ( decoder._wordorder == builder._wordorder # pylint: disable=protected-access - ) # nosec + ) decoded = OrderedDict( [ diff --git a/examples/v2.5.3/changing_framers.py b/examples/v2.5.3/changing_framers.py index 13f8a8bf4a..bf3f86fb30 100755 --- a/examples/v2.5.3/changing_framers.py +++ b/examples/v2.5.3/changing_framers.py @@ -43,8 +43,8 @@ # ----------------------------------------------------------------------- # rq = client.write_coil(1, True) rr = client.read_coils(1, 1) - assert not rq.isError() # nosec test that we are not an error - assert rr.bits[0] # nosec test the expected value + assert not rq.isError() # test that we are not an error + assert rr.bits[0] # test the expected value # ----------------------------------------------------------------------- # # close the client diff --git a/examples/v2.5.3/concurrent_client.py b/examples/v2.5.3/concurrent_client.py index 24d26662df..43bb97c0e0 100755 --- a/examples/v2.5.3/concurrent_client.py +++ b/examples/v2.5.3/concurrent_client.py @@ -111,7 +111,7 @@ def _client_worker_process(factory, input_queue, output_queue, is_shutdown): txt = f"error in worker thread: {threading.current_thread()}" log.exception(txt) output_queue.put(WorkResponse(True, workitem.work_id, exc)) - except Exception: # nosec pylint: disable=broad-except + except Exception: # pylint: disable=broad-except pass txt = f"request worker shutting down: {threading.current_thread()}" log.info(txt) diff --git a/examples/v2.5.3/dbstore_update_server.py b/examples/v2.5.3/dbstore_update_server.py index e99be3ec96..f045a89112 100644 --- a/examples/v2.5.3/dbstore_update_server.py +++ b/examples/v2.5.3/dbstore_update_server.py @@ -57,8 +57,8 @@ def updating_writer(parm1): # import pdb; pdb.set_trace() - rand_value = random.randint(0, 9999) # nosec - rand_addr = random.randint(0, 65000) # nosec + rand_value = random.randint(0, 9999) + rand_addr = random.randint(0, 65000) txt = f"Writing to datastore: {rand_addr}, {rand_value}" log.debug(txt) # import pdb; pdb.set_trace() diff --git a/examples/v2.5.3/modbus_saver.py b/examples/v2.5.3/modbus_saver.py index 1383f396bd..60c1a0785b 100644 --- a/examples/v2.5.3/modbus_saver.py +++ b/examples/v2.5.3/modbus_saver.py @@ -27,7 +27,7 @@ * handle_save_end(self) """ import json -import xml.etree.ElementTree as xml # nosec +import xml.etree.ElementTree as xml class ModbusDatastoreSaver: diff --git a/examples/v2.5.3/modbus_simulator.py b/examples/v2.5.3/modbus_simulator.py index f52d9c5a37..4dc174422e 100644 --- a/examples/v2.5.3/modbus_simulator.py +++ b/examples/v2.5.3/modbus_simulator.py @@ -5,7 +5,7 @@ with read/write data as well as user configurable base data """ import logging -import pickle # nosec +import pickle from optparse import OptionParser # pylint: disable=deprecated-module from pymodbus.datastore import ModbusServerContext, ModbusSlaveContext @@ -82,7 +82,7 @@ def __init__(self, config): def parse(self): """Parse the config file and creates a server context""" - handle = pickle.load(self.file) # nosec + handle = pickle.load(self.file) try: # test for existence, or bomb dsd = handle["di"] csd = handle["ci"] diff --git a/examples/v2.5.3/tornado_twisted/async_tornado_client.py b/examples/v2.5.3/tornado_twisted/async_tornado_client.py index 02e42ab6a6..8d346f19bf 100755 --- a/examples/v2.5.3/tornado_twisted/async_tornado_client.py +++ b/examples/v2.5.3/tornado_twisted/async_tornado_client.py @@ -39,7 +39,7 @@ def dassert(future, callback): def _assertor(value): # by pass assertion, an error here stops the write callbacks - assert value # nosec + assert value def on_done(f_trans): if exc := f_trans.exception(): diff --git a/examples/v2.5.3/tornado_twisted/async_tornado_client_serial.py b/examples/v2.5.3/tornado_twisted/async_tornado_client_serial.py index 3ed782d2c2..18df10870d 100755 --- a/examples/v2.5.3/tornado_twisted/async_tornado_client_serial.py +++ b/examples/v2.5.3/tornado_twisted/async_tornado_client_serial.py @@ -10,6 +10,7 @@ # import needed libraries # ---------------------------------------------------------------------------# import logging +from tempfile import gettempdir from tornado.ioloop import IOLoop @@ -45,7 +46,7 @@ def dassert(future, callback): def _assertor(value): # by pass assertion, an error here stops the write callbacks - assert value # nosec + assert value def on_done(f_trans): if exc := f_trans.exception(): @@ -170,7 +171,7 @@ def callback(protocol, future): ) = AsyncModbusSerialClient( # pylint: disable=unpacking-non-sequence schedulers.IO_LOOP, method="rtu", - port="/tmp/ptyp0", # nosec + port=gettempdir() + "/ptyp0", baudrate=9600, timeout=2, ) diff --git a/examples/v2.5.3/tornado_twisted/async_twisted_client.py b/examples/v2.5.3/tornado_twisted/async_twisted_client.py index 861edaa4f5..3aa7a15c0a 100755 --- a/examples/v2.5.3/tornado_twisted/async_twisted_client.py +++ b/examples/v2.5.3/tornado_twisted/async_twisted_client.py @@ -42,7 +42,7 @@ def dassert(deferred, callback): """Dassert.""" def _assertor(value): - assert value # nosec + assert value deferred.addCallback(lambda r: _assertor(callback(r))) deferred.addErrback(err) diff --git a/examples/v2.5.3/tornado_twisted/async_twisted_client_serial.py b/examples/v2.5.3/tornado_twisted/async_twisted_client_serial.py index cd99ad9605..f7062b0007 100755 --- a/examples/v2.5.3/tornado_twisted/async_twisted_client_serial.py +++ b/examples/v2.5.3/tornado_twisted/async_twisted_client_serial.py @@ -5,6 +5,7 @@ client implementation from pymodbus with twisted. """ import logging +from tempfile import gettempdir from twisted.internet import reactor @@ -20,7 +21,7 @@ # state a few constants # ---------------------------------------------------------------------------# -SERIAL_PORT = "/tmp/ptyp0" # nosec +SERIAL_PORT = gettempdir() + "/ptyp0" STATUS_REGS = (1, 2) STATUS_COILS = (1, 3) CLIENT_DELAY = 1 diff --git a/examples/v2.5.3/tornado_twisted/modbus_scraper.py b/examples/v2.5.3/tornado_twisted/modbus_scraper.py index 0fea743c28..7c59a1754e 100755 --- a/examples/v2.5.3/tornado_twisted/modbus_scraper.py +++ b/examples/v2.5.3/tornado_twisted/modbus_scraper.py @@ -5,7 +5,7 @@ them as a collection of sequential data blocks. """ import logging -import pickle # nosec +import pickle from optparse import OptionParser from twisted.internet import ( # pylint: disable=import-error diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 3ba7207db1..0cfc70b859 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -63,10 +63,6 @@ def run(): **Application methods, common to all clients**: """ - state = ModbusTransactionState.IDLE - last_frame_end: float = 0 - silent_interval: float = 0 - @dataclass class _params: # pylint: disable=too-many-instance-attributes """Parameter class.""" @@ -125,13 +121,16 @@ def __init__( self.params.kwargs = kwargs # Common variables. - if xframer := kwargs.get("xframer", None): - self.framer = xframer - else: - self.framer = self.params.framer(ClientDecoder(), self) + self.framer = self.params.framer(ClientDecoder(), self) self.transaction = DictTransactionManager(self, **kwargs) self.delay_ms = self.params.reconnect_delay - self.use_protocol = hasattr(self, "protocol") + self.use_protocol = False + self._connected = False + self.use_udp = False + self.state = ModbusTransactionState.IDLE + self.last_frame_end: float = 0 + self.silent_interval: float = 0 + self.transport = None # Initialize mixin super().__init__() @@ -185,9 +184,9 @@ def execute(self, request: ModbusRequest = None) -> ModbusResponse: :raises ConnectionException: Check exception text. """ if self.use_protocol: - if not self.protocol: + if not self._connected: raise ConnectionException(f"Not connected[{str(self)}]") - return self.protocol.execute(request) + return self.async_execute(request) if not self.connect(): raise ConnectionException(f"Failed to connect[{str(self)}]") return self.transaction.execute(request) @@ -196,6 +195,115 @@ def close(self) -> None: """Close the underlying socket connection (call **sync/async**).""" raise NotImplementedException + # ----------------------------------------------------------------------- # + # Merged client methods + # ----------------------------------------------------------------------- # + def client_made_connection(self, protocol): + """Run transport specific connection.""" + + def client_lost_connection(self, protocol): + """Run transport specific connection lost.""" + + def datagram_received(self, data, _addr): + """Receive datagram.""" + self.data_received(data) + + async def async_execute(self, request=None): + """Execute requests asynchronously.""" + request.transaction_id = self.transaction.getNextTID() + packet = self.framer.buildPacket(request) + Log.debug("send: {}", packet, ":hex") + if self.use_udp: + self.transport.sendto(packet) + else: + self.transport.write(packet) + req = self._build_response(request.transaction_id) + if self.params.broadcast_enable and not request.unit_id: + resp = b"Broadcast write sent - no response expected" + else: + try: + resp = await asyncio.wait_for(req, timeout=self.params.timeout) + except asyncio.exceptions.TimeoutError: + self.connection_lost("trying to send") + raise + return resp + + def connection_made(self, transport): + """Call when a connection is made. + + The transport argument is the transport representing the connection. + """ + self.transport = transport + Log.debug("Client connected to modbus server") + self._connected = True + self.client_made_connection(self) + + def connection_lost(self, reason): + """Call when the connection is lost or closed. + + The argument is either an exception object or None + """ + if self.transport: + self.transport.abort() + if hasattr(self.transport, "_sock"): + self.transport._sock.close() # pylint: disable=protected-access + self.transport = None + self.client_lost_connection(self) + Log.debug("Client disconnected from modbus server: {}", reason) + self._connected = False + for tid in list(self.transaction): + self.raise_future( + self.transaction.getTransaction(tid), + ConnectionException("Connection lost during request"), + ) + + def data_received(self, data): + """Call when some data is received. + + data is a non-empty bytes object containing the incoming data. + """ + Log.debug("recv: {}", data, ":hex") + self.framer.processIncomingPacket(data, self._handle_response, unit=0) + + def create_future(self): + """Help function to create asyncio Future object.""" + return asyncio.Future() + + def raise_future(self, my_future, exc): + """Set exception of a future if not done.""" + if not my_future.done(): + my_future.set_exception(exc) + + def _handle_response(self, reply, **_kwargs): + """Handle the processed response and link to correct deferred.""" + if reply is not None: + tid = reply.transaction_id + if handler := self.transaction.getTransaction(tid): + if not handler.done(): + handler.set_result(reply) + else: + Log.debug("Unrequested message: {}", reply, ":str") + + def _build_response(self, tid): + """Return a deferred response for the current request.""" + my_future = self.create_future() + if not self._connected: + self.raise_future(my_future, ConnectionException("Client is not connected")) + else: + self.transaction.addTransaction(my_future, tid) + return my_future + + @property + def async_connected(self): + """Return connection status.""" + return self._connected + + async def async_close(self): + """Close connection.""" + if self.transport: + self.transport.close() + self._connected = False + # ----------------------------------------------------------------------- # # Internal methods # ----------------------------------------------------------------------- # @@ -263,153 +371,3 @@ def __str__(self): :returns: The string representation """ return f"{self.__class__.__name__} {self.params.host}:{self.params.port}" - - -class ModbusClientProtocol( - ModbusBaseClient, - asyncio.Protocol, - asyncio.DatagramProtocol, -): - """Asyncio specific implementation of asynchronous modbus client protocol.""" - - #: Factory that created this instance. - factory = None - transport = None - - def __init__( - self, host="127.0.0.1", port=502, source_address=None, use_udp=False, **kwargs - ): - """Initialize a Modbus TCP/UDP asynchronous client""" - super().__init__(**kwargs) - self.use_udp = use_udp - self.params.host = host - self.params.port = port - self.params.source_address = source_address or ("", 0) - - self._connected = False - - def datagram_received(self, data, addr): - """Receive datagram.""" - self._data_received(data) - - async def execute(self, request=None): # pylint: disable=invalid-overridden-method - """Execute requests asynchronously.""" - req = self._execute(request) - if self.params.broadcast_enable and not request.unit_id: - resp = b"Broadcast write sent - no response expected" - else: - try: - resp = await asyncio.wait_for(req, timeout=self.params.timeout) - except asyncio.exceptions.TimeoutError: - self.connection_lost("trying to send") - raise - return resp - - def connection_made(self, transport): - """Call when a connection is made. - - The transport argument is the transport representing the connection. - """ - self.transport = transport - self._connection_made() - - if self.factory: - self.factory.protocol_made_connection(self) # pylint: disable=no-member - - async def close(self): # pylint: disable=invalid-overridden-method - """Close connection.""" - if self.transport: - self.transport.close() - self._connected = False - - def connection_lost(self, reason): - """Call when the connection is lost or closed. - - The argument is either an exception object or None - """ - if self.transport: - self.transport.abort() - if hasattr(self.transport, "_sock"): - self.transport._sock.close() # pylint: disable=protected-access - self.transport = None - if self.factory: - self.factory.protocol_lost_connection(self) # pylint: disable=no-member - self._connection_lost(reason) - - def data_received(self, data): - """Call when some data is received. - - data is a non-empty bytes object containing the incoming data. - """ - self._data_received(data) - - def create_future(self): - """Help function to create asyncio Future object.""" - return asyncio.Future() - - def resolve_future(self, my_future, result): - """Resolve the completed future and sets the result.""" - if not my_future.done(): - my_future.set_result(result) - - def raise_future(self, my_future, exc): - """Set exception of a future if not done.""" - if not my_future.done(): - my_future.set_exception(exc) - - def _connection_made(self): - """Call upon a successful client connection.""" - Log.debug("Client connected to modbus server") - self._connected = True - - def _connection_lost(self, reason): - """Call upon a client disconnect.""" - Log.debug("Client disconnected from modbus server: {}", reason) - self._connected = False - for tid in list(self.transaction): - self.raise_future( - self.transaction.getTransaction(tid), - ConnectionException("Connection lost during request"), - ) - - @property - def connected(self): - """Return connection status.""" - return self._connected - - def write_transport(self, packet): - """Write transport.""" - if self.use_udp: - return self.transport.sendto(packet) - return self.transport.write(packet) - - def _execute(self, request, **kwargs): # pylint: disable=unused-argument - """Start the producer to send the next request to consumer.write(Frame(request)).""" - request.transaction_id = self.transaction.getNextTID() - packet = self.framer.buildPacket(request) - Log.debug("send: {}", packet, ":hex") - self.write_transport(packet) - return self._build_response(request.transaction_id) - - def _data_received(self, data): - """Get response, check for valid message, decode result.""" - Log.debug("recv: {}", data, ":hex") - self.framer.processIncomingPacket(data, self._handle_response, unit=0) - - def _handle_response(self, reply, **kwargs): # pylint: disable=unused-argument - """Handle the processed response and link to correct deferred.""" - if reply is not None: - tid = reply.transaction_id - if handler := self.transaction.getTransaction(tid): - self.resolve_future(handler, reply) - else: - Log.debug("Unrequested message: {}", reply, ":str") - - def _build_response(self, tid): - """Return a deferred response for the current request.""" - my_future = self.create_future() - if not self._connected: - self.raise_future(my_future, ConnectionException("Client is not connected")) - else: - self.transaction.addTransaction(my_future, tid) - return my_future diff --git a/pymodbus/client/serial.py b/pymodbus/client/serial.py index 66a8cb0ed8..383e5d3764 100644 --- a/pymodbus/client/serial.py +++ b/pymodbus/client/serial.py @@ -4,7 +4,7 @@ from functools import partial from typing import Any, Type -from pymodbus.client.base import ModbusBaseClient, ModbusClientProtocol +from pymodbus.client.base import ModbusBaseClient from pymodbus.client.serial_asyncio import create_serial_connection from pymodbus.constants import Defaults from pymodbus.exceptions import ConnectionException @@ -20,7 +20,7 @@ pass -class AsyncModbusSerialClient(ModbusBaseClient): +class AsyncModbusSerialClient(ModbusBaseClient, asyncio.Protocol): """**AsyncModbusSerialClient**. :param port: Serial port used for communication. @@ -61,8 +61,8 @@ def __init__( **kwargs: Any, ) -> None: """Initialize Asyncio Modbus Serial Client.""" - self.protocol = None super().__init__(framer=framer, **kwargs) + self.use_protocol = True self.params.port = port self.params.baudrate = baudrate self.params.bytesize = bytesize @@ -79,11 +79,9 @@ async def close(self): # pylint: disable=invalid-overridden-method # prevent reconnect: self.delay_ms = 0 if self.connected: - if self.protocol.transport: - self.protocol.transport.close() - if self.protocol: - await self.protocol.close() - self.protocol = None + if self.transport: + self.transport.close() + await self.async_close() await asyncio.sleep(0.1) # if there is an unfinished delayed reconnection attempt pending, cancel it @@ -92,12 +90,8 @@ async def close(self): # pylint: disable=invalid-overridden-method self._reconnect_task = None def _create_protocol(self): - """Create protocol.""" - protocol = ModbusClientProtocol( - framer=self.params.framer, xframer=self.framer, timeout=self.params.timeout - ) - protocol.factory = self - return protocol + """Create a protocol instance.""" + return self @property def connected(self): @@ -130,25 +124,21 @@ async def connect(self): # pylint: disable=invalid-overridden-method self._launch_reconnect() return self.connected - def protocol_made_connection(self, protocol): + def client_made_connection(self, protocol): """Notify successful connection.""" Log.info("Serial connected.") if not self.connected: self._connected_event.set() - self.protocol = protocol else: Log.error("Factory protocol connect callback called while connected.") - def protocol_lost_connection(self, protocol): + def client_lost_connection(self, protocol): """Notify lost connection.""" Log.info("Serial lost connection.") - if protocol is not self.protocol: - Log.error("Serial: protocol is not self.protocol.") + if protocol is not self: + Log.error("Serial: protocol is not self.") self._connected_event.clear() - if self.protocol is not None: - del self.protocol - self.protocol = None if self.delay_ms: self._launch_reconnect() diff --git a/pymodbus/client/tcp.py b/pymodbus/client/tcp.py index 962ef5afef..7346be8177 100644 --- a/pymodbus/client/tcp.py +++ b/pymodbus/client/tcp.py @@ -5,7 +5,7 @@ import time from typing import Any, Tuple, Type -from pymodbus.client.base import ModbusBaseClient, ModbusClientProtocol +from pymodbus.client.base import ModbusBaseClient from pymodbus.constants import Defaults from pymodbus.exceptions import ConnectionException from pymodbus.framer import ModbusFramer @@ -14,7 +14,7 @@ from pymodbus.utilities import ModbusTransactionState -class AsyncModbusTcpClient(ModbusBaseClient): +class AsyncModbusTcpClient(ModbusBaseClient, asyncio.Protocol): """**AsyncModbusTcpClient**. :param host: Host IP address or host name @@ -46,8 +46,8 @@ def __init__( **kwargs: Any, ) -> None: """Initialize Asyncio Modbus TCP Client.""" - self.protocol = None super().__init__(framer=framer, **kwargs) + self.use_protocol = True self.params.host = host self.params.port = port self.params.source_address = source_address @@ -72,12 +72,10 @@ async def close(self): # pylint: disable=invalid-overridden-method """Stop client.""" self.delay_ms = 0 if self.connected: - if self.protocol.transport: - self.protocol.transport.abort() - self.protocol.transport.close() - if self.protocol: - await self.protocol.close() - self.protocol = None + if self.transport: + self.transport.abort() + self.transport.close() + await self.async_close() await asyncio.sleep(0.1) if self._reconnect_task: @@ -85,22 +83,8 @@ async def close(self): # pylint: disable=invalid-overridden-method self._reconnect_task = None def _create_protocol(self): - """Create initialized protocol instance with factory function.""" - protocol = ModbusClientProtocol( - framer=self.params.framer, - xframer=self.framer, - timeout=self.params.timeout, - retries=self.params.retries, - retry_on_empty=self.params.retry_on_empty, - close_comm_on_error=self.params.close_comm_on_error, - strict=self.params.strict, - broadcast_enable=self.params.broadcast_enable, - reconnect_delay=self.params.reconnect_delay, - reconnect_delay_max=self.params.reconnect_delay_max, - **self.params.kwargs, - ) - protocol.factory = self - return protocol + """Create initialized protocol instance with function.""" + return self async def _connect(self): """Connect.""" @@ -131,25 +115,21 @@ async def _connect(self): self.reset_delay() return transport, protocol - def protocol_made_connection(self, protocol): + def client_made_connection(self, protocol): """Notify successful connection.""" Log.info("Protocol made connection.") if not self.connected: self.connected = True - self.protocol = protocol else: Log.error("Factory protocol connect callback called while connected.") - def protocol_lost_connection(self, protocol): + def client_lost_connection(self, protocol): """Notify lost connection.""" Log.info("Protocol lost connection.") - if protocol is not self.protocol: + if protocol is not self: Log.error("Factory protocol cb from unknown protocol instance.") self.connected = False - if self.protocol is not None: - del self.protocol - self.protocol = None if self.delay_ms > 0: self._launch_reconnect() diff --git a/pymodbus/client/tls.py b/pymodbus/client/tls.py index 35e55ca733..305fe0c800 100644 --- a/pymodbus/client/tls.py +++ b/pymodbus/client/tls.py @@ -1,4 +1,5 @@ """Modbus client async TLS communication.""" +import asyncio import socket import ssl from typing import Any, Type @@ -39,7 +40,7 @@ def sslctx_provider( return sslctx -class AsyncModbusTlsClient(AsyncModbusTcpClient): +class AsyncModbusTlsClient(AsyncModbusTcpClient, asyncio.Protocol): """**AsyncModbusTlsClient**. :param host: Host IP address or host name diff --git a/pymodbus/client/udp.py b/pymodbus/client/udp.py index d51e182670..04f3969bd3 100644 --- a/pymodbus/client/udp.py +++ b/pymodbus/client/udp.py @@ -1,10 +1,9 @@ """Modbus client async UDP communication.""" import asyncio -import functools import socket from typing import Any, Tuple, Type -from pymodbus.client.base import ModbusBaseClient, ModbusClientProtocol +from pymodbus.client.base import ModbusBaseClient from pymodbus.constants import Defaults from pymodbus.exceptions import ConnectionException from pymodbus.framer import ModbusFramer @@ -15,7 +14,9 @@ DGRAM_TYPE = socket.SOCK_DGRAM -class AsyncModbusUdpClient(ModbusBaseClient): +class AsyncModbusUdpClient( + ModbusBaseClient, asyncio.Protocol, asyncio.DatagramProtocol +): """**AsyncModbusUdpClient**. :param host: Host IP address or host name @@ -45,8 +46,8 @@ def __init__( **kwargs: Any, ) -> None: """Initialize Asyncio Modbus UDP Client.""" - self.protocol = None super().__init__(framer=framer, **kwargs) + self.use_protocol = True self.params.host = host self.params.port = port self.params.source_address = source_address @@ -81,47 +82,27 @@ async def close(self): # pylint: disable=invalid-overridden-method """ self.delay_ms = 0 if self.connected: - if self.protocol.transport: - self.protocol.transport.abort() - self.protocol.transport.close() - if self.protocol: - await self.protocol.close() - self.protocol = None + if self.transport: + self.transport.abort() + self.transport.close() + await self.async_close() await asyncio.sleep(0.1) if self._reconnect_task: self._reconnect_task.cancel() self._reconnect_task = None - def _create_protocol(self, host=None, port=0): - """Create initialized protocol instance with factory function.""" - protocol = ModbusClientProtocol( - use_udp=True, - framer=self.params.framer, - xframer=self.framer, - timeout=self.params.timeout, - retries=self.params.retries, - retry_on_empty=self.params.retry_on_empty, - close_comm_on_error=self.params.close_comm_on_error, - strict=self.params.strict, - broadcast_enable=self.params.broadcast_enable, - reconnect_delay=self.params.reconnect_delay, - reconnect_delay_max=self.params.reconnect_delay_max, - **self.params.kwargs, - ) - protocol.params.host = host - protocol.params.port = port - protocol.factory = self - return protocol + def _create_protocol(self): + """Create initialized protocol instance with function.""" + self.use_udp = True + return self async def _connect(self): """Connect.""" Log.debug("Connecting.") try: endpoint = await self.loop.create_datagram_endpoint( - functools.partial( - self._create_protocol, host=self.params.host, port=self.params.port - ), + self._create_protocol, remote_addr=(self.params.host, self.params.port), ) Log.info("Connected to {}:{}.", self.params.host, self.params.port) @@ -130,7 +111,7 @@ async def _connect(self): Log.warning("Failed to connect: {}", exc) self._reconnect_task = asyncio.ensure_future(self._reconnect()) - def protocol_made_connection(self, protocol): + def client_made_connection(self, protocol): """Notify successful connection. :meta private: @@ -138,23 +119,19 @@ def protocol_made_connection(self, protocol): Log.info("Protocol made connection.") if not self.connected: self.connected = True - self.protocol = protocol else: Log.error("Factory protocol connect callback called while connected.") - def protocol_lost_connection(self, protocol): + def client_lost_connection(self, protocol): """Notify lost connection. :meta private: """ Log.info("Protocol lost connection.") - if protocol is not self.protocol: + if protocol is not self: Log.error("Factory protocol cb from unexpected protocol instance.") self.connected = False - if self.protocol is not None: - del self.protocol - self.protocol = None if self.delay_ms > 0: self._launch_reconnect() diff --git a/pymodbus/repl/server/main.py b/pymodbus/repl/server/main.py index b133d9ccf0..d121086ac5 100644 --- a/pymodbus/repl/server/main.py +++ b/pymodbus/repl/server/main.py @@ -121,7 +121,7 @@ def server( def run( ctx: typer.Context, modbus_server: str = typer.Option( - ModbusServerTypes.tcp, + ModbusServerTypes.tcp.value, "--modbus-server", "-s", case_sensitive=False, @@ -129,7 +129,7 @@ def run( help="Modbus Server", ), modbus_framer: str = typer.Option( - ModbusFramerTypes.socket, + ModbusFramerTypes.socket.value, "--framer", "-f", case_sensitive=False, diff --git a/pymodbus/server/reactive/main.py b/pymodbus/server/reactive/main.py index 0d4ab72b5e..56db4d504b 100644 --- a/pymodbus/server/reactive/main.py +++ b/pymodbus/server/reactive/main.py @@ -429,11 +429,8 @@ def create_context( range(start_address + 1, default_count), default_count - 1 ) address_map.insert(0, 0) - block[modbus_entity] = { - add: val - for add in sorted(address_map) - for val in default_values - } + address_map.sort() + block[modbus_entity] = db(address_map, default_values) else: block[modbus_entity] = db(start_address, default_values) @@ -447,7 +444,7 @@ def create_context( if not single: slaves[i] = slave_context else: - slaves = slave_context + slaves[0] = slave_context server_context = ModbusServerContext(slaves, single=single) return server_context diff --git a/pymodbus/server/simulator/http_server.py b/pymodbus/server/simulator/http_server.py index 7ed5bbc2cb..82f74fef02 100644 --- a/pymodbus/server/simulator/http_server.py +++ b/pymodbus/server/simulator/http_server.py @@ -130,7 +130,9 @@ def __init__( } if custom_actions_module: actions_module = importlib.import_module(custom_actions_module) - custom_actions_module = actions_module.custom_actions_dict + custom_actions_dict = actions_module.custom_actions_dict + else: + custom_actions_dict = None server = setup["server_list"][modbus_server] server["loop"] = asyncio.get_running_loop() if server["comm"] != "serial": @@ -138,7 +140,9 @@ def __init__( del server["host"] del server["port"] device = setup["device_list"][modbus_device] - self.datastore_context = ModbusSimulatorContext(device, custom_actions_module) + self.datastore_context = ModbusSimulatorContext( + device, custom_actions_dict or None + ) datastore = ModbusServerContext(slaves=self.datastore_context, single=True) comm = comm_class[server.pop("comm")] framer = framer_class[server.pop("framer")] @@ -161,10 +165,10 @@ def __init__( self.web_app.on_startup.append(self.start_modbus_server) self.web_app.on_shutdown.append(self.stop_modbus_server) self.generator_html = { - "log": [None, self.build_html_log], - "registers": [None, self.build_html_registers], - "calls": [None, self.build_html_calls], - "server": [None, self.build_html_server], + "log": ["", self.build_html_log], + "registers": ["", self.build_html_registers], + "calls": ["", self.build_html_calls], + "server": ["", self.build_html_server], } self.generator_json = { "log_json": [None, self.build_json_log], @@ -178,7 +182,7 @@ def __init__( self.generator_html[entry][0] = handle.read() self.refresh_rate = 0 self.register_filter: List[int] = [] - self.call_list = [] + self.call_list: List[str] = [] # not implemented yet self.call_monitor = CallTypeMonitor() self.call_response = CallTypeResponse() @@ -500,8 +504,8 @@ async def build_json_server(self, params, json_dict): def helper_build_filter(self, params): """Build list of registers matching filter.""" - range_start = params.get("range_start", -1) - range_stop = params.get("range_stop", range_start) + range_start = int(params.get("range_start", -1)) + range_stop = int(params.get("range_stop", range_start)) reg_action = int(params["action"]) reg_writeable = "writeable" in params reg_type = int(params["type"]) diff --git a/pymodbus/server/simulator/main.py b/pymodbus/server/simulator/main.py index 79961333b7..09af4928af 100755 --- a/pymodbus/server/simulator/main.py +++ b/pymodbus/server/simulator/main.py @@ -109,6 +109,8 @@ def get_commandline(): async def run_main(): """Run server async.""" cmd_args = get_commandline() + cmd_args["http_port"] = 8081 + cmd_args["json_file"] = "./pymodbus/server/simulator/setup.json" task = ModbusSimulatorServer(**cmd_args) await task.run_forever() diff --git a/pymodbus/version.py b/pymodbus/version.py index 3ac0eeb56c..46e7da8678 100644 --- a/pymodbus/version.py +++ b/pymodbus/version.py @@ -38,9 +38,6 @@ def __str__(self) -> str: version = Version("pymodbus", 3, 1, "x", "") -version.__name__ = ( # fix epydoc error # pylint: disable=attribute-defined-outside-init - "pymodbus" -) # --------------------------------------------------------------------------- # diff --git a/requirements.txt b/requirements.txt index 0d57e50e12..336b51620c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ # Required packages. # ------------------------------------------------------------------- # install:required -setuptools<66.0.0' +setuptools<66.0.0 # ------------------------------------------------------------------- # optional packages. diff --git a/setup.cfg b/setup.cfg index c2a52dbb1a..90eeeb2306 100644 --- a/setup.cfg +++ b/setup.cfg @@ -354,7 +354,7 @@ spelling-dict= spelling-ignore-words= # List of comma separated words that should be considered directives. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,isort:skip,mypy: # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= diff --git a/test/test_client.py b/test/test_client.py index 7f3483606a..889cf000d3 100755 --- a/test/test_client.py +++ b/test/test_client.py @@ -13,7 +13,7 @@ import pymodbus.other_message as pdu_other_msg import pymodbus.register_read_message as pdu_reg_read import pymodbus.register_write_message as pdu_req_write -from pymodbus.client.base import ModbusBaseClient, ModbusClientProtocol +from pymodbus.client.base import ModbusBaseClient from pymodbus.client.mixin import ModbusClientMixin from pymodbus.constants import Defaults from pymodbus.exceptions import ConnectionException, NotImplementedException @@ -250,7 +250,7 @@ async def test_client_instanciate( assert not client.idle_time() initial_delay = client.delay_ms - assert initial_delay > 0 # nosec + assert initial_delay > 0 client.delay_ms *= 2 assert client.delay_ms > initial_delay @@ -264,12 +264,12 @@ async def test_client_instanciate( # a successful execute client.connect = lambda: True - client.protocol = lambda: True + client._connected = True # pylint: disable=protected-access client.transaction = mock.Mock(**{"execute.return_value": True}) # a unsuccessful connect client.connect = lambda: False - client.protocol = None + client._connected = False # pylint: disable=protected-access with pytest.raises(ConnectionException): client.execute() @@ -303,42 +303,31 @@ def test_client_modbusbaseclient(): async def test_client_made_connection(): - """Test factory protocol made connection.""" - mock_protocol_class = mock.MagicMock() - client = lib_client.AsyncModbusTcpClient( - "127.0.0.1", protocol_class=mock_protocol_class - ) + """Test protocol made connection.""" + client = lib_client.AsyncModbusTcpClient("127.0.0.1") assert not client.connected - assert client.protocol is None - client.protocol_made_connection(mock.sentinel.PROTOCOL) + client.client_made_connection(mock.sentinel.PROTOCOL) assert client.connected - assert client.protocol is mock.sentinel.PROTOCOL - client.protocol_made_connection(mock.sentinel.PROTOCOL_UNEXPECTED) + client.client_made_connection(mock.sentinel.PROTOCOL_UNEXPECTED) assert client.connected - assert client.protocol is mock.sentinel.PROTOCOL async def test_client_lost_connection(): - """Test factory protocol lost connection.""" - mock_protocol_class = mock.MagicMock() - client = lib_client.AsyncModbusTcpClient( - "127.0.0.1", protocol_class=mock_protocol_class - ) + """Test protocol lost connection.""" + client = lib_client.AsyncModbusTcpClient("127.0.0.1") assert not client.connected - assert client.protocol is None # fake client is connected and *then* looses connection: client.connected = True client.params.host = mock.sentinel.HOST client.params.port = mock.sentinel.PORT - client.protocol = mock.sentinel.PROTOCOL with mock.patch( "pymodbus.client.tcp.AsyncModbusTcpClient._launch_reconnect" ) as mock_reconnect: mock_reconnect.return_value = mock.sentinel.RECONNECT_GENERATOR - client.protocol_lost_connection(mock.sentinel.PROTOCOL_UNEXPECTED) + client.client_lost_connection(mock.sentinel.PROTOCOL_UNEXPECTED) assert not client.connected client.connected = True @@ -347,9 +336,8 @@ async def test_client_lost_connection(): ) as mock_reconnect: mock_reconnect.return_value = mock.sentinel.RECONNECT_GENERATOR - client.protocol_lost_connection(mock.sentinel.PROTOCOL) + client.client_lost_connection(mock.sentinel.PROTOCOL) assert not client.connected - assert client.protocol is None async def test_client_base_async(): @@ -375,122 +363,115 @@ async def test_client_base_async(): @pytest.mark.skip async def test_client_protocol(): - """Test base modbus async client protocol.""" - protocol = ModbusClientProtocol(framer=ModbusSocketFramer) - assert protocol.factory is None - assert protocol.transport is None - assert not protocol.connected - - protocol.factory = mock.MagicMock() - protocol.connection_made(mock.sentinel.TRANSPORT) - assert protocol.transport is mock.sentinel.TRANSPORT - protocol.factory.protocol_made_connection.assert_called_once_with( # pylint: disable=no-member - protocol - ) - assert ( - not protocol.factory.protocol_lost_connection.call_count # nosec pylint: disable=no-member + """Test base modbus async client.""" + base = ModbusBaseClient(framer=ModbusSocketFramer) + assert base.transport is None + assert not base.async_connected + + base.connection_made(mock.sentinel.TRANSPORT) + assert base.transport is mock.sentinel.TRANSPORT + base.client_made_connection.assert_called_once_with( # pylint: disable=no-member + base ) + assert not base.client_lost_connection.call_count # pylint: disable=no-member - protocol.factory.reset_mock() - protocol.connection_lost(mock.sentinel.REASON) - assert protocol.transport is None # nosec - assert ( - not protocol.factory.protocol_made_connection.call_count # nosec pylint: disable=no-member - ) - protocol.factory.protocol_lost_connection.assert_called_once_with( # pylint: disable=no-member - protocol + base.connection_lost(mock.sentinel.REASON) + assert base.transport is None + assert not base.client_made_connection.call_count # pylint: disable=no-member + base.client_lost_connection.assert_called_once_with( # pylint: disable=no-member + base ) - protocol.raise_future = mock.MagicMock() + base.raise_future = mock.MagicMock() request = mock.MagicMock() - protocol.transaction.addTransaction(request, 1) - protocol.connection_lost(mock.sentinel.REASON) - protocol.raise_future.assert_called_once() - call_args = protocol.raise_future.call_args.args + base.transaction.addTransaction(request, 1) + base.connection_lost(mock.sentinel.REASON) + base.raise_future.assert_called_once() + call_args = base.raise_future.call_args.args assert call_args[0] == request assert isinstance(call_args[1], ConnectionException) - protocol.transport = mock.MagicMock() - protocol.transport = None - await protocol.close() + base.transport = mock.MagicMock() + base.transport = None + await base.async_close() async def test_client_protocol_receiver(): """Test the client protocol data received""" - protocol = ModbusClientProtocol(framer=ModbusSocketFramer) + base = ModbusBaseClient(framer=ModbusSocketFramer) transport = mock.MagicMock() - protocol.connection_made(transport) - assert protocol.transport == transport - assert protocol.connected + base.connection_made(transport) + assert base.transport == transport + assert base.async_connected data = b"\x00\x00\x12\x34\x00\x06\xff\x01\x01\x02\x00\x04" # setup existing request - assert not list(protocol.transaction) - response = protocol._build_response(0x00) # pylint: disable=protected-access - protocol.data_received(data) + assert not list(base.transaction) + response = base._build_response(0x00) # pylint: disable=protected-access + base.data_received(data) result = response.result() assert isinstance(result, pdu_bit_read.ReadCoilsResponse) - protocol._connected = False # pylint: disable=protected-access + base._connected = False # pylint: disable=protected-access with pytest.raises(ConnectionException): - await protocol._build_response(0x00) # pylint: disable=protected-access + await base._build_response(0x00) # pylint: disable=protected-access async def test_client_protocol_response(): """Test the udp client protocol builds responses""" - protocol = ModbusClientProtocol(framer=ModbusSocketFramer) - response = protocol._build_response(0x00) # pylint: disable=protected-access + base = ModbusBaseClient(framer=ModbusSocketFramer) + response = base._build_response(0x00) # pylint: disable=protected-access excp = response.exception() assert isinstance(excp, ConnectionException) - assert not list(protocol.transaction) + assert not list(base.transaction) - protocol._connected = True # pylint: disable=protected-access - protocol._build_response(0x00) # pylint: disable=protected-access - assert len(list(protocol.transaction)) == 1 + base._connected = True # pylint: disable=protected-access + base._build_response(0x00) # pylint: disable=protected-access + assert len(list(base.transaction)) == 1 async def test_client_protocol_handler(): """Test the client protocol handles responses""" - protocol = ModbusClientProtocol(framer=ModbusSocketFramer) + base = ModbusBaseClient(framer=ModbusSocketFramer) transport = mock.MagicMock() - protocol.connection_made(transport=transport) + base.connection_made(transport=transport) reply = pdu_bit_read.ReadCoilsRequest(1, 1) reply.transaction_id = 0x00 - protocol._handle_response(None) # pylint: disable=protected-access - protocol._handle_response(reply) # pylint: disable=protected-access - response = protocol._build_response(0x00) # pylint: disable=protected-access - protocol._handle_response(reply) # pylint: disable=protected-access + base._handle_response(None) # pylint: disable=protected-access + base._handle_response(reply) # pylint: disable=protected-access + response = base._build_response(0x00) # pylint: disable=protected-access + base._handle_response(reply) # pylint: disable=protected-access result = response.result() assert result == reply async def test_client_protocol_execute(): """Test the client protocol execute method""" - protocol = ModbusClientProtocol("127.0.0.1", framer=ModbusSocketFramer) - protocol.create_future = mock.MagicMock() + base = ModbusBaseClient(host="127.0.0.1", framer=ModbusSocketFramer) + base.create_future = mock.MagicMock() fut = asyncio.Future() fut.set_result(fut) - protocol.create_future.return_value = fut + base.create_future.return_value = fut transport = mock.MagicMock() - protocol.connection_made(transport) - protocol.transport.write = mock.Mock() + base.connection_made(transport) + base.transport.write = mock.Mock() request = pdu_bit_read.ReadCoilsRequest(1, 1) - response = await protocol.execute(request) + response = await base.async_execute(request) tid = request.transaction_id - f_trans = protocol.transaction.getTransaction(tid) + f_trans = base.transaction.getTransaction(tid) assert response == f_trans - protocol.params.broadcast_enable = True + base.params.broadcast_enable = True request = pdu_bit_read.ReadCoilsRequest(1, 1) - response = await protocol.execute(request) + response = await base.async_execute(request) def test_client_udp(): """Test client udp.""" - protocol = ModbusClientProtocol("127.0.0.1", framer=ModbusSocketFramer) - protocol.datagram_received(bytes("00010000", "utf-8"), 1) - protocol.transport = mock.MagicMock() - protocol.use_udp = True - protocol.write_transport(bytes("00010000", "utf-8")) + base = ModbusBaseClient(host="127.0.0.1", framer=ModbusSocketFramer) + base.datagram_received(bytes("00010000", "utf-8"), 1) + base.transport = mock.MagicMock() + base.use_udp = True + base.transport.sendto(bytes("00010000", "utf-8")) def test_client_udp_connect(): diff --git a/test/test_datastore.py b/test/test_datastore.py index 9088a43ef7..c1bf26c48d 100644 --- a/test/test_datastore.py +++ b/test/test_datastore.py @@ -320,8 +320,8 @@ def __init__(self): self.slave._table.select = MagicMock() self.slave._connection = MagicMock() - self.mock_addr = random.randint(0, 65000) # nosec - self.mock_values = random.sample(range(1, 100), 5) # nosec + self.mock_addr = random.randint(0, 65000) + self.mock_values = random.sample(range(1, 100), 5) self.mock_function = 0x01 self.mock_type = "h" self.mock_offset = 0 diff --git a/test/test_framers.py b/test/test_framers.py index 3206b37486..2132088dae 100644 --- a/test/test_framers.py +++ b/test/test_framers.py @@ -46,37 +46,37 @@ def test_framer_initialization(framer): """Test framer initialization.""" decoder = ClientDecoder() framer = framer(decoder) - assert framer.client is None # nosec - assert framer._buffer == b"" # nosec pylint: disable=protected-access - assert framer.decoder == decoder # nosec + assert framer.client is None + assert framer._buffer == b"" # pylint: disable=protected-access + assert framer.decoder == decoder if isinstance(framer, ModbusAsciiFramer): - assert framer._header == { # nosec pylint: disable=protected-access + assert framer._header == { # pylint: disable=protected-access "lrc": "0000", "len": 0, "uid": 0x00, } - assert framer._hsize == 0x02 # nosec pylint: disable=protected-access - assert framer._start == b":" # nosec pylint: disable=protected-access - assert framer._end == b"\r\n" # nosec pylint: disable=protected-access + assert framer._hsize == 0x02 # pylint: disable=protected-access + assert framer._start == b":" # pylint: disable=protected-access + assert framer._end == b"\r\n" # pylint: disable=protected-access elif isinstance(framer, ModbusRtuFramer): - assert framer._header == { # nosec pylint: disable=protected-access + assert framer._header == { # pylint: disable=protected-access "uid": 0x00, "len": 0, "crc": b"\x00\x00", } - assert framer._hsize == 0x01 # nosec pylint: disable=protected-access - assert framer._end == b"\x0d\x0a" # nosec pylint: disable=protected-access - assert framer._min_frame_size == 4 # nosec pylint: disable=protected-access + assert framer._hsize == 0x01 # pylint: disable=protected-access + assert framer._end == b"\x0d\x0a" # pylint: disable=protected-access + assert framer._min_frame_size == 4 # pylint: disable=protected-access else: - assert framer._header == { # nosec pylint: disable=protected-access + assert framer._header == { # pylint: disable=protected-access "crc": 0x0000, "len": 0, "uid": 0x00, } - assert framer._hsize == 0x01 # nosec pylint: disable=protected-access - assert framer._start == b"\x7b" # nosec pylint: disable=protected-access - assert framer._end == b"\x7d" # nosec pylint: disable=protected-access - assert framer._repeat == [ # nosec pylint: disable=protected-access + assert framer._hsize == 0x01 # pylint: disable=protected-access + assert framer._start == b"\x7b" # pylint: disable=protected-access + assert framer._end == b"\x7d" # pylint: disable=protected-access + assert framer._repeat == [ # pylint: disable=protected-access b"}"[0], b"{"[0], ] @@ -87,7 +87,7 @@ def test_decode_data(rtu_framer, data): # pylint: disable=redefined-outer-name """Test decode data.""" data, expected = data decoded = rtu_framer.decode_data(data) - assert decoded == expected # nosec + assert decoded == expected @pytest.mark.parametrize( @@ -103,7 +103,7 @@ def test_check_frame(rtu_framer, data): # pylint: disable=redefined-outer-name """Test check frame.""" data, expected = data rtu_framer._buffer = data # pylint: disable=protected-access - assert expected == rtu_framer.checkFrame() # nosec + assert expected == rtu_framer.checkFrame() @pytest.mark.parametrize( @@ -125,12 +125,12 @@ def test_rtu_advance_framer(rtu_framer, data): # pylint: disable=redefined-oute rtu_framer._buffer = before_buf # pylint: disable=protected-access rtu_framer._header = before_header # pylint: disable=protected-access rtu_framer.advanceFrame() - assert rtu_framer._header == { # nosec pylint: disable=protected-access + assert rtu_framer._header == { # pylint: disable=protected-access "uid": 0x00, "len": 0, "crc": b"\x00\x00", } - assert rtu_framer._buffer == after_buf # nosec pylint: disable=protected-access + assert rtu_framer._buffer == after_buf # pylint: disable=protected-access @pytest.mark.parametrize("data", [b"", b"abcd"]) @@ -138,12 +138,12 @@ def test_rtu_reset_framer(rtu_framer, data): # pylint: disable=redefined-outer- """Test rtu reset framer.""" rtu_framer._buffer = data # pylint: disable=protected-access rtu_framer.resetFrame() - assert rtu_framer._header == { # nosec pylint: disable=protected-access + assert rtu_framer._header == { # pylint: disable=protected-access "uid": 0x00, "len": 0, "crc": b"\x00\x00", } - assert rtu_framer._buffer == b"" # nosec pylint: disable=protected-access + assert rtu_framer._buffer == b"" # pylint: disable=protected-access @pytest.mark.parametrize( @@ -163,7 +163,7 @@ def test_is_frame_ready(rtu_framer, data): # pylint: disable=redefined-outer-na data, expected = data rtu_framer._buffer = data # pylint: disable=protected-access # rtu_framer.advanceFrame() - assert rtu_framer.isFrameReady() == expected # nosec + assert rtu_framer.isFrameReady() == expected @pytest.mark.parametrize( @@ -201,21 +201,21 @@ def test_rtu_populate_header(rtu_framer, data): # pylint: disable=redefined-out """Test rtu populate header.""" buffer, expected = data rtu_framer.populateHeader(buffer) - assert rtu_framer._header == expected # nosec pylint: disable=protected-access + assert rtu_framer._header == expected # pylint: disable=protected-access def test_add_to_frame(rtu_framer): # pylint: disable=redefined-outer-name """Test add to frame.""" - assert rtu_framer._buffer == b"" # nosec pylint: disable=protected-access + assert rtu_framer._buffer == b"" # pylint: disable=protected-access rtu_framer.addToFrame(b"abcd") - assert rtu_framer._buffer == b"abcd" # nosec pylint: disable=protected-access + assert rtu_framer._buffer == b"abcd" # pylint: disable=protected-access def test_get_frame(rtu_framer): # pylint: disable=redefined-outer-name """Test get frame.""" rtu_framer.addToFrame(b"\x02\x01\x01\x00Q\xcc") rtu_framer.populateHeader(b"\x02\x01\x01\x00Q\xcc") - assert rtu_framer.getFrame() == b"\x01\x01\x00" # nosec + assert rtu_framer.getFrame() == b"\x01\x01\x00" def test_populate_result(rtu_framer): # pylint: disable=redefined-outer-name @@ -223,7 +223,7 @@ def test_populate_result(rtu_framer): # pylint: disable=redefined-outer-name rtu_framer._header["uid"] = 255 # pylint: disable=protected-access result = Mock() rtu_framer.populateResult(result) - assert result.unit_id == 255 # nosec + assert result.unit_id == 255 @pytest.mark.parametrize( @@ -274,14 +274,14 @@ def test_rtu_incoming_packet(rtu_framer, data): # pylint: disable=redefined-out rtu_framer, "resetFrame", wraps=rtu_framer.resetFrame ) as mock_reset: rtu_framer.processIncomingPacket(buffer, Mock(), units) - assert mock_process.call_count == (1 if process_called else 0) # nosec - assert mock_reset.call_count == (1 if reset_called else 0) # nosec + assert mock_process.call_count == (1 if process_called else 0) + assert mock_reset.call_count == (1 if reset_called else 0) def test_build_packet(rtu_framer): # pylint: disable=redefined-outer-name """Test build packet.""" message = ReadCoilsRequest(1, 10) - assert rtu_framer.buildPacket(message) == TEST_MESSAGE # nosec + assert rtu_framer.buildPacket(message) == TEST_MESSAGE def test_send_packet(rtu_framer): # pylint: disable=redefined-outer-name @@ -295,9 +295,9 @@ def test_send_packet(rtu_framer): # pylint: disable=redefined-outer-name client.idle_time = Mock(return_value=1) client.send = Mock(return_value=len(message)) rtu_framer.client = client - assert rtu_framer.sendPacket(message) == len(message) # nosec + assert rtu_framer.sendPacket(message) == len(message) client.state = ModbusTransactionState.PROCESSING_REPLY - assert rtu_framer.sendPacket(message) == len(message) # nosec + assert rtu_framer.sendPacket(message) == len(message) def test_recv_packet(rtu_framer): # pylint: disable=redefined-outer-name @@ -306,7 +306,7 @@ def test_recv_packet(rtu_framer): # pylint: disable=redefined-outer-name client = Mock() client.recv.return_value = message rtu_framer.client = client - assert rtu_framer.recvPacket(len(message)) == message # nosec + assert rtu_framer.recvPacket(len(message)) == message def test_process(rtu_framer): # pylint: disable=redefined-outer-name @@ -322,28 +322,24 @@ def test_get_raw_frame(rtu_framer): # pylint: disable=redefined-outer-name rtu_framer._buffer = TEST_MESSAGE # pylint: disable=protected-access assert ( rtu_framer.getRawFrame() - == rtu_framer._buffer # nosec pylint: disable=protected-access + == rtu_framer._buffer # pylint: disable=protected-access ) def test_validate_unit_id(rtu_framer): # pylint: disable=redefined-outer-name """Test validate unit.""" rtu_framer.populateHeader(TEST_MESSAGE) - assert rtu_framer._validate_unit_id( # nosec pylint: disable=protected-access - [0], False - ) - assert rtu_framer._validate_unit_id( # nosec pylint: disable=protected-access - [1], True - ) + assert rtu_framer._validate_unit_id([0], False) # pylint: disable=protected-access + assert rtu_framer._validate_unit_id([1], True) # pylint: disable=protected-access @pytest.mark.parametrize("data", [b":010100010001FC\r\n", b""]) def test_decode_ascii_data(ascii_framer, data): # pylint: disable=redefined-outer-name """Test decode ascii.""" data = ascii_framer.decode_data(data) - assert isinstance(data, dict) # nosec + assert isinstance(data, dict) if data: - assert data.get("unit") == 1 # nosec - assert data.get("fcode") == 1 # nosec + assert data.get("unit") == 1 + assert data.get("fcode") == 1 else: - assert not data # nosec + assert not data diff --git a/test/test_server_asyncio.py b/test/test_server_asyncio.py index 313ea97d0e..b03278b511 100755 --- a/test/test_server_asyncio.py +++ b/test/test_server_asyncio.py @@ -85,7 +85,7 @@ def clear(cls): BasicClient.done = None BasicClient.received_data = None BasicClient.eof = None - BasicClient.protocol = None + BasicClient.my_protocol = None class AsyncioServerTest( @@ -197,7 +197,10 @@ async def connect_server(self): random_port = self.server.server.sockets[0].getsockname()[ 1 ] # get the random server port - BasicClient.transport, BasicClient.protocol = await self.loop.create_connection( + ( + BasicClient.transport, + BasicClient.my_protocol, + ) = await self.loop.create_connection( BasicClient, host="127.0.0.1", port=random_port ) await asyncio.wait_for(BasicClient.connected, timeout=0.1) @@ -248,7 +251,7 @@ async def test_async_tcp_server_connection_lost(self): await self.connect_server() self.assertEqual(len(self.server.active_connections), 1) - BasicClient.protocol.transport.close() + BasicClient.transport.close() await asyncio.sleep(0.2) # so we have to wait a bit self.assertFalse(self.server.active_connections) @@ -351,7 +354,7 @@ async def test_async_udp_server_serve_forever_close(self): self.assertFalse(self.server.on_connection_terminated.done()) await self.server.server_close() - # TBD self.assertTrue(self.server.protocol.is_closing()) + # TBD self.assertTrue(self.server.is_closing()) self.server = None async def test_async_udp_server_serve_forever_twice(self): diff --git a/test/test_server_task.py b/test/test_server_task.py index 99511dbe05..d1c7bc5825 100755 --- a/test/test_server_task.py +++ b/test/test_server_task.py @@ -166,13 +166,13 @@ async def test_async_task_ok(comm): client = run_client(**client_args) await client.connect() await asyncio.sleep(0.1) - assert client.protocol + assert client._connected # pylint: disable=protected-access rr = await client.read_coils(1, 1, slave=0x01) assert len(rr.bits) == 8 await client.close() await asyncio.sleep(0.1) - assert not client.protocol + assert not client._connected # pylint: disable=protected-access await server.ServerAsyncStop() await task @@ -186,7 +186,7 @@ async def test_async_task_server_stop(comm): await asyncio.sleep(0.1) client = run_client(**client_args) await client.connect() - assert client.protocol + assert client._connected # pylint: disable=protected-access rr = await client.read_coils(1, 1, slave=0x01) assert len(rr.bits) == 8 @@ -196,26 +196,26 @@ async def test_async_task_server_stop(comm): with pytest.raises((ConnectionException, asyncio.exceptions.TimeoutError)): rr = await client.read_coils(1, 1, slave=0x01) - assert not client.protocol + assert not client._connected # pylint: disable=protected-access # Server back online task = asyncio.create_task(run_server(**server_args)) await asyncio.sleep(0.1) timer_allowed = 100 - while not client.protocol: + while not client._connected: # pylint: disable=protected-access await asyncio.sleep(0.1) timer_allowed -= 1 if not timer_allowed: assert False, "client do not reconnect" - assert client.protocol + assert client._connected # pylint: disable=protected-access rr = await client.read_coils(1, 1, slave=0x01) assert len(rr.bits) == 8 await client.close() await asyncio.sleep(0.5) - assert not client.protocol + assert not client._connected # pylint: disable=protected-access await server.ServerAsyncStop() await task diff --git a/test/test_simulator.py b/test/test_simulator.py index 5cfdceb717..036be5a409 100644 --- a/test/test_simulator.py +++ b/test/test_simulator.py @@ -480,7 +480,7 @@ async def test_simulator_example(self): await asyncio.sleep(0.1) client = setup_async_client(args) await client.connect() - assert client.protocol + assert client.connected rr = await client.read_holding_registers(16, 1, slave=1) assert rr.registers diff --git a/test/test_unix_socket.py b/test/test_unix_socket.py index dac0ee5dc2..acb91f6551 100755 --- a/test/test_unix_socket.py +++ b/test/test_unix_socket.py @@ -1,6 +1,7 @@ """Test client async.""" import asyncio import logging +from tempfile import gettempdir import pytest import pytest_asyncio @@ -19,7 +20,7 @@ _logger = logging.getLogger() _logger.setLevel("DEBUG") pymodbus_apply_logging_config(logging.DEBUG) -PATH = "/tmp/unix_domain_socket" # nosec +PATH = gettempdir() + "/unix_domain_socket" HOST = f"unix:{PATH}"