Skip to content

Commit 0a81c92

Browse files
authored
Update async tests. (#1003)
1 parent 69dfa3f commit 0a81c92

File tree

7 files changed

+66
-224
lines changed

7 files changed

+66
-224
lines changed

examples/client_async.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@
4242
)
4343

4444

45-
def setup_async_client(loop):
45+
async def setup_async_client(loop):
4646
"""Run client setup."""
4747
args = get_commandline()
4848
_logger.info("### Create client object")
4949

5050
if args.comm == "tcp":
51-
client = AsyncModbusTCPClient(
51+
client = await AsyncModbusTCPClient(
5252
host="127.0.0.1", # define tcp address where to connect to.
5353
port=args.port, # on which port
5454
framer=ModbusSocketFramer, # how to interpret the messages
@@ -60,7 +60,7 @@ def setup_async_client(loop):
6060
loop=loop,
6161
)
6262
elif args.comm == "udp":
63-
client = AsyncModbusUDPClient(
63+
client = await AsyncModbusUDPClient(
6464
host="localhost", # define tcp address where to connect to.
6565
port=args.port, # on which port
6666
framer=args.framer, # how to interpret the messages
@@ -72,7 +72,7 @@ def setup_async_client(loop):
7272
loop=loop,
7373
)
7474
elif args.comm == "serial":
75-
client = AsyncModbusSerialClient(
75+
client = await AsyncModbusSerialClient(
7676
port=args.port, # serial port
7777
framer=args.framer, # how to interpret the messages
7878
stopbits=1, # The number of stop bits to use
@@ -85,7 +85,7 @@ def setup_async_client(loop):
8585
loop=loop,
8686
)
8787
elif args.comm == "tls":
88-
client = AsyncModbusTLSClient(
88+
client = await AsyncModbusTLSClient(
8989
host="localhost", # define tcp address where to connect to.
9090
port=args.port, # on which port
9191
sslctx=None, # ssl control
@@ -100,7 +100,7 @@ def setup_async_client(loop):
100100
strict=True, # use strict timing, t1.5 for Modbus RTU
101101
loop=loop,
102102
)
103-
return loop, client
103+
return client
104104

105105

106106
async def run_async_client(modbus_calls=None):
@@ -125,7 +125,7 @@ def start_loop(loop):
125125
assert loop.is_running() # nosec
126126
asyncio.set_event_loop(loop)
127127

128-
loop, client = setup_async_client(loop)
128+
client = await setup_async_client(loop)
129129

130130
# Run supplied modbus calls
131131
if modbus_calls:

pymodbus/client/asynchronous/serial.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""SERIAL communication."""
2-
import asyncio
32
import logging
43

54
from pymodbus.factory import ClientDecoder
@@ -11,6 +10,15 @@
1110
_logger = logging.getLogger(__name__)
1211

1312

13+
async def init_serial_client(port, proto_cls, framer, **kwargs):
14+
"""Initialize UDP client with helper function."""
15+
client = AsyncioModbusSerialClient(
16+
port, proto_cls, framer=framer, **kwargs
17+
)
18+
await client.connect()
19+
return client
20+
21+
1422
class AsyncModbusSerialClient(AsyncioModbusSerialClient):
1523
"""Actual Async Serial Client to be used.
1624
@@ -32,18 +40,7 @@ def __new__(cls, framer, port, **kwargs):
3240
:return:
3341
"""
3442
framer = framer(ClientDecoder())
35-
try:
36-
loop = kwargs.pop("loop", None) or asyncio.get_running_loop()
37-
except RuntimeError:
38-
loop = asyncio.new_event_loop()
39-
4043
proto_cls = kwargs.get("proto_cls") or ModbusClientProtocol
4144

42-
client = AsyncioModbusSerialClient(port, proto_cls, framer, **kwargs)
43-
coro = client.connect
44-
if not loop.is_running():
45-
loop.run_until_complete(coro())
46-
else: # loop is not asyncio.get_event_loop():
47-
future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)
48-
future.result()
45+
client = init_serial_client(port, proto_cls, framer=framer, **kwargs)
4946
return client
Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""TCP communication."""
2-
import asyncio
32
import logging
43

54
from pymodbus.client.asynchronous.async_io import (
65
init_tcp_client,
76
ReconnectingAsyncioModbusTcpClient,
87
)
98
from pymodbus.constants import Defaults
9+
from pymodbus.factory import ClientDecoder
10+
from pymodbus.transaction import ModbusSocketFramer
1011

1112
_logger = logging.getLogger(__name__)
1213

@@ -22,6 +23,7 @@ def __new__(
2223
cls,
2324
host="127.0.0.1",
2425
port=Defaults.Port,
26+
framer=None,
2527
**kwargs
2628
):
2729
"""Scheduler to use async_io (asyncio)
@@ -34,23 +36,7 @@ def __new__(
3436
:param kwargs: Other extra args specific to Backend being used
3537
:return:
3638
"""
37-
try:
38-
loop = kwargs.pop("loop", None) or asyncio.get_event_loop()
39-
except RuntimeError:
40-
loop = asyncio.new_event_loop()
41-
39+
framer = framer or ModbusSocketFramer(ClientDecoder())
4240
proto_cls = kwargs.pop("proto_cls", None)
4341

44-
if not loop.is_running():
45-
asyncio.set_event_loop(loop)
46-
cor = init_tcp_client(proto_cls, host, port, **kwargs)
47-
client = loop.run_until_complete(asyncio.gather(cor))[0]
48-
49-
elif loop is asyncio.get_event_loop():
50-
cor = init_tcp_client(proto_cls, host, port, **kwargs)
51-
client = asyncio.create_task(cor)
52-
else:
53-
cor = init_tcp_client(proto_cls, host, port, **kwargs)
54-
future = asyncio.run_coroutine_threadsafe(cor, loop=loop)
55-
client = future.result()
56-
return client
42+
return init_tcp_client(proto_cls, host, port, framer=framer, **kwargs)

pymodbus/client/asynchronous/tls.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""TLS communication."""
2-
import asyncio
32
import logging
43

54
from pymodbus.client.asynchronous.async_io import (
@@ -44,26 +43,9 @@ def __new__(
4443
:return:
4544
"""
4645
framer = framer or ModbusTlsFramer(ClientDecoder())
47-
try:
48-
loop = kwargs.pop("loop", None) or asyncio.get_event_loop()
49-
except RuntimeError:
50-
loop = asyncio.new_event_loop()
51-
5246
proto_cls = kwargs.pop("proto_cls", None)
53-
if not loop.is_running():
54-
asyncio.set_event_loop(loop)
55-
cor = init_tls_client(
56-
proto_cls, host, port, sslctx, server_hostname, framer, **kwargs
57-
)
58-
client = loop.run_until_complete(asyncio.gather(cor))[0]
59-
elif loop is asyncio.get_event_loop():
60-
return init_tls_client(
61-
proto_cls, host, port, sslctx, server_hostname, framer, **kwargs
62-
)
63-
else:
64-
cor = init_tls_client(
65-
proto_cls, host, port, sslctx, server_hostname, framer, **kwargs
66-
)
67-
future = asyncio.run_coroutine_threadsafe(cor, loop=loop)
68-
client = future.result()
47+
48+
client = init_tls_client(
49+
proto_cls, host, port, sslctx, server_hostname, framer, **kwargs
50+
)
6951
return client
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""UDP communication."""
2-
import asyncio
32
import logging
43

54
from pymodbus.client.asynchronous.async_io import (
65
init_udp_client,
76
ReconnectingAsyncioModbusUdpClient,
87
)
98
from pymodbus.constants import Defaults
9+
from pymodbus.factory import ClientDecoder
10+
from pymodbus.transaction import ModbusSocketFramer
1011

1112
_logger = logging.getLogger(__name__)
1213

@@ -22,6 +23,7 @@ def __new__(
2223
cls,
2324
host="127.0.0.1",
2425
port=Defaults.Port,
26+
framer=None,
2527
**kwargs
2628
):
2729
"""Do setup of client.
@@ -34,20 +36,8 @@ def __new__(
3436
:param kwargs: Other extra args specific to Backend being used
3537
:return:
3638
"""
37-
try:
38-
loop = kwargs.pop("loop", None) or asyncio.get_event_loop()
39-
except RuntimeError:
40-
loop = asyncio.new_event_loop()
41-
39+
framer = framer or ModbusSocketFramer(ClientDecoder())
4240
proto_cls = kwargs.pop("proto_cls", None)
43-
cor = init_udp_client(proto_cls, host, port, **kwargs)
44-
if not loop.is_running():
45-
cor = init_udp_client(proto_cls, host, port)
46-
client = loop.run_until_complete(asyncio.gather(cor))[0]
47-
elif loop is asyncio.get_event_loop():
48-
return init_udp_client(proto_cls, host, port)
49-
50-
cor = init_udp_client(proto_cls, host, port)
51-
client = asyncio.run_coroutine_threadsafe(cor, loop=loop)
52-
client = client.result()
41+
42+
client = init_udp_client(proto_cls, host, port, framer=framer, **kwargs)
5343
return client

test/test_client_async.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@
44
import contextlib
55
import ssl
66
import unittest
7-
from unittest.mock import patch
87

98
import pytest
109

1110
from pymodbus.client.asynchronous.async_io import (
1211
AsyncioModbusSerialClient,
1312
ReconnectingAsyncioModbusTcpClient,
1413
ReconnectingAsyncioModbusTlsClient,
14+
ReconnectingAsyncioModbusUdpClient,
1515
)
1616
from pymodbus.client.asynchronous.serial import AsyncModbusSerialClient
1717
from pymodbus.client.asynchronous.tls import AsyncModbusTLSClient
1818
from pymodbus.client.asynchronous.tcp import AsyncModbusTCPClient
19+
from pymodbus.client.asynchronous.udp import AsyncModbusUDPClient
1920
from pymodbus.transaction import (
2021
ModbusAsciiFramer,
2122
ModbusBinaryFramer,
@@ -50,11 +51,16 @@ class TestAsynchronousClient:
5051
# -----------------------------------------------------------------------#
5152
# Test TCP Client client
5253
# -----------------------------------------------------------------------#
53-
def test_tcp_asyncio_client(self):
54+
def test_tcp_no_asyncio_client(self):
5455
"""Test the TCP client."""
5556
client = AsyncModbusTCPClient()
57+
assert asyncio.iscoroutine(client)
58+
59+
async def test_tcp_asyncio_client(self):
60+
"""Test the TCP client."""
61+
client = await AsyncModbusTCPClient()
5662
assert isinstance(client, ReconnectingAsyncioModbusTcpClient) # nosec
57-
# assert isinstance(client.framer, ModbusSocketFramer) # nosec
63+
assert isinstance(client.framer, ModbusSocketFramer) # nosec
5864
assert client.port == 502 # nosec
5965

6066
client.stop()
@@ -64,9 +70,14 @@ def test_tcp_asyncio_client(self):
6470
# Test TLS Client client
6571
# -----------------------------------------------------------------------#
6672

67-
def test_tls_asyncio_client(self):
73+
def test_tls_no_asyncio_client(self):
6874
"""Test the TLS AsyncIO client."""
6975
client = AsyncModbusTLSClient()
76+
assert asyncio.iscoroutine(client)
77+
78+
async def test_tls_asyncio_client(self):
79+
"""Test the TLS AsyncIO client."""
80+
client = await AsyncModbusTLSClient()
7081
assert isinstance(client, ReconnectingAsyncioModbusTlsClient) # nosec
7182
assert isinstance(client.framer, ModbusTlsFramer) # nosec
7283
assert isinstance(client.sslctx, ssl.SSLContext) # nosec
@@ -78,22 +89,30 @@ def test_tls_asyncio_client(self):
7889
# -----------------------------------------------------------------------#
7990
# Test UDP client
8091
# -----------------------------------------------------------------------#
81-
def test_udp_asyncio_client(self):
92+
def test_udp_no_asyncio_client(self):
8293
"""Test the udp asyncio client"""
83-
# client = AsyncModbusUDPClient()
84-
# assert isinstance(client, ReconnectingAsyncioModbusTcpClient) # nosec
85-
# assert isinstance(client.framer, ModbusSocketFramer) # nosec
86-
# assert client.port == 502 # nosec
94+
client = AsyncModbusUDPClient()
95+
assert asyncio.iscoroutine(client)
8796

88-
# client.stop()
89-
# assert client.host is None # nosec
97+
async def test_udp_asyncio_client(self):
98+
"""Test the udp asyncio client"""
99+
client = await AsyncModbusUDPClient()
100+
assert isinstance(client, ReconnectingAsyncioModbusUdpClient) # nosec
101+
assert isinstance(client.framer, ModbusSocketFramer) # nosec
102+
assert client.port == 502 # nosec
103+
104+
client.stop()
105+
assert client.host is None # nosec
90106

91107
# -----------------------------------------------------------------------#
92108
# Test Serial client
93109
# -----------------------------------------------------------------------#
94110

95-
@patch("asyncio.get_event_loop")
96-
@patch("asyncio.gather", side_effect=mock_asyncio_gather)
111+
def test_serial_no_asyncio_client(self):
112+
"""Test that AsyncModbusSerialClient instantiates AsyncioModbusSerialClient for asyncio scheduler."""
113+
client = AsyncModbusSerialClient(port="not here", framer=ModbusRtuFramer)
114+
assert asyncio.iscoroutine(client)
115+
97116
@pytest.mark.parametrize(
98117
"framer",
99118
[
@@ -103,20 +122,14 @@ def test_udp_asyncio_client(self):
103122
ModbusAsciiFramer,
104123
],
105124
)
106-
@pytest.mark.asyncio
107125
async def test_serial_asyncio_client(
108126
self,
109-
mock_gather, # pylint: disable=unused-argument
110-
mock_event_loop,
111127
framer,
112-
): # pylint: disable=unused-argument
128+
):
113129
"""Test that AsyncModbusSerialClient instantiates AsyncioModbusSerialClient for asyncio scheduler."""
114-
loop = asyncio.get_event_loop()
115-
loop.is_running.side_effect = lambda: False
116-
client = AsyncModbusSerialClient(
130+
client = await AsyncModbusSerialClient(
117131
framer=framer,
118132
port=pytest.SERIAL_PORT,
119-
loop=loop,
120133
baudrate=19200,
121134
parity="E",
122135
stopbits=2,
@@ -132,7 +145,6 @@ async def test_serial_asyncio_client(
132145
assert client.bytesize == 7 # nosec
133146
asyncio.wait_for(client.connect(), timeout=1)
134147
client.stop()
135-
loop.stop()
136148

137149

138150
# ---------------------------------------------------------------------------#

0 commit comments

Comments
 (0)