From b5d3f2e30977b9b7015142b6deb32635eb7f891d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Wed, 20 Jan 2021 16:46:51 +0100 Subject: [PATCH] modernize CI setup (#13) - use GitHub Actions for CI - use pre-commit (black, flake8, pyupgrade) - add support for Python 3.9 --- .github/workflows/main.yml | 50 ++++++ .pre-commit-config.yaml | 11 +- .travis.yml | 27 --- CHANGES.txt | 2 + CREDITS.txt | 2 + README.rst | 23 ++- bootstrap.py | 170 ------------------ .../tests/test_default_commit_veto.py | 4 +- more/transaction/tests/test_transaction.py | 20 +-- pyproject.toml | 16 -- setup.py | 6 +- tox.ini | 25 ++- 12 files changed, 110 insertions(+), 246 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml delete mode 100644 bootstrap.py delete mode 100644 pyproject.toml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..90a59d3 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,50 @@ +name: CI + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events + push: + pull_request: + schedule: + - cron: '0 12 * * 0' # run once a week on Sunday + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + tests: + name: "Python ${{ matrix.python-version }}" + runs-on: "ubuntu-latest" + + strategy: + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "pypy3"] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: "actions/checkout@v2" + - uses: "actions/setup-python@v2" + with: + python-version: "${{ matrix.python-version }}" + - name: "Install dependencies" + run: | + set -xe + python -VV + python -m site + python -m pip install --upgrade pip setuptools wheel + python -m pip install --upgrade virtualenv tox tox-gh-actions + - name: "Run tox targets for ${{ matrix.python-version }}" + run: "python -m tox" + + - name: "Report to coveralls" + # coverage is only created in the py39 environment + # --service=github is a workaround for bug + # https://github.com/coveralls-clients/coveralls-python/issues/251 + if: "matrix.python-version == '3.9'" + run: | + pip install coveralls + coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 29bca3f..9a9ce62 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,14 @@ repos: - repo: https://github.com/psf/black - rev: stable + rev: 20.8b1 hooks: - id: black + - repo: https://gitlab.com/pycqa/flake8 + rev: "3.8.4" + hooks: + - id: flake8 + - repo: https://github.com/asottile/pyupgrade + rev: v2.7.4 + hooks: + - id: pyupgrade + args: [--py36-plus] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 622d714..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: python - -python: 3.8 - -cache: pip - -matrix: - include: - - python: "3.6" - env: TOXENV=py36 - - python: "3.7" - env: TOXENV=py37 - - python: "3.8" - env: TOXENV=py38 - - python: "3.8" - env: TOXENV=pep8 - - python: "3.8" - env: TOXENV=coverage -before_install: - - pip install --upgrade pip setuptools -install: - - pip install tox - - if [ "$TOXENV" = 'coverage' ]; then pip install coveralls; fi -script: - - tox -e $TOXENV -after_success: - - if [ "$TOXENV" = 'coverage' ]; then coveralls; fi diff --git a/CHANGES.txt b/CHANGES.txt index aa3cf2e..f2d94b6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,8 @@ CHANGES - Make Python 3.6 the minimal version +- Use GitHub Actions for CI. + 0.8 (2016-12-28) ================ diff --git a/CREDITS.txt b/CREDITS.txt index f75b33e..fb892fa 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -5,4 +5,6 @@ CREDITS * Denis Krienbühl (bug reports) +* Jürgen Gmach (CI) + * Special thanks to CONTACT software. diff --git a/README.rst b/README.rst index 1e63503..e1875db 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,17 @@ +.. image:: https://github.com/morepath/more.transaction/workflows/CI/badge.svg?branch=master + :target: https://github.com/morepath/more.transaction/actions?workflow=CI + :alt: CI Status + +.. image:: https://coveralls.io/repos/github/morepath/more.transaction/badge.svg?branch=master + :target: https://coveralls.io/github/morepath/more.transaction?branch=master + +.. image:: https://img.shields.io/pypi/v/more.transaction.svg + :target: https://pypi.org/project/more.transaction/ + +.. image:: https://img.shields.io/pypi/pyversions/more.transaction.svg + :target: https://pypi.org/project/more.transaction/ + + more.transaction: SQLAlchemy and ZODB integration for Morepath ============================================================== @@ -9,12 +23,3 @@ See https://github.com/morepath/morepath_sqlalchemy for a demo. .. _transaction: https://pypi.python.org/pypi/transaction .. _Morepath: http://morepath.readthedocs.org - -Build Status ------------- - -.. image:: https://travis-ci.com/morepath/more.transaction.svg?branch=master - :target: https://travis-ci.com/morepath/more.transaction - -.. image:: https://coveralls.io/repos/morepath/more.transaction/badge.svg?branch=master - :target: https://coveralls.io/r/morepath/more.transaction?branch=master diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100644 index 1b28969..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,170 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Bootstrap a buildout-based project - -Simply run this script in a directory containing a buildout.cfg. -The script accepts buildout command-line options, so you can -use the -c option to specify an alternate configuration file. -""" - -import os -import shutil -import sys -import tempfile - -from optparse import OptionParser - -tmpeggs = tempfile.mkdtemp() - -usage = '''\ -[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] - -Bootstraps a buildout-based project. - -Simply run this script in a directory containing a buildout.cfg, using the -Python that you want bin/buildout to use. - -Note that by using --find-links to point to local resources, you can keep -this script from going over the network. -''' - -parser = OptionParser(usage=usage) -parser.add_option("-v", "--version", help="use a specific zc.buildout version") - -parser.add_option("-t", "--accept-buildout-test-releases", - dest='accept_buildout_test_releases', - action="store_true", default=False, - help=("Normally, if you do not specify a --version, the " - "bootstrap script and buildout gets the newest " - "*final* versions of zc.buildout and its recipes and " - "extensions for you. If you use this flag, " - "bootstrap and buildout will get the newest releases " - "even if they are alphas or betas.")) -parser.add_option("-c", "--config-file", - help=("Specify the path to the buildout configuration " - "file to be used.")) -parser.add_option("-f", "--find-links", - help=("Specify a URL to search for buildout releases")) - - -options, args = parser.parse_args() - -###################################################################### -# load/install setuptools - -to_reload = False -try: - import pkg_resources - import setuptools -except ImportError: - ez = {} - - try: - from urllib.request import urlopen - except ImportError: - from urllib2 import urlopen - - # XXX use a more permanent ez_setup.py URL when available. - exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py' - ).read(), ez) - setup_args = dict(to_dir=tmpeggs, download_delay=0) - ez['use_setuptools'](**setup_args) - - if to_reload: - reload(pkg_resources) - import pkg_resources - # This does not (always?) update the default working set. We will - # do it. - for path in sys.path: - if path not in pkg_resources.working_set.entries: - pkg_resources.working_set.add_entry(path) - -###################################################################### -# Install buildout - -ws = pkg_resources.working_set - -cmd = [sys.executable, '-c', - 'from setuptools.command.easy_install import main; main()', - '-mZqNxd', tmpeggs] - -find_links = os.environ.get( - 'bootstrap-testing-find-links', - options.find_links or - ('http://downloads.buildout.org/' - if options.accept_buildout_test_releases else None) - ) -if find_links: - cmd.extend(['-f', find_links]) - -setuptools_path = ws.find( - pkg_resources.Requirement.parse('setuptools')).location - -requirement = 'zc.buildout' -version = options.version -if version is None and not options.accept_buildout_test_releases: - # Figure out the most recent final version of zc.buildout. - import setuptools.package_index - _final_parts = '*final-', '*final' - - def _final_version(parsed_version): - for part in parsed_version: - if (part[:1] == '*') and (part not in _final_parts): - return False - return True - index = setuptools.package_index.PackageIndex( - search_path=[setuptools_path]) - if find_links: - index.add_find_links((find_links,)) - req = pkg_resources.Requirement.parse(requirement) - if index.obtain(req) is not None: - best = [] - bestv = None - for dist in index[req.project_name]: - distv = dist.parsed_version - if _final_version(distv): - if bestv is None or distv > bestv: - best = [dist] - bestv = distv - elif distv == bestv: - best.append(dist) - if best: - best.sort() - version = best[-1].version -if version: - requirement = '=='.join((requirement, version)) -cmd.append(requirement) - -import subprocess -if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0: - raise Exception( - "Failed to execute command:\n%s", - repr(cmd)[1:-1]) - -###################################################################### -# Import and run buildout - -ws.add_entry(tmpeggs) -ws.require(requirement) -import zc.buildout.buildout - -if not [a for a in args if '=' not in a]: - args.append('bootstrap') - -# if -c was provided, we push it back into args for buildout' main function -if options.config_file is not None: - args[0:0] = ['-c', options.config_file] - -zc.buildout.buildout.main(args) -shutil.rmtree(tmpeggs) diff --git a/more/transaction/tests/test_default_commit_veto.py b/more/transaction/tests/test_default_commit_veto.py index 53c43ac..a789f27 100644 --- a/more/transaction/tests/test_default_commit_veto.py +++ b/more/transaction/tests/test_default_commit_veto.py @@ -60,7 +60,7 @@ def test_it_true_x_tm_anythingelse(): assert callFUT(response) -class DummyRequest(object): +class DummyRequest: path_info = "/" def __init__(self): @@ -71,7 +71,7 @@ def make_body_seekable(self): self.made_seekable += 1 -class DummyResponse(object): +class DummyResponse: def __init__(self, status="200 OK", headers=None): self.status = status if headers is None: diff --git a/more/transaction/tests/test_transaction.py b/more/transaction/tests/test_transaction.py index 6790048..4f0ebfd 100644 --- a/more/transaction/tests/test_transaction.py +++ b/more/transaction/tests/test_transaction.py @@ -13,7 +13,7 @@ class TestApp(TransactionApp): attempts = 0 @TestApp.path("/{type}/{id}") - class Document(object): + class Document: def __init__(self, type, id): self.type = type self.id = id @@ -43,7 +43,7 @@ class TestApp(TransactionApp): attempts = 0 @TestApp.path("/foo/bar") - class Foo(object): + class Foo: pass @TestApp.view(model=Foo) @@ -59,7 +59,7 @@ def view_foo(self, request): # if the unconsumed path is reset wrongly, it'll accidentally pick # up this model instead of Foo @TestApp.path("/bar/foo") - class Bar(object): + class Bar: pass @TestApp.view(model=Bar) @@ -88,7 +88,7 @@ def mount_testapp(): return TestApp() @TestApp.path("/sub") - class Foo(object): + class Foo: pass @TestApp.view(model=Foo) @@ -329,18 +329,18 @@ def handler(request): assert txn.committed -class DummySettingsSectionContainer(object): +class DummySettingsSectionContainer: def __init__(self): self.transaction = DummyTransactionSettingSection() -class DummyTransactionSettingSection(object): +class DummyTransactionSettingSection: def __init__(self): self.attempts = 1 self.commit_veto = None -class DummyApp(object): +class DummyApp: def __init__(self): self.settings = DummySettingsSectionContainer() @@ -372,7 +372,7 @@ def get(self): return self def setUser(self, name, path="/"): - self.username = "%s:%s" % (path, name) + self.username = f"{path}:{name}" def isDoomed(self): return self.doomed @@ -393,7 +393,7 @@ def note(self, value): self._note = value -class DummyRequest(object): +class DummyRequest: path = "/" identity = morepath.NO_IDENTITY @@ -412,7 +412,7 @@ def path_info(self): return self.path -class DummyResponse(object): +class DummyResponse: def __init__(self, status="200 OK", headers=None): self.status = status if headers is None: diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 396d8e9..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,16 +0,0 @@ -[tool.black] -line-length = 80 -target-version = ['py36', 'py37', 'py38'] -include = '\.pyi?$' -exclude = ''' -( - /( - \.git - | \.tox - | env - | build - | dist - | bootstrap.py - )/ -) -''' diff --git a/setup.py b/setup.py index 424ba09..3376853 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,9 @@ -import io from setuptools import setup, find_packages long_description = "\n".join( ( - io.open("README.rst", encoding="utf-8").read(), - io.open("CHANGES.txt", encoding="utf-8").read(), + open("README.rst", encoding="utf-8").read(), + open("CHANGES.txt", encoding="utf-8").read(), ) ) @@ -31,6 +30,7 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: PyPy", "Development Status :: 5 - Production/Stable", ], diff --git a/tox.ini b/tox.ini index 20eab24..235179a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36, py37, py38, pypy3, coverage, pep8 +envlist = py36, py37, py38, py39, pypy3, coverage, pre-commit skipsdist = True skip_missing_interpreters = True @@ -9,17 +9,26 @@ extras = test commands = pytest {posargs} -[testenv:pep8] -basepython = python3.8 -extras = pep8 - -commands = flake8 more/ setup.py - black --check more/ setup.py +[testenv:pre-commit] +deps = pre-commit +commands = pre-commit run --all-files [testenv:coverage] -basepython = python3.8 +basepython = python3 extras = test coverage commands = pytest --cov more/transaction --cov-fail-under=100 {posargs} +[gh-actions] +python = + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39, pre-commit, mypy, coverage + +[flake8] +max-line-length = 88 +ignore = + E231 # clashes with black + W503