diff --git a/.appveyor.yml b/.appveyor.yml index a0ffc795..9f176ee5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,18 +7,13 @@ image: environment: matrix: - TOXENV: check - - TOXENV: 'py27-pytest46-xdist127-coverage55' - - TOXENV: 'py35-pytest46-xdist127-coverage55' - TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' - TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - TOXENV: 'py39-pytest62-xdist202-coverage55' - - TOXENV: 'pypy-pytest46-xdist127-coverage55' - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' matrix: exclude: - - image: Visual Studio 2019 - TOXENV: 'py27-pytest46-xdist127-coverage55' - image: Visual Studio 2015 TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - image: Visual Studio 2015 @@ -27,15 +22,12 @@ matrix: TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - image: Visual Studio 2015 TOXENV: 'py39-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'pypy-pytest46-xdist127-coverage55' - image: Visual Studio 2015 TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - - IF "%TOXENV:~0,5%" == "pypy-" choco install --no-progress python.pypy - IF "%TOXENV:~0,6%" == "pypy3-" choco install --no-progress pypy3 - SET PATH=C:\tools\pypy\pypy;%PATH% - C:\Python37\python -m pip install --progress-bar=off tox -rci/requirements.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 67d55a68..dc36fa2b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,13 +2,13 @@ # pre-commit install # To update the pre-commit hooks run: # pre-commit install-hooks -exclude: '^(src/.*\.pth|\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer + exclude: '.*\.pth$' - id: debug-statements - repo: https://github.com/PyCQA/isort rev: 5.9.3 @@ -18,3 +18,8 @@ repos: rev: 3.9.2 hooks: - id: flake8 + - repo: https://github.com/asottile/pyupgrade + rev: v2.23.3 + hooks: + - id: pyupgrade + args: [--py36-plus] diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 61747a15..77daad5b 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,8 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals import os import subprocess @@ -30,7 +26,7 @@ def exec_in_env(): if not exists(env_path): import subprocess - print("Making bootstrap env in: {0} ...".format(env_path)) + print(f"Making bootstrap env in: {env_path} ...") try: check_call([sys.executable, "-m", "venv", env_path]) except subprocess.CalledProcessError: @@ -44,7 +40,7 @@ def exec_in_env(): if not os.path.exists(python_executable): python_executable += '.exe' - print("Re-executing with: {0}".format(python_executable)) + print(f"Re-executing with: {python_executable}") print("+ exec", python_executable, __file__, "--no-env") os.execv(python_executable, [python_executable, __file__, "--no-env"]) @@ -52,7 +48,7 @@ def exec_in_env(): def main(): import jinja2 - print("Project path: {0}".format(base_path)) + print(f"Project path: {base_path}") jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), @@ -78,7 +74,7 @@ def main(): with open(join(base_path, name), "w") as fh: fh.write('# NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/%s).\n' % name) fh.write(jinja.get_template(name).render(**template_vars)) - print("Wrote {}".format(name)) + print(f"Wrote {name}") print("DONE.") @@ -89,5 +85,5 @@ def main(): elif not args: exec_in_env() else: - print("Unexpected arguments {0}".format(args), file=sys.stderr) + print(f"Unexpected arguments {args}", file=sys.stderr) sys.exit(1) diff --git a/docs/conf.py b/docs/conf.py index 0668fdc2..45da656c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - import os import sphinx_py3doc_enhanced_theme @@ -25,7 +22,7 @@ project = 'pytest-cov' year = '2016' author = 'pytest-cov contributors' -copyright = '{}, {}'.format(year, author) +copyright = f'{year}, {author}' version = release = '2.12.1' pygments_style = 'trac' @@ -47,7 +44,7 @@ html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } -html_short_title = '%s-%s' % (project, version) +html_short_title = f'{project}-{version}' napoleon_use_ivar = True napoleon_use_rtype = False diff --git a/examples/adhoc-layout/example/__init__.py b/examples/adhoc-layout/example/__init__.py index 18080ac5..8a6dfd5e 100644 --- a/examples/adhoc-layout/example/__init__.py +++ b/examples/adhoc-layout/example/__init__.py @@ -1,4 +1,3 @@ - import sys PY2 = sys.version_info[0] == 2 diff --git a/examples/src-layout/src/example/__init__.py b/examples/src-layout/src/example/__init__.py index 18080ac5..8a6dfd5e 100644 --- a/examples/src-layout/src/example/__init__.py +++ b/examples/src-layout/src/example/__init__.py @@ -1,4 +1,3 @@ - import sys PY2 = sys.version_info[0] == 2 diff --git a/setup.py b/setup.py index d4e0c6de..11e98a88 100755 --- a/setup.py +++ b/setup.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -# -*- encoding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -import io import re from distutils.command.build import build from glob import glob @@ -22,7 +18,7 @@ def read(*names, **kwargs): - with io.open( + with open( join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') ) as fh: @@ -88,7 +84,7 @@ def run(self): version='2.12.1', license='MIT', description='Pytest plugin for measuring coverage.', - long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), + long_description='{}\n{}'.format(read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), author='Marc Schlaich', author_email='marc.schlaich@gmail.com', url='https://github.com/pytest-dev/pytest-cov', @@ -107,9 +103,8 @@ def run(self): 'Operating System :: POSIX', 'Operating System :: Unix', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', @@ -126,7 +121,7 @@ def run(self): 'pytest>=4.6', 'coverage[toml]>=5.2.1' ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', + python_requires='>=3.6', extras_require={ 'testing': [ 'fields', diff --git a/src/pytest_cov/compat.py b/src/pytest_cov/compat.py index 5b4a0bfb..f422f25c 100644 --- a/src/pytest_cov/compat.py +++ b/src/pytest_cov/compat.py @@ -14,7 +14,7 @@ hookwrapper = pytest.mark.hookwrapper -class SessionWrapper(object): +class SessionWrapper: def __init__(self, session): self._session = session if hasattr(session, 'testsfailed'): diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 084e92ea..0303c2f1 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -14,7 +14,7 @@ from .embed import cleanup -class _NullFile(object): +class _NullFile: @staticmethod def write(v): pass @@ -49,7 +49,7 @@ def ensure_topdir_wrapper(self, *args, **kwargs): return ensure_topdir_wrapper -class CovController(object): +class CovController: """Base class for different plugin implementations.""" def __init__(self, cov_source, cov_report, cov_config, cov_append, cov_branch, config=None, nodeid=None): @@ -116,7 +116,7 @@ def unset_env(): def get_node_desc(platform, version_info): """Return a description of this node.""" - return 'platform %s, python %s' % (platform, '%s.%s.%s-%s-%s' % version_info[:5]) + return 'platform {}, python {}'.format(platform, '%s.%s.%s-%s-%s' % version_info[:5]) @staticmethod def sep(stream, s, txt): @@ -126,7 +126,7 @@ def sep(stream, s, txt): sep_total = max((70 - 2 - len(txt)), 2) sep_len = sep_total // 2 sep_extra = sep_total % 2 - out = '%s %s %s\n' % (s * sep_len, txt, s * (sep_len + sep_extra)) + out = f'{s * sep_len} {txt} {s * (sep_len + sep_extra)}\n' stream.write(out) @_ensure_topdir diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index f5a00953..e214627c 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -36,7 +36,7 @@ def validate_report(arg): values = arg.split(":", 1) report_type = values[0] if report_type not in all_choices + ['']: - msg = 'invalid choice: "{}" (choose from "{}")'.format(arg, all_choices) + msg = f'invalid choice: "{arg}" (choose from "{all_choices}")' raise argparse.ArgumentTypeError(msg) if len(values) == 1: @@ -141,7 +141,7 @@ def pytest_load_initial_conftests(early_config, parser, args): early_config.pluginmanager.register(plugin, '_cov') -class CovPlugin(object): +class CovPlugin: """Use coverage package to produce code coverage reports. Delegates all work to a particular implementation based on whether @@ -196,7 +196,7 @@ def start(self, controller_cls, config=None, nodeid=None): if config is None: # fake config option for engine - class Config(object): + class Config: option = self.options config = Config() @@ -354,7 +354,7 @@ def pytest_runtest_call(self, item): yield -class TestContextPlugin(object): +class TestContextPlugin: def __init__(self, cov): self.cov = cov @@ -368,7 +368,7 @@ def pytest_runtest_call(self, item): self.switch_context(item, 'run') def switch_context(self, item, when): - context = "{item.nodeid}|{when}".format(item=item, when=when) + context = f"{item.nodeid}|{when}" self.cov.switch_context(context) os.environ['COV_CORE_CONTEXT'] = context diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 0d1b5a23..233058fd 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -16,7 +16,6 @@ from process_tests import TestProcess as _TestProcess from process_tests import dump_on_error from process_tests import wait_for_strings -from six import exec_ import pytest_cov.plugin @@ -780,13 +779,13 @@ def test_dist_not_collocated_coveragerc_source(testdir, prop): dir2 = testdir.mkdir('dir2') testdir.tmpdir.join('.coveragerc').write(''' [run] -%s -source = %s +{} +source = {} [paths] source = . dir1 - dir2''' % (prop.conf, script.dirpath())) + dir2'''.format(prop.conf, script.dirpath())) result = testdir.runpytest('-v', '--cov', @@ -1964,7 +1963,7 @@ def bad_init(): monkeypatch.setattr(embed, 'init', bad_init) monkeypatch.setattr(sys, 'stderr', buff) monkeypatch.setitem(os.environ, 'COV_CORE_SOURCE', 'foobar') - exec_(payload) + exec(payload) assert buff.getvalue() == '''pytest-cov: Failed to setup subprocess coverage. Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError() ''' @@ -2087,7 +2086,7 @@ def test_contexts(testdir, opts): continue data.set_query_context(context) actual = set(data.lines(test_context_path)) - assert line_data[label] == actual, "Wrong lines for context {!r}".format(context) + assert line_data[label] == actual, f"Wrong lines for context {context!r}" @pytest.mark.skipif("coverage.version_info >= (5, 0)") diff --git a/tox.ini b/tox.ini index 6be986e9..490a5201 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ passenv = [tox] envlist = check - py{27,35,36,37,py,py3}-pytest46-xdist127-coverage{55} + py{36,37,py,py3}-pytest46-xdist127-coverage{55} py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55} py{36,37,38,39,py3}-pytest{62}-xdist202-coverage{55} docs