Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
b454757
Enable Windows testing on GHA
altendky Jan 16, 2021
635f0a7
Enable macOS 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
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
ef40c98
Merge branch 'gha' into test_macos
altendky Jan 20, 2021
19fc044
mock serial.Serial in macOS
altendky Jan 20, 2021
187ce00
a bit more
altendky Jan 20, 2021
72532f2
always run coverage job
altendky Jan 20, 2021
371dccb
remove pypy for now
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
01d864d
No need to change tox.ini
altendky Feb 16, 2021
9ae8d17
Merge branch 'dev' into test_windows
altendky Mar 3, 2021
3315340
another @inet_pton_skipif
altendky Mar 3, 2021
76f69cd
stop removing pypy
altendky Mar 3, 2021
0020ebe
bring in the pypy fixes
altendky Mar 3, 2021
413e6d3
import pytest
altendky Mar 3, 2021
863ae82
allow Twisted 20.3.0 for py2 support
altendky Mar 3, 2021
b8e43c5
rustup
altendky Mar 3, 2021
e28deac
PIP_NO_CLEAN 1
altendky Mar 3, 2021
f2ce139
tidy
altendky Mar 3, 2021
977f5c1
Merge branch 'dev' into test_macos
altendky Mar 3, 2021
c7c5a26
Merge branch 'test_windows' into test_macos
altendky Mar 3, 2021
2440e78
more catchup
altendky Mar 3, 2021
4eabfac
use brew to get openssl and rust for cryptography build
altendky Mar 3, 2021
50591b8
it is macos
altendky Mar 3, 2021
136e381
alphabetical os order
altendky Mar 3, 2021
acaffa8
remove python_platform from matrix variables
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
70 changes: 59 additions & 11 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 @@ -30,7 +32,6 @@ jobs:
os:
- name: Linux
runs-on: ubuntu-latest
python_platform: linux
matrix: linux
container:
2.7: docker://python:2.7-buster
Expand All @@ -40,55 +41,67 @@ 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: macOS
# runs-on: macos-latest
# python_platform: darwin
# matrix: macos
- name: macOS
runs-on: macos-latest
matrix: macos
- name: Windows
runs-on: windows-latest
matrix: windows
openssl:
x86: win32
x64: win64
python:
- name: CPython 2.7
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 +118,12 @@ jobs:
matrix: macos
arch:
matrix: x86
- os:
matrix: windows
python:
implementation: pypy
arch:
matrix: x64
env:
# Should match name above
JOB_NAME: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }}
Expand All @@ -129,7 +148,37 @@ jobs:
pip install --upgrade pip setuptools wheel
pip install --upgrade tox
- 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: Add Brew
if: ${{ matrix.os.matrix == 'macos' && matrix.python.implementation == 'pypy'}}
shell: bash
run: |
brew install openssl@1.1 rust
echo "LDFLAGS=-L$(brew --prefix openssl@1.1)/lib" >> $GITHUB_ENV
echo "CFLAGS=-I$(brew --prefix openssl@1.1)/include" >> $GITHUB_ENV
- name: rustup
if: ${{ matrix.os.matrix == 'windows' && matrix.python.implementation == 'pypy'}}
shell: bash
run: |
rustup target add i686-pc-windows-msvc
- name: Test
env:
# When compiling Cryptography for PyPy on Windows there is a cleanup
# failure. This is CI, it doesn't matter.
PIP_NO_CLEAN: 1
run: |
tox -vv -e ${{ matrix.python.tox }}
- name: Coverage Processing
Expand Down Expand Up @@ -161,7 +210,6 @@ jobs:
os:
- name: Linux
runs-on: ubuntu-latest
python_platform: linux
matrix: linux
container:
3.8: docker://python:3.8-buster
Expand Down Expand Up @@ -195,6 +243,7 @@ jobs:
# Should match JOB_NAME below
name: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }}
runs-on: ${{ matrix.os.runs-on }}
if: always()
needs:
- test
container: ${{ matrix.os.container[matrix.python.docker] }}
Expand All @@ -208,7 +257,6 @@ jobs:
os:
- name: Linux
runs-on: ubuntu-latest
python_platform: linux
matrix: linux
container:
3.8: docker://python:3.8-buster
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 != "PyPy" 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 != "PyPy" 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 in ('darwin', '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
Loading