From 9ad934413bf5de05485e6ed51d7c80735ec208d4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 4 Nov 2026 19:58:49 -0300 Subject: [PATCH 1/4] Drop support for EOL Python 3.8, 3.9, and 3.10 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 4 ++-- CHANGELOG.md | 4 ++++ setup.py | 5 +---- tox.ini | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01f4fc5..075690a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v7.1.0 with: - python-version: "3.8" + python-version: "3.11" - name: Install GoogleTest run: | wget https://github.com/google/googletest/archive/release-${{ matrix.gtest_ver }}.tar.gz @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python: ["3.11", "3.12"] steps: - uses: actions/checkout@v7.0.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff1913..42af747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# UNRELEASED + +- Dropped support for EOL Python 3.8, 3.9, and 3.10. + # 2.6.0 *2024-09-17* diff --git a/setup.py b/setup.py index c79698f..2f62eb5 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ "pytest11": ["cpp = pytest_cpp.plugin"], }, install_requires=["pytest"], - python_requires=">=3.8", + python_requires=">=3.11", author="Bruno Oliveira", author_email="nicoddemus@gmail.com", description="Use pytest's runner to discover and execute C++ tests", @@ -26,9 +26,6 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: C++", diff --git a/tox.ini b/tox.ini index 9264d9f..27ebad4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{38, 39, 310, 311, 312} +envlist = py{311, 312} [testenv] deps= From 84729522c1b1f243dffe03997df65829cdc7ed97 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 4 Nov 2026 20:02:17 -0300 Subject: [PATCH 2/4] Add support for Python 3.13, 3.14, and 3.15 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 2 +- CHANGELOG.md | 1 + setup.py | 3 +++ tox.ini | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 075690a..a421295 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - python: ["3.11", "3.12"] + python: ["3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v7.0.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 42af747..f2fdda8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # UNRELEASED - Dropped support for EOL Python 3.8, 3.9, and 3.10. +- Added support for Python 3.13, 3.14, and 3.15. # 2.6.0 diff --git a/setup.py b/setup.py index 2f62eb5..f035419 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,9 @@ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Programming Language :: C++", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", diff --git a/tox.ini b/tox.ini index 27ebad4..44d5393 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{311, 312} +envlist = py{311, 312, 313, 314, 315} [testenv] deps= From fef7f1de0449ca112149b9f13a44c789ff8392c4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 4 Nov 2026 20:04:49 -0300 Subject: [PATCH 3/4] Apply pyupgrade --py311-plus Also fix the type annotation of the `parent` parameter in `CppFile.__init__`, which is a `pytest.Collector`, as already declared by `CppFile.from_parent`. Co-Authored-By: Claude Opus 5.5 --- src/pytest_cpp/boost.py | 11 +++++------ src/pytest_cpp/catch2.py | 11 +++++------ src/pytest_cpp/error.py | 9 ++++----- src/pytest_cpp/facade_abc.py | 2 +- src/pytest_cpp/google.py | 8 ++++---- src/pytest_cpp/helpers.py | 2 +- src/pytest_cpp/plugin.py | 9 ++++----- 7 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/pytest_cpp/boost.py b/src/pytest_cpp/boost.py index b97352c..27c353e 100644 --- a/src/pytest_cpp/boost.py +++ b/src/pytest_cpp/boost.py @@ -1,10 +1,9 @@ from __future__ import annotations -import io import os import subprocess import tempfile -from typing import Sequence +from collections.abc import Sequence from xml.etree import ElementTree from pytest_cpp.error import CppTestFailure @@ -29,7 +28,7 @@ def is_test_suite( output = subprocess.check_output( args, stderr=subprocess.STDOUT, - universal_newlines=True, + text=True, ) except (subprocess.CalledProcessError, OSError): return False @@ -55,9 +54,9 @@ def run_test( ) -> tuple[Sequence[BoostTestFailure] | None, str]: def read_file(name: str) -> str: try: - with io.open(name) as f: + with open(name) as f: return f.read() - except IOError: + except OSError: return "" with tempfile.TemporaryDirectory(prefix="pytest-cpp") as temp_dir: @@ -128,7 +127,7 @@ def _parse_log(self, log: str) -> list[BoostTestFailure]: # Boosttest will sometimes generate unparseable XML # so we surround it with xml tags. parsed_elements = [] - log = "{}".format(log) + log = f"{log}" log_root = ElementTree.fromstring(log) testlog = log_root.find("TestLog") diff --git a/src/pytest_cpp/catch2.py b/src/pytest_cpp/catch2.py index 8b4eb9e..507dace 100644 --- a/src/pytest_cpp/catch2.py +++ b/src/pytest_cpp/catch2.py @@ -4,8 +4,7 @@ import os import subprocess import tempfile -from typing import Optional -from typing import Sequence +from collections.abc import Sequence from xml.etree import ElementTree import pytest @@ -39,13 +38,13 @@ def get_catch_version( cls, executable: str, harness_collect: Sequence[str] = (), - ) -> Optional[Catch2Version]: + ) -> Catch2Version | None: args = make_cmdline(harness_collect, executable, ["--help"]) try: output = subprocess.check_output( args, stderr=subprocess.STDOUT, - universal_newlines=True, + text=True, ) except (subprocess.CalledProcessError, OSError): return None @@ -91,7 +90,7 @@ def list_tests( output = subprocess.check_output( args, stderr=subprocess.STDOUT, - universal_newlines=True, + text=True, ) except subprocess.CalledProcessError as e: output = e.output @@ -132,7 +131,7 @@ def run_test( try: output = subprocess.check_output( - args, stderr=subprocess.STDOUT, universal_newlines=True + args, stderr=subprocess.STDOUT, text=True ) except subprocess.CalledProcessError as e: output = e.output diff --git a/src/pytest_cpp/error.py b/src/pytest_cpp/error.py index da77df5..dd923b3 100644 --- a/src/pytest_cpp/error.py +++ b/src/pytest_cpp/error.py @@ -4,8 +4,7 @@ import string from abc import ABC from abc import abstractmethod -from typing import Sequence -from typing import Tuple +from collections.abc import Sequence from _pytest._code.code import ReprFileLocation from _pytest._io import TerminalWriter @@ -20,7 +19,7 @@ def __init__(self, failures: Sequence[CppTestFailure]) -> None: self.failures = list(failures) -Markup = Tuple[str, ...] +Markup = tuple[str, ...] class CppTestFailure(ABC): @@ -49,7 +48,7 @@ def get_file_reference(self) -> tuple[str, int]: """ -class CppFailureRepr(object): +class CppFailureRepr: """ "repr" object for pytest that knows how to print a CppFailure instance into both terminal and files. @@ -65,7 +64,7 @@ def __str__(self) -> str: for failure in self.failures: pure_lines = "\n".join(x[0] for x in failure.get_lines()) repr_loc = self._get_repr_file_location(failure) - reprs.append("%s\n%s" % (pure_lines, repr_loc)) + reprs.append(f"{pure_lines}\n{repr_loc}") return self.failure_sep.join(reprs) def _get_repr_file_location(self, failure: CppTestFailure) -> ReprFileLocation: diff --git a/src/pytest_cpp/facade_abc.py b/src/pytest_cpp/facade_abc.py index d2a2935..55c59a8 100644 --- a/src/pytest_cpp/facade_abc.py +++ b/src/pytest_cpp/facade_abc.py @@ -2,7 +2,7 @@ from abc import ABC from abc import abstractmethod -from typing import Sequence +from collections.abc import Sequence from pytest_cpp.error import CppTestFailure diff --git a/src/pytest_cpp/google.py b/src/pytest_cpp/google.py index cc2bdc7..cbb4eb7 100644 --- a/src/pytest_cpp/google.py +++ b/src/pytest_cpp/google.py @@ -3,7 +3,7 @@ import os import subprocess import tempfile -from typing import Sequence +from collections.abc import Sequence from xml.etree import ElementTree import pytest @@ -30,7 +30,7 @@ def is_test_suite( output = subprocess.check_output( args, stderr=subprocess.STDOUT, - universal_newlines=True, + text=True, ) except (subprocess.CalledProcessError, OSError): return False @@ -55,7 +55,7 @@ def list_tests( output = subprocess.check_output( args, stderr=subprocess.STDOUT, - universal_newlines=True, + text=True, ) def strip_comment(x: str) -> str: @@ -100,7 +100,7 @@ def run_test( try: output = subprocess.check_output( - args, stderr=subprocess.STDOUT, universal_newlines=True + args, stderr=subprocess.STDOUT, text=True ) except subprocess.CalledProcessError as e: output = e.output diff --git a/src/pytest_cpp/helpers.py b/src/pytest_cpp/helpers.py index 88c337f..7692f49 100644 --- a/src/pytest_cpp/helpers.py +++ b/src/pytest_cpp/helpers.py @@ -1,4 +1,4 @@ -from typing import Sequence +from collections.abc import Sequence def make_cmdline( diff --git a/src/pytest_cpp/plugin.py b/src/pytest_cpp/plugin.py index 781b738..ec83c16 100644 --- a/src/pytest_cpp/plugin.py +++ b/src/pytest_cpp/plugin.py @@ -3,12 +3,11 @@ import os import stat import sys +from collections.abc import Iterator +from collections.abc import Sequence from fnmatch import fnmatch from pathlib import Path from typing import Any -from typing import Iterator -from typing import Sequence -from typing import Type from typing import TYPE_CHECKING import pytest @@ -24,7 +23,7 @@ from _pytest._code.code import TerminalRepr -FACADES: Sequence[Type[AbstractFacade]] = ( +FACADES: Sequence[type[AbstractFacade]] = ( GoogleTestFacade, BoostTestFacade, Catch2Facade, @@ -123,7 +122,7 @@ def __init__( self, *, path: Path, - parent: pytest.Item, + parent: pytest.Collector, facade: AbstractFacade, arguments: Sequence[str], **kwargs: Any, From f475a9913350373ef47e81f5cb2db8415eb59c3f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 4 Nov 2026 20:07:08 -0300 Subject: [PATCH 4/4] CI: fix GoogleTest build with CMake 4 GoogleTest 1.11.0 declares a `cmake_minimum_required` below 3.5, which is rejected by the CMake version available in the runner. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a421295..45ef32d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: cd googletest-release-${{ matrix.gtest_ver }} mkdir build cd build - cmake .. + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. sudo make install - name: Install Boost.Test run: |