From 064ecdb39e6ff60f6c5e92841961ca99356400de Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 22 Sep 2021 17:15:31 +0300 Subject: [PATCH 1/4] Replace Travis CI with GitHub Actions --- .github/workflows/test.yml | 53 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 31 ---------------------- DEVELOPER.rst | 4 +-- README.rst | 6 ++--- 4 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..43f6829 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +name: Test + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10-dev"] + os: [ubuntu-latest, macos-latest, windows-latest] + exclude: + # TODO Fix these versions on Windows + - {os: windows-latest, python-version: "3.7"} + - {os: windows-latest, python-version: "3.8"} + - {os: windows-latest, python-version: "3.9"} + - {os: windows-latest, python-version: "3.10-dev"} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.os }}-${{ matrix.python-version }}-v1-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -e . + pip install -r tests/requirements.txt + + - name: Test + run: | + pytest -vv diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e69ccf5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -dist: xenial -language: python -# Use new container-based Travis infrastructure. -sudo: false -cache: pip - -python: - - 2.7 - - 3.5 - - 3.6 - - 3.7 - - "nightly" - -allow_failures: - # Just tests how PyInstaller performs with upcoming Python 3.8 - - python: "nightly" - -# Install dependencies. -install: - # Install unittest2pytest - - pip install -e . - - # Install dependencies for tests. - - # The download-progress bars break Travis's log view. Disable them - # by piping output through another program (if output is not a tty, - # no progress bars) - - pip install -r tests/requirements.txt | cat - -script: - - py.test -vv diff --git a/DEVELOPER.rst b/DEVELOPER.rst index 2819ac6..449d1c2 100644 --- a/DEVELOPER.rst +++ b/DEVELOPER.rst @@ -3,7 +3,7 @@ Testing =========== Continuous integration tests are available at -https://travis-ci.org/pytest-dev/unittest2pytest/. +https://github.com/pytest-dev/unittest2pytest/actions. Prior to pushing a pull-request to github, please test locally:: @@ -57,7 +57,7 @@ Full Release Process tox -e py27,py33,py34 5. Be sure `CI tests - `_ pass. + `_ pass. Now we start with the main release process. diff --git a/README.rst b/README.rst index 65a2496..0b31528 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,9 @@ Helps converting unittest test-cases to pytest :Homepage: https://github.com/pytest-dev/unittest2pytest -.. image:: https://secure.travis-ci.org/pytest-dev/unittest2pytest.png?branch=develop - :target: https://travis-ci.org/pytest-dev/unittest2pytest/ - +.. image:: https://github.com/pytest-dev/unittest2pytest/actions/workflows/test.yml/badge.svg + :target: https://github.com/pytest-dev/unittest2pytest/actions + :alt: See Build Status on GitHub Actions `unittest2pytest` is a tool that helps rewriting Python `unittest` test-cases into pytest_ test-cases. From 4539b8333b6508e1d777795759ce311e5a699561 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 22 Sep 2021 17:34:42 +0300 Subject: [PATCH 2/4] Add support for Python 3.8-3.10 --- setup.py | 3 +++ tox.ini | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ce2848f..9545486 100644 --- a/setup.py +++ b/setup.py @@ -75,6 +75,9 @@ def read(filename): "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Software Development", "Topic :: Utilities", ], diff --git a/tox.ini b/tox.ini index 154599b..f94c513 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests +# Tox (https://tox.readthedocs.io) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] -envlist = py27, py35, py36, py37 +envlist = py27, py35, py36, py37, py38, py39, py310 [testenv] deps = From 498d6e15b00e17561988cac8c10dbc93239faf4a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 22 Sep 2021 17:49:42 +0300 Subject: [PATCH 3/4] Drop support for EOL Python 2.7 and 3.5 --- .github/workflows/test.yml | 2 +- DEVELOPER.rst | 8 +-- setup.cfg | 3 - setup.py | 17 ++--- tox.ini | 2 +- unittest2pytest/fixes/fix_self_assert.py | 5 +- unittest2pytest/utils.py | 83 ++++-------------------- 7 files changed, 27 insertions(+), 93 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 43f6829..2036733 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10-dev"] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev"] os: [ubuntu-latest, macos-latest, windows-latest] exclude: # TODO Fix these versions on Windows diff --git a/DEVELOPER.rst b/DEVELOPER.rst index 449d1c2..587605b 100644 --- a/DEVELOPER.rst +++ b/DEVELOPER.rst @@ -5,10 +5,10 @@ Testing Continuous integration tests are available at https://github.com/pytest-dev/unittest2pytest/actions. -Prior to pushing a pull-request to github, please test locally:: +Prior to pushing a pull request to GitHub, please test locally:: pip install tox pytest - tox # or tox -e py27,py33,py34 + tox # or tox -e py37,py38,py39 Version Scheme @@ -54,7 +54,7 @@ Full Release Process 4. Be sure that the current code passes tests locally:: - tox -e py27,py33,py34 + tox -e py37,py38,py39 5. Be sure `CI tests `_ pass. @@ -154,7 +154,7 @@ Now we start with the main release process. git push --follow-tags origin master -15. Create release on github: +15. Create release on GitHub: a. Go to the `unittest2pytest release page `_ diff --git a/setup.cfg b/setup.cfg index 8313024..ebd6f52 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [zest.releaser] python-file-with-version = unittest2pytest/__init__.py push-changes = no diff --git a/setup.py b/setup.py index 9545486..b927f3a 100644 --- a/setup.py +++ b/setup.py @@ -20,9 +20,7 @@ # from setuptools import setup -import codecs import re -import sys def get_version(filename): @@ -37,14 +35,11 @@ def get_version(filename): def read(filename): - try: - return unicode(codecs.open(filename, encoding='utf-8').read()) - except NameError: - return open(filename, 'r', encoding='utf-8').read() + return open(filename, 'r', encoding='utf-8').read() + + long_description = '\n\n'.join([read('README.rst'), read('CHANGES.rst')]) -if sys.version_info < (3,): - long_description = long_description.encode('utf-8') setup( @@ -69,10 +64,8 @@ def read(filename): "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", @@ -81,6 +74,6 @@ def read(filename): "Topic :: Software Development", "Topic :: Utilities", ], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", + python_requires=">=3.6", zip_safe=False ) diff --git a/tox.ini b/tox.ini index f94c513..fea8ff2 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py35, py36, py37, py38, py39, py310 +envlist = py36, py37, py38, py39, py310 [testenv] deps = diff --git a/unittest2pytest/fixes/fix_self_assert.py b/unittest2pytest/fixes/fix_self_assert.py index 8000c08..30e5341 100644 --- a/unittest2pytest/fixes/fix_self_assert.py +++ b/unittest2pytest/fixes/fix_self_assert.py @@ -136,9 +136,8 @@ def RaisesOp(context, exceptionClass, indent, kws, arglist, node): exceptionClass.prefix = "" args = [exceptionClass] # Add match keyword arg to with statement if an expected regex was provided. - # In py27 the keyword is `expected_regexp`, in py3 is `expected_regex` - if 'expected_regex' in kws or 'expected_regexp' in kws: - expected_regex = kws.get('expected_regex', kws.get('expected_regexp')).clone() + if 'expected_regex' in kws: + expected_regex = kws.get('expected_regex').clone() expected_regex.prefix = '' args.append(String(', ')) args.append( diff --git a/unittest2pytest/utils.py b/unittest2pytest/utils.py index 9f96200..31789ed 100644 --- a/unittest2pytest/utils.py +++ b/unittest2pytest/utils.py @@ -27,80 +27,25 @@ import inspect -try: - from inspect import Parameter -except ImportError: - # Python 2 - pass -from collections import OrderedDict +from inspect import Parameter class SelfMarker: pass -def __apply_defaults(boundargs): - # Backport of Python 3.5 inspect.BoundArgs.apply_defaults() - arguments = boundargs.arguments - if not arguments: - return - new_arguments = [] - for name, param in boundargs.signature.parameters.items(): - try: - new_arguments.append((name, arguments[name])) - except KeyError: - if param.default is not Parameter.empty: - val = param.default - elif param.kind is Parameter.VAR_POSITIONAL: - val = () - elif param.kind is Parameter.VAR_KEYWORD: - val = {} - else: - # This BoundArguments was likely produced by - # Signature.bind_partial(). - continue - new_arguments.append((name, val)) - boundargs.arguments = OrderedDict(new_arguments) - def resolve_func_args(test_func, posargs, kwargs): - try: - inspect.signature - except AttributeError: - # Python 2.7 - posargs.insert(0, SelfMarker) - args = inspect.getcallargs(test_func, *posargs, **kwargs) - - assert args['self'] == SelfMarker - argspec = inspect.getargspec(test_func) - #if not 'Raises' in method: - # assert argspec.varargs is None # unhandled case - # assert argspec.keywords is None # unhandled case - - # get the required arguments - if argspec.defaults: - required_args = argspec.args[1:-len(argspec.defaults)] - else: - required_args = argspec.args[1:] - required_args = [args[argname] for argname in required_args] - - else: - sig = inspect.signature(test_func) - assert (list(iter(sig.parameters))[0] == 'self') - posargs.insert(0, SelfMarker) - ba = sig.bind(*posargs, **kwargs) - try: - ba.apply_defaults - except AttributeError: - # Python < 3.5 - __apply_defaults(ba) - else: - ba.apply_defaults() - args = ba.arguments - required_args = [n for n,v in sig.parameters.items() - if (v.default is Parameter.empty and - v.kind not in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD))] - assert args['self'] == SelfMarker - assert required_args[0] == 'self' - del required_args[0], args['self'] - required_args = [args[n] for n in required_args] + sig = inspect.signature(test_func) + assert (list(iter(sig.parameters))[0] == 'self') + posargs.insert(0, SelfMarker) + ba = sig.bind(*posargs, **kwargs) + ba.apply_defaults() + args = ba.arguments + required_args = [n for n,v in sig.parameters.items() + if (v.default is Parameter.empty and + v.kind not in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD))] + assert args['self'] == SelfMarker + assert required_args[0] == 'self' + del required_args[0], args['self'] + required_args = [args[n] for n in required_args] return required_args, args From 622abb44d93e8f26bb14da0d96f6db6563d8b152 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 22 Sep 2021 17:52:31 +0300 Subject: [PATCH 4/4] Upgrade Python syntax with pyupgrade --py36-plus (don't apply assert changes) --- setup.py | 3 +-- tests/test_all_fixes.py | 4 +--- unittest2pytest/__init__.py | 1 - unittest2pytest/__main__.py | 1 - unittest2pytest/fixes/fix_remove_class.py | 1 - unittest2pytest/fixes/fix_self_assert.py | 1 - unittest2pytest/utils.py | 1 - 7 files changed, 2 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index b927f3a..8c305d4 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Copyright 2015-2019 by Hartmut Goebel # @@ -35,7 +34,7 @@ def get_version(filename): def read(filename): - return open(filename, 'r', encoding='utf-8').read() + return open(filename, encoding='utf-8').read() long_description = '\n\n'.join([read('README.rst'), diff --git a/tests/test_all_fixes.py b/tests/test_all_fixes.py index c435554..2463e70 100644 --- a/tests/test_all_fixes.py +++ b/tests/test_all_fixes.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ This file is part of test-suite for unittest2pytest. """ @@ -22,7 +21,6 @@ # along with this program. If not, see . # -from __future__ import unicode_literals __author__ = "Hartmut Goebel " __copyright__ = "Copyright 2015-2019 by Hartmut Goebel" @@ -108,7 +106,7 @@ def test_check_fixture(in_file, fixer, tmpdir): try: compile(''.join(expected_contents), expected_file, 'exec') except Exception as e: - pytest.fail("FATAL: %s does not compile: %s" % (expected_file, e), + pytest.fail(f"FATAL: {expected_file} does not compile: {e}", False) if result_file_contents != expected_contents: diff --git a/unittest2pytest/__init__.py b/unittest2pytest/__init__.py index e63c1a5..4d087ef 100644 --- a/unittest2pytest/__init__.py +++ b/unittest2pytest/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright 2015-2019 by Hartmut Goebel # diff --git a/unittest2pytest/__main__.py b/unittest2pytest/__main__.py index dfcf051..384f85c 100755 --- a/unittest2pytest/__main__.py +++ b/unittest2pytest/__main__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright 2015-2019 by Hartmut Goebel # diff --git a/unittest2pytest/fixes/fix_remove_class.py b/unittest2pytest/fixes/fix_remove_class.py index 0fb3b3d..9f0109d 100644 --- a/unittest2pytest/fixes/fix_remove_class.py +++ b/unittest2pytest/fixes/fix_remove_class.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ fix_remove_class - lib2to3 fix for removing "class Testxxx(TestCase):" headers and dedenting the contained code. diff --git a/unittest2pytest/fixes/fix_self_assert.py b/unittest2pytest/fixes/fix_self_assert.py index 30e5341..2b0cad5 100644 --- a/unittest2pytest/fixes/fix_self_assert.py +++ b/unittest2pytest/fixes/fix_self_assert.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ fix_self_assert - lib2to3 fix for replacing assertXXX() method calls by their pytest equivalent. diff --git a/unittest2pytest/utils.py b/unittest2pytest/utils.py index 31789ed..c223830 100644 --- a/unittest2pytest/utils.py +++ b/unittest2pytest/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Some utility functions for unittest2pytest. """