diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..a0caa141e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,258 @@ +name: CI + +on: + push: + branches: + - dev + - master + tags: + - v* + pull_request: + branches: + - "*" + schedule: + # Daily at 05:14 + - cron: '14 5 * * *' + +jobs: + test: + # Should match JOB_NAME below + 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] }} + strategy: + fail-fast: false + matrix: + task: + - name: Test + tox: test + coverage: true + os: + - name: Linux + runs-on: ubuntu-latest + python_platform: linux + matrix: linux + container: + 2.7: docker://python:2.7-buster + 3.6: docker://python:3.6-buster + 3.7: docker://python:3.7-buster + 3.8: docker://python:3.8-buster + 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 + python: + - name: CPython 2.7 + tox: py27 + action: 2.7 + docker: 2.7 + implementation: cpython + - name: PyPy 2.7 + tox: pypy27 + action: pypy-2.7 + docker: pypy2.7 + implementation: pypy + - name: CPython 3.6 + tox: py36 + action: 3.6 + docker: 3.6 + implementation: cpython + - name: CPython 3.7 + tox: py37 + action: 3.7 + docker: 3.7 + implementation: cpython + - name: CPython 3.8 + tox: py38 + action: 3.8 + docker: 3.8 + implementation: cpython + - name: CPython 3.9 + tox: py39 + action: 3.9 + docker: 3.9 + implementation: cpython + - name: PyPy 3.6 + tox: pypy36 + action: pypy-3.6 + docker: pypy3.6 + implementation: pypy + - name: PyPy 3.7 + tox: pypy37 + action: pypy-3.7 + docker: pypy3.7 + implementation: pypy + arch: + - name: x86 + action: x86 + matrix: x86 + - name: x64 + action: x64 + matrix: x64 + exclude: + - os: + matrix: linux + arch: + matrix: x86 + - os: + matrix: macos + arch: + matrix: x86 + env: + # Should match name above + JOB_NAME: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up ${{ matrix.python.name }} (if CPython) + if: ${{ job.container == '' && matrix.python.implementation == 'cpython'}} + uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.python.action }}.0-alpha - ${{ matrix.python.action }}.X' + architecture: '${{ matrix.arch.action }}' + - name: Set up ${{ matrix.python.name }} (if PyPy) + if: ${{ job.container == '' && matrix.python.implementation == 'pypy'}} + uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.python.action }}' + architecture: '${{ matrix.arch.action }}' + - name: Install + run: | + pip install --upgrade pip setuptools wheel + pip install --upgrade tox + - uses: twisted/python-info-action@v1.0.1 + - name: Test + run: | + tox -vv -e ${{ matrix.python.tox }} + - name: Coverage Processing + if: matrix.task.coverage + run: | + mkdir coverage_reports + cp .coverage "coverage_reports/.coverage.${{ env.JOB_NAME }}" + cp coverage.xml "coverage_reports/coverage.${{ env.JOB_NAME }}.xml" + - name: Upload Coverage + if: matrix.task.coverage + uses: actions/upload-artifact@v2 + with: + name: coverage + path: coverage_reports/* + check: + # Should match JOB_NAME below + 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] }} + strategy: + fail-fast: false + matrix: + task: + - name: flake8 + tox: flake8 + continue_on_error: true + - name: Docs + tox: docs + os: + - name: Linux + runs-on: ubuntu-latest + python_platform: linux + matrix: linux + container: + 3.8: docker://python:3.8-buster + python: + - name: CPython 3.8 + tox: py38 + action: 3.8 + docker: 3.8 + implementation: cpython + arch: + - name: x64 + action: x64 + matrix: x64 + env: + # Should match name above + JOB_NAME: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install + run: | + pip install --upgrade pip setuptools wheel + pip install --upgrade tox + - uses: twisted/python-info-action@v1.0.1 + - name: Test + continue-on-error: ${{ matrix.task.continue_on_error == true }} + run: | + tox -vv -e ${{ matrix.task.tox }} + coverage: + # Should match JOB_NAME below + name: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }} + runs-on: ${{ matrix.os.runs-on }} + needs: + - test + container: ${{ matrix.os.container[matrix.python.docker] }} + strategy: + fail-fast: false + matrix: + task: + - name: Coverage + tox: combined-coverage + download_coverage: true + os: + - name: Linux + runs-on: ubuntu-latest + python_platform: linux + matrix: linux + container: + 3.8: docker://python:3.8-buster + python: + - name: CPython 3.8 + tox: py38 + action: 3.8 + docker: 3.8 + implementation: cpython + arch: + - name: x64 + action: x64 + matrix: x64 + env: + # Should match name above + JOB_NAME: ${{ matrix.task.name }} - ${{ matrix.os.name }} ${{ matrix.python.name }} ${{ matrix.arch.name }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install + run: | + pip install --upgrade pip setuptools wheel + pip install --upgrade tox + - uses: twisted/python-info-action@v1.0.1 + - name: Download Coverage + if: matrix.task.download_coverage + uses: actions/download-artifact@v2 + with: + name: coverage + path: coverage_reports + - name: Test + continue-on-error: ${{ matrix.task.continue_on_error == true }} + run: | + tox -vv -e ${{ matrix.task.tox }} + all: + name: All + runs-on: ubuntu-latest + needs: + - check + - coverage + - test + steps: + - name: This + shell: python + run: | + import this diff --git a/doc/conf.py b/doc/conf.py index 294b41b79..4f0dc4122 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -47,7 +47,7 @@ # ones. #extensions = ['sphinx.ext.autodoc', 'm2r', 'recommonmark'] -extensions = ['sphinx.ext.autodoc', 'm2r'] +extensions = ['sphinx.ext.autodoc', 'm2r2'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/requirements-coverage.txt b/requirements-coverage.txt new file mode 100644 index 000000000..9808587c9 --- /dev/null +++ b/requirements-coverage.txt @@ -0,0 +1 @@ +coverage >= 4.2 diff --git a/requirements-docs.txt b/requirements-docs.txt index f1165c9c6..b0a4d2416 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -13,5 +13,5 @@ tornado>=4.5.3 # Required to parse some files Twisted>=17.1.0 # Required to parse some files prompt_toolkit>=2.0.4 click>=7.0 -m2r>=0.2.0 +m2r2>=0.2.0 diff --git a/requirements-tests.txt b/requirements-tests.txt index eba656a38..5eca1922c 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1,6 +1,6 @@ bcrypt>=3.1.6 capturer >= 2.2 -coverage >= 4.2 +-r requirements-coverage.txt cryptography>= 2.3 mock >= 1.0.1 pyserial-asyncio>=0.4.0;python_version>="3.4" @@ -14,6 +14,6 @@ sqlalchemy>=1.1.15 #wsgiref>=0.1.2 verboselogs >= 1.5 tornado==4.5.3 -Twisted>=20.3.0 +Twisted[serial]>=20.3.0 zope.interface>=4.4.0 asynctest>=0.10.0 diff --git a/scripts/travis.sh b/scripts/travis.sh deleted file mode 100755 index 8f4338270..000000000 --- a/scripts/travis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -e -set -x -if [ "$TRAVIS_OS_NAME" = osx ]; then - VIRTUAL_ENV="$HOME/.virtualenvs/python2.7" - if [ ! -x "$VIRTUAL_ENV/bin/python" ]; then - virtualenv "$VIRTUAL_ENV" - fi - source "$VIRTUAL_ENV/bin/activate" -fi - -eval "$@" diff --git a/setup.py b/setup.py index 50da034be..ce65b7d2c 100644 --- a/setup.py +++ b/setup.py @@ -89,7 +89,7 @@ 'sphinx_rtd_theme', 'humanfriendly'], 'twisted': [ - 'twisted >= 20.3.0', + 'twisted[serial] >= 20.3.0', 'pyasn1 >= 0.1.4', ], 'tornado': [ diff --git a/test/conftest.py b/test/conftest.py index 748fd4b7a..932e8124c 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,3 +1,8 @@ -from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION -if not IS_PYTHON3 or IS_PYTHON3 and PYTHON_VERSION.minor < 7: - collect_ignore = ["test_server_asyncio.py"] +from pymodbus.compat import PYTHON_VERSION +if PYTHON_VERSION < (3,): + # These files use syntax introduced between Python 2 and our lowest + # supported Python 3 version. We just won't run these tests in Python 2. + collect_ignore = [ + "test_client_async_asyncio.py", + "test_server_asyncio.py", + ] diff --git a/tox.ini b/tox.ini index 909d6a74d..91a573e7a 100644 --- a/tox.ini +++ b/tox.ini @@ -4,12 +4,36 @@ # directory. [tox] -envlist = py27, py35, py36, py37, pypy +envlist = py{27,py27,36,37,38,39,py36,py37} [testenv] deps = -r requirements-tests.txt -commands = py.test {posargs} -setenv = with_gmp=no +commands = + pytest {posargs:--cov=pymodbus/ --cov-report=term-missing --cov-report=xml} +setenv = + with_gmp=no + +[testenv:flake8] +deps = -r requirements-checks.txt +commands = + flake8 + +[testenv:docs] +allowlist_externals = + make +deps = -r requirements-docs.txt +commands = + make -C doc/ clean + make -C doc/ html + +[testenv:combined-coverage] +allowlist_externals = + ls +deps = -r requirements-coverage.txt +commands = + ls -la coverage_reports + coverage combine coverage_reports + coverage report --fail-under=85 --ignore-errors [flake8] exclude = .tox