From 42008984632607cb815c8b493eba9f3fd895b8f2 Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Tue, 28 Feb 2023 11:07:31 +0100
Subject: [PATCH 1/3] pre-commit: Replace flake8, isort, and pyupgrade with
 ruff

---
 .pre-commit-config.yaml | 16 ++++------------
 .ruff.toml              | 26 ++++++++++++++++++++++++++
 MANIFEST.in             |  8 ++++----
 3 files changed, 34 insertions(+), 16 deletions(-)
 create mode 100644 .ruff.toml

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1b105884..b1506c68 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,16 +10,8 @@ repos:
       - id: end-of-file-fixer
         exclude: '.*\.pth$'
       - id: debug-statements
-  - repo: https://github.com/PyCQA/isort
-    rev: 5.12.0
+  - repo: https://github.com/charliermarsh/ruff-pre-commit
+    rev: v0.0.253
     hooks:
-      - id: isort
-  - repo: https://github.com/asottile/pyupgrade
-    rev: v3.3.1
-    hooks:
-      - id: pyupgrade
-        args: [--py37-plus]
-  - repo: https://github.com/PyCQA/flake8
-    rev: 6.0.0
-    hooks:
-      - id: flake8
+      - id: ruff
+        # args: [--fix, --exit-non-zero-on-fix]
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 00000000..f285d165
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,26 @@
+# [tool.ruff]  # <- TODO Uncomment when migrating to pyproject.toml
+exclude = [
+    ".eggs",
+    ".tox",
+    "build",
+    "ci/templates",
+    "dist",
+]
+line-length = 140
+select = [
+    "E",
+    "F",
+    "I",
+    "PLC",
+    "PLE",
+    "UP",
+    "W",
+]
+target-version = "py37"
+
+# [tool.ruff.isort]  # <- TODO Uncomment when migrating to pyproject.toml
+[isort]
+# default-section = "THIRDPARTY"
+force-single-line = true
+forced-separate = ["test_pytest_cov"]
+known-first-party = ["pytest_cov"]
diff --git a/MANIFEST.in b/MANIFEST.in
index cbb88f74..ebf88344 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -7,22 +7,22 @@ prune examples/adhoc-layout/*.egg-info
 prune examples/src-layout/src/*.egg-info
 
 graft .github/workflows
-graft src
 graft ci
+graft src
 graft tests
 
 include .bumpversion.cfg
 include .cookiecutterrc
 include .coveragerc
 include .editorconfig
-include tox.ini
-include .readthedocs.yml
 include .pre-commit-config.yaml
+include .readthedocs.yml
+include .ruff.toml
 include AUTHORS.rst
 include CHANGELOG.rst
 include CONTRIBUTING.rst
 include LICENSE
 include README.rst
-
+include tox.ini
 
 global-exclude *.py[cod] __pycache__/* *.so *.dylib

From 430f0c862a55964a14e258c80411bf07b6e5e65d Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Mon, 20 Mar 2023 17:40:01 +0100
Subject: [PATCH 2/3] Make changes requested in code review

---
 .pre-commit-config.yaml                     |  2 +-
 .ruff.toml                                  | 49 +++++++++++++++++----
 examples/adhoc-layout/tests/test_example.py |  2 +-
 examples/src-layout/tests/test_example.py   |  2 +-
 setup.cfg                                   | 12 -----
 src/pytest_cov/embed.py                     |  7 +--
 src/pytest_cov/engine.py                    |  2 -
 src/pytest_cov/plugin.py                    |  9 ++--
 tests/contextful.py                         |  8 ++--
 tox.ini                                     |  4 +-
 10 files changed, 55 insertions(+), 42 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b1506c68..cb3c1e2a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,4 +14,4 @@ repos:
     rev: v0.0.253
     hooks:
       - id: ruff
-        # args: [--fix, --exit-non-zero-on-fix]
+        args: [--fix, --exit-non-zero-on-fix, --show-fixes]
diff --git a/.ruff.toml b/.ruff.toml
index f285d165..12fe797c 100644
--- a/.ruff.toml
+++ b/.ruff.toml
@@ -6,16 +6,33 @@ exclude = [
     "ci/templates",
     "dist",
 ]
-line-length = 140
-select = [
-    "E",
-    "F",
-    "I",
-    "PLC",
-    "PLE",
-    "UP",
-    "W",
+ignore = [
+    "ANN", # flake8-annotations
+    "ARG", # flake8-unused-arguments
+    "BLE", # flake8-blind-except
+    "COM", # flake8-comma
+    "D", # pydocstyle
+    "EM", # flake8-errmsg
+    "FBT", # flake8-boolean-trap
+    "INP", # flake8-no-pep420
+    "PLR0133",  # Pylint Refactor
+    "PLR2004",  # Pylint Refactor
+    "PLW", # Pylint Warning
+    "PTH", # flake8-use-pathlib
+    "Q", # flake8-quotes
+    "RET", # flake8-return
+    "RUF100", # Ruff-internal
+    "S101", # flake8-bandit assert
+    "S102", # flake8-bandit exec
+    "S110", # flake8-bandit try-except-pass
+    "SIM102", # flake8-simplify collapsible-if
+    "SIM105", # flake8-simplify use-contextlib-suppress
+    "SLF", # flake8-self
+    "T20", # flake8-print
+    "TRY", # tryceratops
 ]
+line-length = 140
+select = ["ALL"]
 target-version = "py37"
 
 # [tool.ruff.isort]  # <- TODO Uncomment when migrating to pyproject.toml
@@ -24,3 +41,17 @@ target-version = "py37"
 force-single-line = true
 forced-separate = ["test_pytest_cov"]
 known-first-party = ["pytest_cov"]
+
+[mccabe]
+max-complexity = 12
+
+[per-file-ignores]
+"ci/bootstrap.py" = ["S701"]
+"docs/conf.py" = ["A001"]
+"src/pytest_cov/plugin.py" = ["B904", "PT004"]
+"setup.py" = ["SIM117"]
+"tests/test_pytest_cov.py" = ["N801", "PT004", "RSE102", "SIM117"]
+
+[pylint]
+max-args = 8
+max-branches = 13
diff --git a/examples/adhoc-layout/tests/test_example.py b/examples/adhoc-layout/tests/test_example.py
index f4948e66..8cf207de 100644
--- a/examples/adhoc-layout/tests/test_example.py
+++ b/examples/adhoc-layout/tests/test_example.py
@@ -3,4 +3,4 @@
 
 def test_add():
     assert example.add(1, 1) == 2
-    assert not example.add(0, 1) == 2
+    assert example.add(0, 1) != 2
diff --git a/examples/src-layout/tests/test_example.py b/examples/src-layout/tests/test_example.py
index f4948e66..8cf207de 100644
--- a/examples/src-layout/tests/test_example.py
+++ b/examples/src-layout/tests/test_example.py
@@ -3,4 +3,4 @@
 
 def test_add():
     assert example.add(1, 1) == 2
-    assert not example.add(0, 1) == 2
+    assert example.add(0, 1) != 2
diff --git a/setup.cfg b/setup.cfg
index 360a416d..268333ec 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,7 +1,3 @@
-[flake8]
-max-line-length = 140
-exclude = .tox,.eggs,ci/templates,build,dist
-
 [tool:pytest]
 testpaths = tests
 python_files = test_*.py
@@ -9,11 +5,3 @@ addopts =
     -ra
     --strict
     -p pytester
-
-[tool:isort]
-force_single_line = True
-line_length = 120
-known_first_party = pytest_cov
-default_section = THIRDPARTY
-forced_separate = test_pytest_cov
-skip = .tox,.eggs,ci/templates,build,dist
diff --git a/src/pytest_cov/embed.py b/src/pytest_cov/embed.py
index f8a2749f..be50a50b 100644
--- a/src/pytest_cov/embed.py
+++ b/src/pytest_cov/embed.py
@@ -38,10 +38,7 @@ def init():
         import coverage
 
         # Determine all source roots.
-        if cov_source in os.pathsep:
-            cov_source = None
-        else:
-            cov_source = cov_source.split(os.pathsep)
+        cov_source = None if cov_source in os.pathsep else cov_source.split(os.pathsep)
         if cov_config == os.pathsep:
             cov_config = True
 
@@ -108,7 +105,7 @@ def _signal_cleanup_handler(signum, frame):
     elif signum == signal.SIGTERM:
         os._exit(128 + signum)
     elif signum == signal.SIGINT:
-        raise KeyboardInterrupt()
+        raise KeyboardInterrupt
 
 
 def cleanup_on_signal(signum):
diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py
index 27e1a090..91e32169 100644
--- a/src/pytest_cov/engine.py
+++ b/src/pytest_cov/engine.py
@@ -412,5 +412,3 @@ def finish(self):
 
     def summary(self, stream):
         """Only the master reports so do nothing."""
-
-        pass
diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py
index dd7b8c4e..4dfab1a0 100644
--- a/src/pytest_cov/plugin.py
+++ b/src/pytest_cov/plugin.py
@@ -35,7 +35,7 @@ def validate_report(arg):
     all_choices = term_choices + file_choices
     values = arg.split(":", 1)
     report_type = values[0]
-    if report_type not in all_choices + ['']:
+    if report_type not in [*all_choices, '']:
         msg = f'invalid choice: "{arg}" (choose from "{all_choices}")'
         raise argparse.ArgumentTypeError(msg)
 
@@ -247,7 +247,7 @@ def pytest_sessionstart(self, session):
         self.pid = os.getpid()
         if self._is_worker(session):
             nodeid = (
-                session.config.workerinput.get('workerid', getattr(session, 'nodeid'))
+                session.config.workerinput.get('workerid', session.nodeid)
             )
             self.start(engine.DistWorker, session.config, nodeid)
         elif not self._started:
@@ -389,13 +389,12 @@ def switch_context(self, item, when):
         os.environ['COV_CORE_CONTEXT'] = context
 
 
-@pytest.fixture
+@pytest.fixture()
 def no_cover():
     """A pytest fixture to disable coverage."""
-    pass
 
 
-@pytest.fixture
+@pytest.fixture()
 def cov(request):
     """A pytest fixture to provide access to the underlying coverage object."""
 
diff --git a/tests/contextful.py b/tests/contextful.py
index 3527e499..7edb87c0 100644
--- a/tests/contextful.py
+++ b/tests/contextful.py
@@ -39,7 +39,7 @@ def test_04(self):
         assert self.items[0] == "hello"             # r4
 
 
-@pytest.fixture
+@pytest.fixture()
 def some_data():
     return [1, 2, 3]                                # s5 s6
 
@@ -48,7 +48,7 @@ def test_05(some_data):
     assert len(some_data) == 3                      # r5
 
 
-@pytest.fixture
+@pytest.fixture()
 def more_data(some_data):
     return [2*x for x in some_data]                 # s6
 
@@ -83,7 +83,7 @@ def test_10():
     assert 1 == 1                                   # r10
 
 
-@pytest.mark.parametrize("x, ans", [
+@pytest.mark.parametrize(("x", "ans"), [
     (1, 101),
     (2, 202),
 ])
@@ -91,7 +91,7 @@ def test_11(x, ans):
     assert 100 * x + x == ans                       # r11-1 r11-2
 
 
-@pytest.mark.parametrize("x, ans", [
+@pytest.mark.parametrize(("x", "ans"), [
     (1, 101),
     (2, 202),
 ], ids=['one', 'two'])
diff --git a/tox.ini b/tox.ini
index aeccd9de..fe1c20cc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -93,14 +93,14 @@ deps =
     check-manifest
     colorama  # TODO Remove when isort > v6.0.0b2 is released.
     docutils
-    flake8
     isort
     pygments
     readme-renderer
+    ruff
 skip_install = true
 usedevelop = false
 commands =
     python setup.py check --strict --metadata --restructuredtext
     check-manifest {toxinidir}
-    flake8 src tests setup.py
+    ruff .
     isort --check-only --diff src tests setup.py

From 469085c29e662a7dc6746faf4bfcdd086fdfbe24 Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Mon, 20 Mar 2023 18:51:07 +0100
Subject: [PATCH 3/3] Add ruff per-file-ignores

---
 src/pytest_cov/plugin.py | 4 ++--
 tests/test_pytest_cov.py | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py
index 4dfab1a0..a406eb33 100644
--- a/src/pytest_cov/plugin.py
+++ b/src/pytest_cov/plugin.py
@@ -308,7 +308,7 @@ def pytest_runtestloop(self, session):
                 message = 'Failed to generate report: %s\n' % exc
                 session.config.pluginmanager.getplugin("terminalreporter").write(
                     'WARNING: %s\n' % message, red=True, bold=True)
-                warnings.warn(CovReportWarning(message))
+                warnings.warn(CovReportWarning(message))  # noqa: B028
                 self.cov_total = 0
             assert self.cov_total is not None, 'Test coverage should never be `None`'
             if self._failed_cov_total() and not self.options.collectonly:
@@ -320,7 +320,7 @@ def pytest_terminal_summary(self, terminalreporter):
             if self.options.no_cov_should_warn:
                 message = 'Coverage disabled via --no-cov switch!'
                 terminalreporter.write('WARNING: %s\n' % message, red=True, bold=True)
-                warnings.warn(CovDisabledWarning(message))
+                warnings.warn(CovDisabledWarning(message))  # noqa: B028
             return
         if self.cov_controller is None:
             return
diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py
index 77859bf4..d1f10a20 100644
--- a/tests/test_pytest_cov.py
+++ b/tests/test_pytest_cov.py
@@ -1945,7 +1945,7 @@ def test_contexts(pytester, testdir, opts):
 
     line_data = find_labels(contextful_tests, r"[crst]\d+(?:-\d+)?")
     for context, label in EXPECTED_CONTEXTS.items():
-        if context == '':
+        if context == '':  # noqa: PLC1901
             continue
         data.set_query_context(context)
         actual = set(data.lines(test_context_path))