From dcba9ae7e7a6430a348cd1a72164c472aef374af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 20 Nov 2025 12:06:09 +0100 Subject: [PATCH 1/4] MAINT: dropping Python 3.9 support --- .github/workflows/python-tests.yml | 14 +------------- setup.cfg | 3 +-- tests/test_encoding.py | 5 +++-- tox.ini | 2 +- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 5e93fe4..ec6abd3 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -19,21 +19,9 @@ jobs: # The aim with the matrix below is to walk through a representative but not full combination of OS, Python, and pytest versions. matrix: include: - - os: ubuntu-latest - python-version: '3.9' - toxenv: py39-test-pytestoldest - - os: windows-latest - python-version: '3.9' - toxenv: py39-test-pytest50 - - os: ubuntu-latest - python-version: '3.9' - toxenv: py39-test-pytest60 - - os: macos-latest - python-version: '3.9' - toxenv: py39-test-pytest62 - os: ubuntu-latest python-version: '3.10' - toxenv: py310-test-pytest70 + toxenv: py10-test-pytestoldest - os: ubuntu-latest python-version: '3.10' toxenv: py310-test-pytest71 diff --git a/setup.cfg b/setup.cfg index 81eb324..f482f60 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,7 +11,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 @@ -29,7 +28,7 @@ keywords = doctest, rst, pytest, py.test [options] zip_safe = False packages = find: -python_requires = >=3.9 +python_requires = >=3.10 setup_requires = setuptools_scm install_requires = diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 8215299..ff542b2 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -2,7 +2,8 @@ import os from pathlib import Path from textwrap import dedent -from typing import Callable, Optional +from typing import Optional +from collections.abc import Callable import pytest @@ -66,7 +67,7 @@ def f(): def ini_file(testdir) -> Callable[..., Path]: def makeini( - encoding: Optional[str] = None, + encoding: str | None = None, ) -> Path: """Create a pytest.ini file with the specified encoding.""" diff --git a/tox.ini b/tox.ini index 54c4644..a91612f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{39,310,311,312,313,314}-test + py{310,311,312,313,314}-test codestyle requires = setuptools >= 30.3.0 From c420aedd00b7e85b089754105e8f647ee4314430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 20 Nov 2025 12:06:52 +0100 Subject: [PATCH 2/4] MAINT: dropping pytest<7 support (7.0 is the first with official Python 3.10 compatibility) --- pytest_doctestplus/plugin.py | 61 +++++++++--------------------------- setup.cfg | 4 +-- tests/test_doctestplus.py | 14 ++------- tox.ini | 10 +----- 4 files changed, 20 insertions(+), 69 deletions(-) diff --git a/pytest_doctestplus/plugin.py b/pytest_doctestplus/plugin.py index 8f74a09..b8b70aa 100644 --- a/pytest_doctestplus/plugin.py +++ b/pytest_doctestplus/plugin.py @@ -28,14 +28,8 @@ OutputChecker) _pytest_version = Version(pytest.__version__) -PYTEST_GT_5 = _pytest_version > Version('5.9.9') -PYTEST_GE_5_4 = _pytest_version >= Version('5.4') -PYTEST_GE_7_0 = _pytest_version >= Version('7.0') PYTEST_GE_8_0 = _pytest_version >= Version('8.0') PYTEST_GE_8_1_1 = _pytest_version >= Version('8.1.1') -PYTEST_GE_8_2 = any([_pytest_version.is_devrelease, - _pytest_version.is_prerelease, - _pytest_version >= Version('8.2')]) comment_characters = { '.txt': '#', @@ -256,30 +250,21 @@ class DocTestModulePlus(doctest_plugin.DoctestModule): def collect(self): # When running directly from pytest we need to make sure that we # don't accidentally import setup.py! - if PYTEST_GE_7_0: - fspath = self.path - filepath = self.path.name - else: - fspath = self.fspath - filepath = self.fspath.basename + fspath = self.path + filepath = self.path.name if filepath in ("setup.py", "__main__.py"): return try: - if PYTEST_GT_5: - from _pytest.pathlib import import_path - mode = self.config.getoption("importmode") + from _pytest.pathlib import import_path + mode = self.config.getoption("importmode") if PYTEST_GE_8_1_1: consider_namespace_packages = self.config.getini("consider_namespace_packages") module = import_path(fspath, mode=mode, root=self.config.rootpath, consider_namespace_packages=consider_namespace_packages) - elif PYTEST_GE_7_0: - module = import_path(fspath, mode=mode, root=self.config.rootpath) - elif PYTEST_GT_5: - module = import_path(fspath, mode=mode) else: - module = fspath.pyimport() + module = import_path(fspath, mode=mode, root=self.config.rootpath) except ImportError: if self.config.getvalue("doctest_ignore_import_errors"): pytest.skip("unable to import module %r" % fspath) @@ -345,12 +330,8 @@ class DocTestTextfilePlus(pytest.Module): obj = None def collect(self): - if PYTEST_GE_7_0: - fspath = self.path - filepath = self.path.name - else: - fspath = self.fspath - filepath = self.fspath.basename + fspath = self.path + filepath = self.path.name encoding = self.config.getini("doctest_encoding") text = fspath.read_text(encoding) @@ -689,14 +670,10 @@ def pytest_ignore_collect(self, path, config): Skip paths that match any of the doctest_norecursedirs patterns or if doctest_only is True then skip all regular test files (eg test_*.py). """ - if PYTEST_GE_7_0: - dirpath = Path(path).parent - collect_ignore = config._getconftest_pathlist("collect_ignore", - path=dirpath, - rootpath=config.rootpath) - else: - dirpath = path.dirpath() - collect_ignore = config._getconftest_pathlist("collect_ignore", path=dirpath) + dirpath = Path(path).parent + collect_ignore = config._getconftest_pathlist("collect_ignore", + path=dirpath, + rootpath=config.rootpath) # The collect_ignore conftest.py variable should cause all test # runners to ignore this file and all subfiles and subdirectories @@ -779,12 +756,7 @@ def pytest_collect_file(self, path, parent): return None # Don't override the built-in doctest plugin - if PYTEST_GE_7_0: - return self._doctest_module_item_cls.from_parent(parent, path=Path(path)) - elif PYTEST_GE_5_4: - return self._doctest_module_item_cls.from_parent(parent, fspath=path) - else: - return self._doctest_module_item_cls(path, parent) + return self._doctest_module_item_cls.from_parent(parent, path=Path(path)) elif any([path.check(fnmatch=pat) for pat in self._file_globs]): # Ignore generated .rst files @@ -811,12 +783,7 @@ def pytest_collect_file(self, path, parent): # TODO: Get better names on these items when they are # displayed in py.test output - if PYTEST_GE_7_0: - return self._doctest_textfile_item_cls.from_parent(parent, path=Path(path)) - elif PYTEST_GE_5_4: - return self._doctest_textfile_item_cls.from_parent(parent, fspath=path) - else: - return self._doctest_textfile_item_cls(path, parent) + return self._doctest_textfile_item_cls.from_parent(parent, path=Path(path)) class DocTestFinderPlus(doctest.DocTestFinder): @@ -930,7 +897,7 @@ def _prepend_importorskip(self, test, *, module): """Prepends `pytest.importorskip` before the doctest.""" source = ( "import pytest; " - # Hide output of this statement in `___`, otherwise doctests fail + # Hide output of this statement in `___`, otherwise doctests fail f"___ = pytest.importorskip({module!r}); " # Don't impact what's available in the namespace "del pytest; del ___" diff --git a/setup.cfg b/setup.cfg index f482f60..f338b34 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ python_requires = >=3.10 setup_requires = setuptools_scm install_requires = - pytest>=4.6 + pytest>=7.0 packaging>=17.0 [options.extras_require] @@ -51,7 +51,7 @@ exclude = tests [tool:pytest] -minversion = 4.6 +minversion = 7.0 testpaths = tests pytest_doctestplus xfail_strict=true filterwarnings = diff --git a/tests/test_doctestplus.py b/tests/test_doctestplus.py index 71ecc40..9476494 100644 --- a/tests/test_doctestplus.py +++ b/tests/test_doctestplus.py @@ -24,9 +24,6 @@ pytest_plugins = ['pytester'] -PYTEST_LT_6 = Version(pytest.__version__) < Version('6.0.0') - - def test_ignored_whitespace(testdir): testdir.makeini( """ @@ -863,8 +860,6 @@ def test_doctest_float_replacement(tmp_path): @pytest.fixture() def subpackage_requires_testdir(testdir, request): - if request.param[0] == 'makepyprojecttoml' and PYTEST_LT_6: - return None, None config_file = getattr(testdir, request.param[0])(request.param[1]) @@ -902,7 +897,6 @@ def test_doctest_subpackage_requires(subpackage_requires_testdir, caplog): @pytest.mark.parametrize(('import_mode', 'expected'), [ - pytest.param('importlib', dict(passed=2), marks=pytest.mark.skipif(PYTEST_LT_6, reason="importlib import mode not supported on Pytest <6"), id="importlib"), pytest.param('append', dict(failed=1), id="append"), pytest.param('prepend', dict(failed=1), id="prepend"), ]) @@ -1390,8 +1384,6 @@ def wrapper(*args, **kwargs): @pytest.fixture() def norecursedirs_testdir(testdir, request): - if request.param[0] == 'makepyprojecttoml' and PYTEST_LT_6: - return None, None config_file = getattr(testdir, request.param[0])(request.param[1]) @@ -1550,14 +1542,14 @@ def f(): def test_skip_module_variable(testdir): p = testdir.makepyfile(""" __doctest_skip__ = ["f"] - + def f(): ''' >>> 1 + 2 5 ''' pass - + def g(): ''' >>> 1 + 1 @@ -1580,7 +1572,7 @@ def f(): >>> import module_that_is_not_availabe ''' pass - + def g(): ''' Test that call to `pytest.importorskip` is not visible diff --git a/tox.ini b/tox.ini index a91612f..a4b9b08 100644 --- a/tox.ini +++ b/tox.ini @@ -14,15 +14,7 @@ setenv = numpydev: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/scientific-python-nightly-wheels/simple description = run tests deps = - pytestoldest: pytest==4.6.0 - pytest50: pytest==5.0.* - pytest51: pytest==5.1.* - pytest52: pytest==5.2.* - pytest53: pytest==5.3.* - pytest60: pytest==6.0.* - pytest61: pytest==6.1.* - pytest62: pytest==6.2.* - pytest70: pytest==7.0.* + pytestoldest: pytest==7.0.0 pytest71: pytest==7.1.* pytest72: pytest==7.2.* pytest73: pytest==7.3.* From 152091b5e41e65fc8c6c8bc92e44ce4a1d5b2e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 20 Nov 2025 12:07:40 +0100 Subject: [PATCH 3/4] DOC: adding changelog --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 61a4eaa..759cf05 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ 1.7.0 (unreleased) ================== - +- Versions of Python <3.10 are no longer supported. [#313] 1.6.0 (2025-11-20) ================== From 5ab5024ce715a74c262ffdbff6a6430d1e5e090a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 20 Nov 2025 11:19:47 -0800 Subject: [PATCH 4/4] Update CHANGES.rst Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com> --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 759cf05..b0ee624 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ 1.7.0 (unreleased) ================== -- Versions of Python <3.10 are no longer supported. [#313] +- Versions of Python <3.10 and pytest<7 are no longer supported. [#313] 1.6.0 (2025-11-20) ==================