Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b454757
Enable Windows testing on GHA
altendky Jan 16, 2021
4d2b446
Hackily use Twisted serial testing classes
altendky Jan 18, 2021
de69322
Resupport Linux too
altendky Jan 18, 2021
8db334c
more hacky fixup for linux
altendky Jan 18, 2021
27ae242
even hackier
altendky Jan 18, 2021
f30e961
mocks -> bind_and_activate=False
altendky Jan 18, 2021
47de91c
fixup for linux again
altendky Jan 18, 2021
c63202f
no twisted serial on pypy
altendky Jan 19, 2021
d12ae25
add gha timeout of 10 minutes
altendky Jan 19, 2021
1a8f017
platform_python_implementation
altendky Jan 19, 2021
b30b691
maybe we do not need cryptography for tests?
altendky Jan 19, 2021
9043ba7
add back cryptography to testing and conch extra to twisted for manhole
altendky Jan 19, 2021
2f84a96
install openssl in github actions windows for pypy
altendky Jan 19, 2021
9da283b
more openssl
altendky Jan 19, 2021
710ed9c
let's try choco install openssl and see where it installs
altendky Jan 19, 2021
37fae4a
more for chocolately
altendky Jan 19, 2021
3cd4ae1
python info before openssl
altendky Jan 19, 2021
63cd0ca
skip pypy 64 on windows since you still get 32
altendky Jan 19, 2021
08631ab
use pypy's openssl build
altendky Jan 19, 2021
8ea88d0
env var
altendky Jan 19, 2021
7b79291
hmm
altendky Jan 19, 2021
47dc912
shell: bash
altendky Jan 19, 2021
e34a13e
${{ env.GITHUB_WORKSPACE }}
altendky Jan 19, 2021
a6b8fb6
env.workspace
altendky Jan 19, 2021
d4e5740
github.workspace
altendky Jan 19, 2021
d428355
mock out socketserver.TCPServer.server_activate
altendky Jan 19, 2021
a5a975b
just skipif tornado serial on windows for now
altendky Jan 19, 2021
ba6814a
some more sleeps to help asyncio tests
altendky Jan 19, 2021
649ac09
inet_pton skips
altendky Jan 19, 2021
74e34fc
it is sys.version_info for the tuple comparison
altendky Jan 19, 2021
892c8b4
exclude Python 3.9 on Windows since twisted does not support it
altendky Jan 19, 2021
5f5cc4a
only skip twisted" s serial extra on pypy/windows
altendky Jan 19, 2021
5f6808f
another bind avoidance
altendky Jan 19, 2021
29ef6b5
skip some twisted on windows in pypy
altendky Jan 19, 2021
960a745
more pypy/windows skipping
altendky Jan 19, 2021
0afe7e6
remove pypy stuff
altendky Jan 20, 2021
183918b
Merge branch 'test_windows' into test_pypy
altendky Jan 20, 2021
20ffdf9
way simplify the twisted
altendky Jan 20, 2021
e930946
un-ignore a couple test files
altendky Jan 20, 2021
ceada4c
formatting
altendky Jan 20, 2021
6db6c6e
mock for tornado on windows
altendky Jan 20, 2021
14535dc
Merge branch 'gha' into test_windows
altendky Jan 20, 2021
57837cd
Merge remote-tracking branch 'upstream/test-fixes' into test_windows
altendky Feb 16, 2021
964ae69
Remove unused import pytest
altendky Feb 16, 2021
7c5a1bc
Merge remote-tracking branch 'upstream/test-fixes' into test_pypy
altendky Feb 16, 2021
78ad99b
Merge branch 'test_windows' into test_pypy
altendky Feb 16, 2021
e03e31c
Merge branch 'dev' into test_pypy
altendky Mar 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
name: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }}
runs-on: ${{ matrix.os.runs-on }}
container: ${{ matrix.os.container[matrix.python.docker] }}
# present runtime seems to be about 1 minute 30 seconds
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
Expand All @@ -40,10 +42,13 @@ jobs:
3.9: docker://python:3.9-buster
pypy2: docker://pypy:2-jessie
pypy3: docker://pypy:3-stretch
# - name: Windows
# runs-on: windows-latest
# python_platform: win32
# matrix: windows
- name: Windows
runs-on: windows-latest
python_platform: win32
matrix: windows
openssl:
x86: win32
x64: win64
# - name: macOS
# runs-on: macos-latest
# python_platform: darwin
Expand All @@ -53,42 +58,53 @@ jobs:
tox: py27
action: 2.7
docker: 2.7
matrix: 2.7
implementation: cpython
- name: PyPy 2.7
tox: pypy27
action: pypy-2.7
docker: pypy2.7
matrix: 2.7
implementation: pypy
openssl_msvc_version: 2019
- name: CPython 3.6
tox: py36
action: 3.6
docker: 3.6
matrix: 3.6
implementation: cpython
- name: CPython 3.7
tox: py37
action: 3.7
docker: 3.7
matrix: 3.7
implementation: cpython
- name: CPython 3.8
tox: py38
action: 3.8
docker: 3.8
matrix: 3.8
implementation: cpython
- name: CPython 3.9
tox: py39
action: 3.9
docker: 3.9
matrix: 3.9
implementation: cpython
- name: PyPy 3.6
tox: pypy36
action: pypy-3.6
docker: pypy3.6
matrix: 3.6
implementation: pypy
openssl_msvc_version: 2019
- name: PyPy 3.7
tox: pypy37
action: pypy-3.7
docker: pypy3.7
matrix: 3.7
implementation: pypy
openssl_msvc_version: 2019
arch:
- name: x86
action: x86
Expand All @@ -105,6 +121,17 @@ jobs:
matrix: macos
arch:
matrix: x86
- os:
matrix: windows
python:
implementation: pypy
arch:
matrix: x64
# Twisted doesn't support Python 3.9 on Windows as of 20.3.0
- os:
matrix: windows
python:
matrix: 3.9
env:
# Should match name above
JOB_NAME: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }}
Expand All @@ -127,8 +154,22 @@ jobs:
- name: Install
run: |
pip install --upgrade pip setuptools wheel
pip install --upgrade tox
pip install --upgrade tox requests urllib3
- uses: twisted/python-info-action@v1.0.1
- name: Add PyPy Externals
if: ${{ matrix.os.matrix == 'windows' && matrix.python.implementation == 'pypy'}}
env:
PYPY_EXTERNALS_PATH: ${{ github.workspace }}/pypy_externals
shell: bash
run: |
echo $PYPY_EXTERNALS_PATH
mkdir --parents $(dirname $PYPY_EXTERNALS_PATH)
hg clone https://foss.heptapod.net/pypy/externals/ $PYPY_EXTERNALS_PATH
dir $PYPY_EXTERNALS_PATH
cd $PYPY_EXTERNALS_PATH && hg update win32_14x
echo "INCLUDE=$PYPY_EXTERNALS_PATH/include;$INCLUDE" >> $GITHUB_ENV
echo "LIB=$PYPY_EXTERNALS_PATH/lib;$LIB" >> $GITHUB_ENV
# echo "CL=${{ matrix.PYTHON.CL_FLAGS }}" >> $GITHUB_ENV
- name: Test
run: |
tox -vv -e ${{ matrix.python.tox }}
Expand Down
13 changes: 7 additions & 6 deletions pymodbus/server/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,16 +327,17 @@ def __init__(self, context, framer=None, identity=None,
self.control = ModbusControlBlock()
self.address = address or ("", Defaults.Port)
self.handler = handler or ModbusConnectedRequestHandler
self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves',
self.ignore_missing_slaves = kwargs.pop('ignore_missing_slaves',
Defaults.IgnoreMissingSlaves)
self.broadcast_enable = kwargs.get('broadcast_enable',
self.broadcast_enable = kwargs.pop('broadcast_enable',
Defaults.broadcast_enable)

if isinstance(identity, ModbusDeviceIdentification):
self.control.Identity.update(identity)

socketserver.ThreadingTCPServer.__init__(self, self.address,
self.handler)
self.handler,
**kwargs)

def process_request(self, request, client):
""" Callback for connecting a new client thread
Expand Down Expand Up @@ -456,16 +457,16 @@ def __init__(self, context, framer=None, identity=None, address=None,
self.control = ModbusControlBlock()
self.address = address or ("", Defaults.Port)
self.handler = handler or ModbusDisconnectedRequestHandler
self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves',
self.ignore_missing_slaves = kwargs.pop('ignore_missing_slaves',
Defaults.IgnoreMissingSlaves)
self.broadcast_enable = kwargs.get('broadcast_enable',
self.broadcast_enable = kwargs.pop('broadcast_enable',
Defaults.broadcast_enable)

if isinstance(identity, ModbusDeviceIdentification):
self.control.Identity.update(identity)

socketserver.ThreadingUDPServer.__init__(self,
self.address, self.handler)
self.address, self.handler, **kwargs)
# self._BaseServer__shutdown_request = True

def process_request(self, request, client):
Expand Down
7 changes: 6 additions & 1 deletion requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ sqlalchemy>=1.1.15
#wsgiref>=0.1.2
verboselogs >= 1.5
tornado==4.5.3
Twisted[serial]>=20.3.0
# using platform_python_implementation rather than
# implementation_name for Python 2 support
Twisted[conch,serial]>=20.3.0; platform_python_implementation == "CPython" or sys_platform != "win32"
# pywin32 isn't supported on pypy
# https://github.com/mhammond/pywin32/issues/1289
Twisted[conch]>=20.3.0; platform_python_implementation == "PyPy" and sys_platform == "win32"
zope.interface>=4.4.0
asynctest>=0.10.0
8 changes: 6 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,12 @@
'sphinx_rtd_theme',
'humanfriendly'],
'twisted': [
'twisted[serial] >= 20.3.0',
'pyasn1 >= 0.1.4',
# using platform_python_implementation rather than
# implementation_name for Python 2 support
'Twisted[conch,serial]>=20.3.0; platform_python_implementation == "CPython" or sys_platform != "win32"',
# pywin32 isn't supported on pypy
# https://github.com/mhammond/pywin32/issues/1289
'Twisted[conch]>=20.3.0; platform_python_implementation == "PyPy" and sys_platform == "win32"',
],
'tornado': [
'tornado == 4.5.3'
Expand Down
98 changes: 60 additions & 38 deletions test/test_client_async.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env python
import contextlib
import sys
import unittest
import pytest
from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
Expand Down Expand Up @@ -30,13 +32,18 @@
import ssl

IS_DARWIN = platform.system().lower() == "darwin"
IS_WINDOWS = platform.system().lower() == "windows"
OSX_SIERRA = LooseVersion("10.12")
if IS_DARWIN:
IS_HIGH_SIERRA_OR_ABOVE = LooseVersion(platform.mac_ver()[0])
SERIAL_PORT = '/dev/ttyp0' if not IS_HIGH_SIERRA_OR_ABOVE else '/dev/ptyp0'
else:
IS_HIGH_SIERRA_OR_ABOVE = False
SERIAL_PORT = "/dev/ptmx"
if IS_WINDOWS:
# the use is mocked out
SERIAL_PORT = ""
else:
SERIAL_PORT = "/dev/ptmx"

# ---------------------------------------------------------------------------#
# Fixture
Expand All @@ -47,6 +54,15 @@ def mock_asyncio_gather(coro):
return coro


@contextlib.contextmanager
def maybe_manage(condition, manager):
if condition:
with manager as value:
yield value
else:
yield None


class TestAsynchronousClient(object):
"""
This is the unittest for the pymodbus.client.asynchronous module
Expand Down Expand Up @@ -175,6 +191,10 @@ def testUdpAsycioClient(self, mock_gather, mock_event_loop):
# Test Serial client
# -----------------------------------------------------------------------#

@pytest.mark.skipif(
sys.platform == 'win32' and platform.python_implementation() == 'PyPy',
reason='Twisted serial requires pywin32 which is not compatible with PyPy',
)
@pytest.mark.parametrize("method, framer", [("rtu", ModbusRtuFramer),
("socket", ModbusSocketFramer),
("binary", ModbusBinaryFramer),
Expand All @@ -185,55 +205,57 @@ def testSerialTwistedClient(self, method, framer):
with patch("serial.Serial") as mock_sp:
from twisted.internet import reactor
from twisted.internet.serialport import SerialPort
with maybe_manage(sys.platform == 'win32', patch.object(SerialPort, "_finishPortSetup")):
with patch('twisted.internet.reactor') as mock_reactor:

with patch('twisted.internet.reactor') as mock_reactor:

protocol, client = AsyncModbusSerialClient(schedulers.REACTOR,
method=method,
port=SERIAL_PORT,
proto_cls=ModbusSerClientProtocol)
protocol, client = AsyncModbusSerialClient(schedulers.REACTOR,
method=method,
port=SERIAL_PORT,
proto_cls=ModbusSerClientProtocol)

assert (isinstance(client, SerialPort))
assert (isinstance(client.protocol, ModbusSerClientProtocol))
assert (0 == len(list(client.protocol.transaction)))
assert (isinstance(client.protocol.framer, framer))
assert (client.protocol._connected)
assert (isinstance(client, SerialPort))
assert (isinstance(client.protocol, ModbusSerClientProtocol))
assert (0 == len(list(client.protocol.transaction)))
assert (isinstance(client.protocol.framer, framer))
assert (client.protocol._connected)

def handle_failure(failure):
assert (isinstance(failure.exception(), ConnectionException))
def handle_failure(failure):
assert (isinstance(failure.exception(), ConnectionException))

d = client.protocol._buildResponse(0x00)
d.addCallback(handle_failure)
d = client.protocol._buildResponse(0x00)
d.addCallback(handle_failure)

assert (client.protocol._connected)
client.protocol.close()
protocol.stop()
assert (not client.protocol._connected)
assert (client.protocol._connected)
client.protocol.close()
protocol.stop()
assert (not client.protocol._connected)

@pytest.mark.parametrize("method, framer", [("rtu", ModbusRtuFramer),
("socket", ModbusSocketFramer),
("binary", ModbusBinaryFramer),
("ascii", ModbusAsciiFramer)])
def testSerialTornadoClient(self, method, framer):
""" Test the serial tornado client client initialize """
protocol, future = AsyncModbusSerialClient(schedulers.IO_LOOP, method=method, port=SERIAL_PORT)
client = future.result()
assert(isinstance(client, AsyncTornadoModbusSerialClient))
assert(0 == len(list(client.transaction)))
assert(isinstance(client.framer, framer))
assert(client.port == SERIAL_PORT)
assert(client._connected)

def handle_failure(failure):
assert(isinstance(failure.exception(), ConnectionException))

d = client._build_response(0x00)
d.add_done_callback(handle_failure)

assert(client._connected)
client.close()
protocol.stop()
assert(not client._connected)
from serial import Serial
with maybe_manage(sys.platform == 'win32', patch.object(Serial, "open")):
protocol, future = AsyncModbusSerialClient(schedulers.IO_LOOP, method=method, port=SERIAL_PORT)
client = future.result()
assert(isinstance(client, AsyncTornadoModbusSerialClient))
assert(0 == len(list(client.transaction)))
assert(isinstance(client.framer, framer))
assert(client.port == SERIAL_PORT)
assert(client._connected)

def handle_failure(failure):
assert(isinstance(failure.exception(), ConnectionException))

d = client._build_response(0x00)
d.add_done_callback(handle_failure)

assert(client._connected)
client.close()
protocol.stop()
assert(not client._connected)

@pytest.mark.skipif(IS_PYTHON3 , reason="requires python2.7")
def testSerialAsyncioClientPython2(self):
Expand Down
14 changes: 14 additions & 0 deletions test/test_client_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import socket
import serial
import ssl
import sys

import pytest

from pymodbus.client.sync import ModbusTcpClient, ModbusUdpClient
from pymodbus.client.sync import ModbusSerialClient, BaseModbusClient
Expand Down Expand Up @@ -47,6 +50,15 @@ def setblocking(self, flag): return None
def in_waiting(self): return None


inet_pton_skipif = pytest.mark.skipif(
sys.platform == "win32" and sys.version_info < (3, 4),
reason=(
"Uses socket.inet_pton() which wasn't available on Windows until"
" 3.4.",
)
)



# ---------------------------------------------------------------------------#
# Fixture
Expand Down Expand Up @@ -128,13 +140,15 @@ def testBasicSyncUdpClient(self):

self.assertEqual("ModbusUdpClient(127.0.0.1:502)", str(client))

@inet_pton_skipif
def testUdpClientAddressFamily(self):
''' Test the Udp client get address family method'''
client = ModbusUdpClient()
self.assertEqual(socket.AF_INET,
client._get_address_family('127.0.0.1'))
self.assertEqual(socket.AF_INET6, client._get_address_family('::1'))

@inet_pton_skipif
def testUdpClientConnect(self):
''' Test the Udp client connection method'''
with patch.object(socket, 'socket') as mock_method:
Expand Down
Loading