From 353805ee61314a3d11288ccbe3a706a1ffa66ce8 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Wed, 9 Apr 2025 18:41:37 +0100 Subject: [PATCH 01/16] Drop support for Python 3.6 --- .github/workflows/python_ci.yml | 6 ++---- .github/workflows/python_ci_linux.yml | 6 ++---- .github/workflows/python_ci_macos.yml | 5 ++--- pyproject.toml | 4 ++-- repo_helper.yml | 4 +--- tox.ini | 10 +++------- 6 files changed, 12 insertions(+), 23 deletions(-) diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index 4505aef..83f4854 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -22,21 +22,19 @@ jobs: runs-on: "windows-2019" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.6,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False matrix: config: - - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12", testenvs: "py312,build", experimental: False} - - {python-version: "3.13", testenvs: "py313-dev,build", experimental: True} - - {python-version: "pypy-3.6", testenvs: "pypy36", experimental: False} + - {python-version: "3.13", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9-v7.3.15", testenvs: "pypy39,build", experimental: True} diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index de8bd50..3158d9c 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -23,21 +23,19 @@ jobs: runs-on: "ubuntu-20.04" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.6,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False matrix: config: - - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12", testenvs: "py312,build", experimental: False} - - {python-version: "3.13", testenvs: "py313-dev,build", experimental: True} - - {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False} + - {python-version: "3.13", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9", testenvs: "pypy39,build", experimental: True} diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index d008b4e..f174699 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -22,20 +22,19 @@ jobs: runs-on: "macos-13" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False matrix: config: - - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12", testenvs: "py312,build", experimental: False} - - {python-version: "3.13", testenvs: "py313-dev,build", experimental: True} + - {python-version: "3.13", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9", testenvs: "pypy39,build", experimental: True} diff --git a/pyproject.toml b/pyproject.toml index 335c68b..35144b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,13 +16,13 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "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", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", @@ -142,7 +142,7 @@ base-classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed", ] -python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12",] +python-versions = [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" diff --git a/repo_helper.yml b/repo_helper.yml index 4c9b41d..e2f87c8 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -20,15 +20,13 @@ conda_channels: - conda-forge python_versions: - 3.6: 3.7: 3.8: 3.9: "3.10": "3.11": "3.12": - "3.13-dev": - pypy36: + "3.13": pypy37: pypy38: pypy39: diff --git a/tox.ini b/tox.ini index 8e920f9..f9e840a 100644 --- a/tox.ini +++ b/tox.ini @@ -22,15 +22,13 @@ [tox] envlist = - py36 py37 py38 py39 py310 py311 py312 - py313-dev - pypy36 + py313 pypy37 pypy38 pypy39 @@ -47,15 +45,13 @@ requires = [envlists] test = - py36 py37 py38 py39 py310 py311 py312 - py313-dev - pypy36 + py313 pypy37 pypy38 pypy39 @@ -68,7 +64,7 @@ setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 -[testenv:py313-dev] +[testenv:py313] download = True setenv = PYTHONDEVMODE=1 From 458df4bed76191aa386eb41c79b9c0ba5f6ed80d Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:21:54 +0100 Subject: [PATCH 02/16] Bump Ubuntu to 22.04 (#132) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/python_ci_linux.yml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 0a8c0c3..af1b394 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -16,7 +16,7 @@ permissions: jobs: Run: name: "Flake8" - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" steps: - name: Checkout ๐Ÿ›Ž๏ธ diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 10c6f30..bd74787 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - os: ['ubuntu-20.04', 'windows-2019'] + os: ['ubuntu-22.04', 'windows-2019'] fail-fast: false steps: diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 3158d9c..2c2f3de 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -19,8 +19,8 @@ permissions: jobs: tests: - name: "ubuntu-20.04 / Python ${{ matrix.config.python-version }}" - runs-on: "ubuntu-20.04" + name: "ubuntu-22.04 / Python ${{ matrix.config.python-version }}" + runs-on: "ubuntu-22.04" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' @@ -85,7 +85,7 @@ jobs: Coverage: needs: tests - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" steps: - name: Checkout ๐Ÿ›Ž๏ธ uses: "actions/checkout@v4" @@ -134,7 +134,7 @@ jobs: Deploy: needs: tests - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" steps: - name: Checkout ๐Ÿ›Ž๏ธ uses: "actions/checkout@v4" From bb77dffd85efc9e7d876ca05c670414935f460e8 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 10:59:42 +0100 Subject: [PATCH 03/16] [repo-helper] Configuration Update (#133) * Updated files with 'repo_helper'. * Updated files with 'repo_helper'. --------- Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/python_ci_macos.yml | 26 +++++++++++++------------- .readthedocs.yml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index f174699..f992249 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -18,8 +18,8 @@ permissions: jobs: tests: - name: "macos-13 / Python ${{ matrix.config.python-version }}" - runs-on: "macos-13" + name: "macos-${{ matrix.config.os-ver }} / Python ${{ matrix.config.python-version }}" + runs-on: "macos-${{ matrix.config.os-ver }}" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' @@ -28,17 +28,17 @@ jobs: fail-fast: False matrix: config: - - {python-version: "3.7", testenvs: "py37,build", experimental: False} - - {python-version: "3.8", testenvs: "py38,build", experimental: False} - - {python-version: "3.9", testenvs: "py39,build", experimental: False} - - {python-version: "3.10", testenvs: "py310,build", experimental: False} - - {python-version: "3.11", testenvs: "py311,build", experimental: False} - - {python-version: "3.12", testenvs: "py312,build", experimental: False} - - {python-version: "3.13", testenvs: "py313,build", experimental: False} - - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - - {python-version: "pypy-3.9", testenvs: "pypy39,build", experimental: True} - - {python-version: "pypy-3.10", testenvs: "pypy310,build", experimental: True} + - {python-version: "3.7", os-ver: "13", testenvs: "py37,build", experimental: False} + - {python-version: "3.8", os-ver: "14", testenvs: "py38,build", experimental: False} + - {python-version: "3.9", os-ver: "14", testenvs: "py39,build", experimental: False} + - {python-version: "3.10", os-ver: "14", testenvs: "py310,build", experimental: False} + - {python-version: "3.11", os-ver: "14", testenvs: "py311,build", experimental: False} + - {python-version: "3.12", os-ver: "14", testenvs: "py312,build", experimental: False} + - {python-version: "3.13", os-ver: "14", testenvs: "py313,build", experimental: False} + - {python-version: "pypy-3.7", os-ver: "13", testenvs: "pypy37,build", experimental: False} + - {python-version: "pypy-3.8", os-ver: "14", testenvs: "pypy38,build", experimental: False} + - {python-version: "pypy-3.9", os-ver: "14", testenvs: "pypy39,build", experimental: True} + - {python-version: "pypy-3.10", os-ver: "14", testenvs: "pypy310,build", experimental: True} steps: - name: Checkout ๐Ÿ›Ž๏ธ diff --git a/.readthedocs.yml b/.readthedocs.yml index 35b5c85..e73aa4a 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,7 +13,7 @@ python: - requirements: requirements.txt - requirements: doc-source/requirements.txt build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: python: '3.9' jobs: From 6a2e03a222eddd0be374deb9103131dbb3640475 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Tue, 6 May 2025 10:59:27 +0100 Subject: [PATCH 04/16] Bump alt-linux workflow to Ubuntu 22.04 --- .github/workflows/python_ci_alt_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python_ci_alt_linux.yml b/.github/workflows/python_ci_alt_linux.yml index 6aa867f..8401ea3 100644 --- a/.github/workflows/python_ci_alt_linux.yml +++ b/.github/workflows/python_ci_alt_linux.yml @@ -20,7 +20,7 @@ permissions: jobs: tests: name: "alt-linux / Python ${{ matrix.config.python-version }}" - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" container: image: ghcr.io/domdfcoding/alt-linux-python:latest continue-on-error: ${{ matrix.config.experimental }} From de027bbf180c33fd42f743dadd2030222f27bd10 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 21:45:12 +0100 Subject: [PATCH 05/16] Updated files with 'repo_helper'. (#134) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/python_ci.yml | 1 + .github/workflows/python_ci_linux.yml | 3 +++ .github/workflows/python_ci_macos.yml | 1 + .pre-commit-config.yaml | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index 83f4854..f6491d9 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -79,3 +79,4 @@ jobs: with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage + include-hidden-files: true diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 2c2f3de..fb7d97a 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -81,6 +81,7 @@ jobs: with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage + include-hidden-files: true Coverage: @@ -123,6 +124,7 @@ jobs: with: name: "combined-coverage" path: .coverage + include-hidden-files: true - name: "Upload Combined Coverage to Coveralls" if: ${{ steps.show.outcome != 'failure' }} @@ -208,6 +210,7 @@ jobs: $CONDA/bin/conda config --set always_yes yes --set changeps1 no $CONDA/bin/conda update -n base conda $CONDA/bin/conda info -a + $CONDA/bin/conda install conda-forge::py-lief=0.14.1 $CONDA/bin/conda config --add channels conda-forge $CONDA/bin/conda config --add channels domdfcoding diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index f992249..d672cec 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -79,3 +79,4 @@ jobs: with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage + include-hidden-files: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b4912f4..bc4c3b7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ ci: repos: - repo: https://github.com/repo-helper/pyproject-parser - rev: v0.11.1 + rev: v0.13.0 hooks: - id: reformat-pyproject From fa89aff45e9604c9b58b37abb91db796ed1966e7 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 16:45:44 +0100 Subject: [PATCH 06/16] Update contributing guide (#135) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- CONTRIBUTING.rst | 4 ++-- doc-source/contributing.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4c3b5a2..35c62f8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -38,11 +38,11 @@ Automated tests ------------------- Tests are run with ``tox`` and ``pytest``. -To run tests for a specific Python version, such as Python 3.6: +To run tests for a specific Python version, such as Python 3.10: .. code-block:: bash - $ tox -e py36 + $ tox -e py310 To run tests for all Python versions, simply run: diff --git a/doc-source/contributing.rst b/doc-source/contributing.rst index f155af0..d3e8a02 100644 --- a/doc-source/contributing.rst +++ b/doc-source/contributing.rst @@ -37,11 +37,11 @@ Automated tests ------------------- Tests are run with ``tox`` and ``pytest``. -To run tests for a specific Python version, such as Python 3.6: +To run tests for a specific Python version, such as Python 3.10: .. prompt:: bash - tox -e py36 + tox -e py310 To run tests for all Python versions, simply run: From 3a2a9af5861d926f1f3b65fdce72094697e26c83 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 8 Dec 2025 11:15:27 +0000 Subject: [PATCH 07/16] Bump mypy --- domdf_python_tools/bases.py | 10 +++++----- domdf_python_tools/pretty_print.py | 4 ++-- domdf_python_tools/utils.py | 2 +- domdf_python_tools/versions.py | 2 +- tests/discover_demo_module/__init__.py | 2 +- tests/mypy_test.py | 4 +--- tests/test_bases.py | 6 ++---- tests/test_delegators.py | 4 ++-- tox.ini | 2 +- 9 files changed, 16 insertions(+), 20 deletions(-) diff --git a/domdf_python_tools/bases.py b/domdf_python_tools/bases.py index d25b380..d5219b9 100644 --- a/domdf_python_tools/bases.py +++ b/domdf_python_tools/bases.py @@ -116,7 +116,7 @@ def __deepcopy__(self, memodict={}): @property @abstractmethod - def __dict__(self): + def __dict__(self): # type: ignore[override] return dict() # pragma: no cover (abc) def __eq__(self, other) -> bool: @@ -422,7 +422,7 @@ def __sub__(self: _F, other: float) -> _F: def __mul__(self: _F, other: float) -> _F: return self.__class__(float(self).__mul__(other)) - def __floordiv__(self: _F, other: float) -> _F: # type: ignore[override] + def __floordiv__(self: _F, other: float) -> _F: return self.__class__(float(self).__floordiv__(other)) def __truediv__(self: _F, other: float) -> _F: @@ -446,7 +446,7 @@ def __rsub__(self: _F, other: float) -> _F: def __rmul__(self: _F, other: float) -> _F: return self.__class__(float(self).__rmul__(other)) - def __rfloordiv__(self: _F, other: float) -> _F: # type: ignore[override] + def __rfloordiv__(self: _F, other: float) -> _F: return self.__class__(float(self).__rfloordiv__(other)) def __rtruediv__(self: _F, other: float) -> _F: @@ -538,8 +538,8 @@ def __float__(self) -> float: def __abs__(self: _F) -> _F: return self.__class__(float(self).__abs__()) - def __hash__(self) -> int: - return float(self).__hash__() + # def __hash__(self) -> int: + # return float(self).__hash__() def __repr__(self) -> str: return str(self) diff --git a/domdf_python_tools/pretty_print.py b/domdf_python_tools/pretty_print.py index d205744..29ab843 100644 --- a/domdf_python_tools/pretty_print.py +++ b/domdf_python_tools/pretty_print.py @@ -39,7 +39,7 @@ # stdlib import sys from io import StringIO -from typing import IO, Any, Callable, Iterator, MutableMapping, Optional, Tuple, Type, TypeVar +from typing import IO, Any, Callable, ClassVar, Iterator, MutableMapping, Optional, Tuple, Type, TypeVar try: # pragma: no cover @@ -106,7 +106,7 @@ def __init__( _dispatch: MutableMapping[Callable, Callable] _indent_per_level: int - _format_items: Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None] + _format_items: ClassVar[Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None]] _dispatch = dict(PrettyPrinter._dispatch) # type: ignore def _make_open(self, char: str, indent: int, obj): diff --git a/domdf_python_tools/utils.py b/domdf_python_tools/utils.py index 8207d80..ae08236 100644 --- a/domdf_python_tools/utils.py +++ b/domdf_python_tools/utils.py @@ -431,7 +431,7 @@ def head(obj: Union[Tuple, List, "DataFrame", "Series", String, HasHead], n: int if len(obj) <= n: return repr(obj) else: - head_of_namedtuple = {k: v for k, v in zip(obj._fields[:n], obj[:n])} # type: ignore + head_of_namedtuple = {k: v for k, v in zip(obj._fields[:n], obj[:n])} repr_fmt = '(' + ", ".join(f"{k}={v!r}" for k, v in head_of_namedtuple.items()) + f", {etc})" return obj.__class__.__name__ + repr_fmt diff --git a/domdf_python_tools/versions.py b/domdf_python_tools/versions.py index 2ebc152..fe5cc7f 100644 --- a/domdf_python_tools/versions.py +++ b/domdf_python_tools/versions.py @@ -90,7 +90,7 @@ def patch(self): # noqa: D102 return self[2] def __new__(cls: Type[_V], major=0, minor=0, patch=0) -> _V: # noqa: D102 - t: _V = super().__new__(cls, (int(major), int(minor), int(patch))) # type: ignore + t: _V = super().__new__(cls, (int(major), int(minor), int(patch))) return t diff --git a/tests/discover_demo_module/__init__.py b/tests/discover_demo_module/__init__.py index abee116..6a52ec9 100644 --- a/tests/discover_demo_module/__init__.py +++ b/tests/discover_demo_module/__init__.py @@ -1,2 +1,2 @@ def foo_in_init() -> str: - pass + return '' diff --git a/tests/mypy_test.py b/tests/mypy_test.py index a72f85b..deea8d9 100644 --- a/tests/mypy_test.py +++ b/tests/mypy_test.py @@ -8,13 +8,11 @@ class MyDictable(Dictable): def __init__(self, foo: str, bar: int): - super().__init__() - self.foo: str = foo self.bar: float = float(bar) @property - def __dict__(self): + def __dict__(self): # type: ignore[override] return dict(foo=self.foo, bar=self.bar) diff --git a/tests/test_bases.py b/tests/test_bases.py index 1125ffb..e0f22d6 100644 --- a/tests/test_bases.py +++ b/tests/test_bases.py @@ -23,14 +23,12 @@ class Person(Dictable): def __init__(self, name, age, occupation=None): - super().__init__() - self.name = str(name) self.age = int(age) self.occupation = occupation @property - def __dict__(self): + def __dict__(self): # type: ignore[override] return dict( name=self.name, age=self.age, @@ -46,7 +44,7 @@ def __init__(self, name, age, school): self.school = "school" @property - def __dict__(self): + def __dict__(self): # type: ignore[override] class_dict = super().__dict__ class_dict["School"] = self.school return class_dict diff --git a/tests/test_delegators.py b/tests/test_delegators.py index 8afbe8d..8282648 100644 --- a/tests/test_delegators.py +++ b/tests/test_delegators.py @@ -7,7 +7,7 @@ def f(a: int = 1, b: float = 1.1, c: int = 2, d: list = [], e: tuple = (), f: str = '', g: bytes = b'') -> int: - pass + return 0 def test_delegate_kwargs(): @@ -84,7 +84,7 @@ class F: @delegates(f) def g(self, *args, **kwargs) -> str: - pass + return '' sig = inspect.signature(F.g) assert list(sig.parameters.keys()) == ["self", 'a', 'b', 'c', 'd', 'e', 'f', 'g'] diff --git a/tox.ini b/tox.ini index f9e840a..ce6eb92 100644 --- a/tox.ini +++ b/tox.ini @@ -148,7 +148,7 @@ ignore_errors = True changedir = {toxinidir} extras = all deps = - mypy==0.971 + mypy==1.17.1 -r{toxinidir}/tests/requirements.txt -r{toxinidir}/stubs.txt pprint36 From 7307876f4950d2c8118969c1e8d993af7ab0ab0c Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 8 Dec 2025 11:58:35 +0000 Subject: [PATCH 08/16] Update type hints --- domdf_python_tools/bases.py | 24 +++++++---- domdf_python_tools/dates.py | 5 ++- domdf_python_tools/delegators.py | 6 +-- domdf_python_tools/iterative.py | 2 +- domdf_python_tools/paths.py | 12 +++--- domdf_python_tools/pretty_print.py | 30 ++++++------- domdf_python_tools/stringlist.py | 2 +- domdf_python_tools/terminal.py | 12 ++---- domdf_python_tools/typing.py | 2 +- domdf_python_tools/utils.py | 8 ++-- domdf_python_tools/words.py | 6 ++- tests/list_tests.py | 2 +- tests/seq_tests.py | 6 +-- tests/test_dates.py | 2 +- tests/test_doctools.py | 23 +++++----- tests/test_getters.py | 68 +++++++++++++++--------------- tests/test_import_tools.py | 2 +- tests/test_namedlist.py | 2 +- tests/test_paths_stdlib.py | 6 +-- tests/test_pretty_print.py | 8 ++-- tests/test_stringlist.py | 8 ++-- tests/test_typing.py | 2 +- tests/test_userlist.py | 6 +-- tests/test_versions.py | 12 +++--- 24 files changed, 129 insertions(+), 127 deletions(-) diff --git a/domdf_python_tools/bases.py b/domdf_python_tools/bases.py index d5219b9..044a182 100644 --- a/domdf_python_tools/bases.py +++ b/domdf_python_tools/bases.py @@ -456,7 +456,7 @@ def __rmod__(self: _F, other: float) -> _F: return self.__class__(float(self).__rmod__(other)) def __rdivmod__(self: _F, other: float) -> Tuple[_F, _F]: - return tuple(self.__class__(x) for x in float(self).__rdivmod__(other)) # type: ignore + return tuple(self.__class__(x) for x in float(self).__rdivmod__(other)) # type: ignore[return-value] def __rpow__(self: _F, other: float, mod=None) -> _F: return self.__class__(float(self).__rpow__(other, mod)) @@ -471,7 +471,13 @@ def __trunc__(self) -> int: return float(self).__trunc__() - def __round__(self, ndigits: Optional[int] = None) -> Union[int, float]: # type: ignore + @overload + def __round__(self, ndigits: None = ...) -> int: ... + + @overload + def __round__(self, ndigits: int) -> float: ... + + def __round__(self, ndigits: Optional[int] = None) -> Union[int, float]: """ Round the :class:`~.UserFloat` to ``ndigits`` decimal places, defaulting to ``0``. @@ -622,7 +628,7 @@ def replace(self: _LU, what: _T, with_: _T) -> _LU: return self - def sort( # type: ignore + def sort( # type: ignore[override] self: _LU, *, key=None, @@ -643,25 +649,25 @@ def sort( # type: ignore super().sort(key=key, reverse=reverse) return self - def reverse(self: _LU) -> _LU: # type: ignore # noqa: D102 + def reverse(self: _LU) -> _LU: # type: ignore[override] # noqa: D102 super().reverse() return self - def append( # type: ignore # noqa: D102 + def append( # type: ignore[override] # noqa: D102 self: _LU, item: _T, ) -> _LU: super().append(item) return self - def extend( # type: ignore # noqa: D102 + def extend( # type: ignore[override] # noqa: D102 self: _LU, other: Iterable[_T], ) -> _LU: super().extend(other) return self - def insert( # type: ignore # noqa: D102 + def insert( # type: ignore[override] # noqa: D102 self: _LU, i: int, item: _T, @@ -669,13 +675,13 @@ def insert( # type: ignore # noqa: D102 super().insert(i, item) return self - def remove( # type: ignore # noqa: D102 + def remove( # type: ignore[override] # noqa: D102 self: _LU, item: _T, ) -> _LU: super().remove(item) return self - def clear(self: _LU) -> _LU: # type: ignore # noqa: D102 + def clear(self: _LU) -> _LU: # type: ignore[override] # noqa: D102 super().clear() return self diff --git a/domdf_python_tools/dates.py b/domdf_python_tools/dates.py index 22dc083..e8253f1 100644 --- a/domdf_python_tools/dates.py +++ b/domdf_python_tools/dates.py @@ -147,7 +147,7 @@ def utc_timestamp_to_datetime( if sys.version_info <= (3, 7, 2): # pragma: no cover (py37+) MonthsType = OrderedDict else: # pragma: no cover ( str: month = int(month) except ValueError: try: - return months[month.capitalize()[:3]] # type: ignore + assert not isinstance(month, int) + return months[month.capitalize()[:3]] except KeyError: raise ValueError(error_text) diff --git a/domdf_python_tools/delegators.py b/domdf_python_tools/delegators.py index 3244bc5..7ce12df 100644 --- a/domdf_python_tools/delegators.py +++ b/domdf_python_tools/delegators.py @@ -82,7 +82,7 @@ def _f(f: _C) -> _C: if param in to_params: del to_params[param] - f.__signature__ = from_sig.replace( # type: ignore + f.__signature__ = from_sig.replace( # type: ignore[attr-defined] parameters=[*from_params.values(), *to_params.values()] ) f.__annotations__ = {**to_annotations, **from_annotations} @@ -116,11 +116,11 @@ def _f(f: _C) -> _C: from_params = dict(from_sig.parameters) if tuple(from_params.keys()) == ("args", "kwargs"): - f.__signature__ = to_sig # type: ignore + f.__signature__ = to_sig # type: ignore[attr-defined] copy_annotations(f) elif tuple(from_params.keys()) == ("self", "args", "kwargs"): - f.__signature__ = from_sig.replace( # type: ignore + f.__signature__ = from_sig.replace( # type: ignore[attr-defined] parameters=[from_params["self"], *to_sig.parameters.values()] ) diff --git a/domdf_python_tools/iterative.py b/domdf_python_tools/iterative.py index b3cf970..5d24cf6 100644 --- a/domdf_python_tools/iterative.py +++ b/domdf_python_tools/iterative.py @@ -501,4 +501,4 @@ def __init_subclass__(cls, **kwargs): count.__qualname__ = count.__name__ = "count" - return count() # type: ignore + return count() # type: ignore[return-value] diff --git a/domdf_python_tools/paths.py b/domdf_python_tools/paths.py index f7c6b7b..f248c0d 100644 --- a/domdf_python_tools/paths.py +++ b/domdf_python_tools/paths.py @@ -388,7 +388,7 @@ class PathPlus(pathlib.Path): __slots__ = () if sys.version_info < (3, 11): - _accessor = pathlib._normal_accessor # type: ignore + _accessor = pathlib._normal_accessor # type: ignore[attr-defined] _closed = False def _init(self, *args, **kwargs): @@ -396,11 +396,11 @@ def _init(self, *args, **kwargs): @classmethod def _from_parts(cls, args, init=True): - return super()._from_parts(args) # type: ignore + return super()._from_parts(args) # type: ignore[misc] def __new__(cls: Type[_PP], *args, **kwargs) -> _PP: # noqa: D102 if cls is PathPlus: - cls = WindowsPathPlus if os.name == "nt" else PosixPathPlus # type: ignore + cls = WindowsPathPlus if os.name == "nt" else PosixPathPlus # type: ignore[assignment] return super().__new__(cls, *args, **kwargs) @@ -630,7 +630,7 @@ def dump_json( data: Any, encoding: Optional[str] = "UTF-8", errors: Optional[str] = None, - json_library: JsonLibrary = json, # type: ignore + json_library: JsonLibrary = json, # type: ignore[assignment] *, compress: bool = False, **kwargs, @@ -674,7 +674,7 @@ def load_json( self, encoding: Optional[str] = "UTF-8", errors: Optional[str] = None, - json_library: JsonLibrary = json, # type: ignore + json_library: JsonLibrary = json, # type: ignore[assignment] *, decompress: bool = False, **kwargs, @@ -1255,7 +1255,7 @@ def phase4(self) -> None: # noqa: D102 "right_list": filecmp.dircmp.phase0 } - methodmap = _methodmap # type: ignore + methodmap = _methodmap # type: ignore[assignment] def compare_dirs(a: PathLike, b: PathLike) -> bool: diff --git a/domdf_python_tools/pretty_print.py b/domdf_python_tools/pretty_print.py index 29ab843..a67bef0 100644 --- a/domdf_python_tools/pretty_print.py +++ b/domdf_python_tools/pretty_print.py @@ -45,14 +45,14 @@ # 3rd party from pprint36 import PrettyPrinter - from pprint36._pprint import _safe_key # type: ignore + from pprint36._pprint import _safe_key # type: ignore[attr-defined] supports_sort_dicts = True except ImportError: # stdlib - from pprint import PrettyPrinter, _safe_key # type: ignore + from pprint import PrettyPrinter, _safe_key # type: ignore[attr-defined] supports_sort_dicts = sys.version_info >= (3, 8) @@ -107,7 +107,7 @@ def __init__( _dispatch: MutableMapping[Callable, Callable] _indent_per_level: int _format_items: ClassVar[Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None]] - _dispatch = dict(PrettyPrinter._dispatch) # type: ignore + _dispatch = dict(PrettyPrinter._dispatch) # type: ignore[attr-defined] def _make_open(self, char: str, indent: int, obj): if self._indent_per_level > 1: @@ -115,13 +115,13 @@ def _make_open(self, char: str, indent: int, obj): else: the_indent = ' ' * (indent + self._indent_per_level) - if obj and not self._compact: # type: ignore + if obj and not self._compact: # type: ignore[attr-defined] return f"{char}\n{the_indent}" else: return char def _make_close(self, char: str, indent: int, obj): - if obj and not self._compact: # type: ignore + if obj and not self._compact: # type: ignore[attr-defined] return f",\n{' ' * (indent + self._indent_per_level)}{char}" else: return char @@ -143,14 +143,14 @@ def _pprint_dict( write((self._indent_per_level - 1) * ' ') if obj: - self._format_dict_items( # type: ignore - obj.items(), - stream, - indent, - allowance + 1, - context, - level, - ) + self._format_dict_items( # type: ignore[attr-defined] + obj.items(), + stream, + indent, + allowance + 1, + context, + level, + ) write(self._make_close('}', indent, obj)) @@ -243,7 +243,7 @@ def _format_attribute_items(self, items, stream, indent, allowance, context, lev last = i == last_index write(key) write('=') - self._format( # type: ignore + self._format( # type: ignore[attr-defined] ent, stream, indent + len(key) + 2, @@ -284,7 +284,7 @@ def __repr__(self) -> str: __repr__.__name__ = "__repr__" __repr__.__module__ = obj.__module__ __repr__.__qualname__ = f"{obj.__module__}.__repr__" - obj.__repr__ = __repr__ # type: ignore + obj.__repr__ = __repr__ # type: ignore[assignment] return obj diff --git a/domdf_python_tools/stringlist.py b/domdf_python_tools/stringlist.py index 4b0c173..920bb2c 100644 --- a/domdf_python_tools/stringlist.py +++ b/domdf_python_tools/stringlist.py @@ -411,7 +411,7 @@ def with_indent(self, indent: Union[String, Indent], size: int = 0): :param size: If ``indent`` is an indent type, the indent size to use within the ``with`` block. """ - original_indent: Tuple[int, str] = tuple(self.indent) # type: ignore + original_indent: Tuple[int, str] = tuple(self.indent) # type: ignore[assignment] try: self.set_indent(indent, size) diff --git a/domdf_python_tools/terminal.py b/domdf_python_tools/terminal.py index a8a09f8..2717938 100644 --- a/domdf_python_tools/terminal.py +++ b/domdf_python_tools/terminal.py @@ -170,26 +170,22 @@ def __init__(self, indent: str = ' ' * 2): if frame is None: # pragma: no cover raise ValueError("Unable to obtain the frame of the caller.") else: - self.parent_frame = inspect.currentframe().f_back # type: ignore # TODO + self.parent_frame = inspect.currentframe().f_back # type: ignore[union-attr] # TODO def __enter__(self): """ Called when entering the context manager. """ - self.locals_on_entry = self.parent_frame.f_locals.copy() # type: ignore + self.locals_on_entry = self.parent_frame.f_locals.copy() # type: ignore[union-attr] def __exit__(self, *args, **kwargs): """ Called when exiting the context manager. """ - new_locals = { - k: v - for k, - v in self.parent_frame.f_locals.items() # type: ignore - if k not in self.locals_on_entry - } + f_locals = self.parent_frame.f_locals # type: ignore[union-attr] + new_locals = {k: v for k, v in f_locals.items() if k not in self.locals_on_entry} print(textwrap.indent(pprint.pformat(new_locals), self.indent)) diff --git a/domdf_python_tools/typing.py b/domdf_python_tools/typing.py index efa533a..a87878e 100644 --- a/domdf_python_tools/typing.py +++ b/domdf_python_tools/typing.py @@ -109,7 +109,7 @@ def check_membership(obj: Any, type_: Union[Type, object]) -> bool: such as a :class:`typing.List`, :py:data:`typing.Union` or :py:class:`typing.Sequence`. """ - return isinstance(obj, type_.__args__) # type: ignore + return isinstance(obj, type_.__args__) # type: ignore[union-attr] class JsonLibrary(Protocol): diff --git a/domdf_python_tools/utils.py b/domdf_python_tools/utils.py index ae08236..54589a5 100644 --- a/domdf_python_tools/utils.py +++ b/domdf_python_tools/utils.py @@ -444,11 +444,11 @@ def head(obj: Union[Tuple, List, "DataFrame", "Series", String, HasHead], n: int elif isinstance(obj, HasHead): return obj.head(n).to_string() - elif len(obj) <= n: # type: ignore + elif len(obj) <= n: # type: ignore[arg-type] return str(obj) else: - return str(obj[:n]) + etc # type: ignore + return str(obj[:n]) + etc # type: ignore[index] def magnitude(x: float) -> int: @@ -553,7 +553,7 @@ def divide(string: str, sep: str) -> Tuple[str, str]: raise ValueError(f"{sep!r} not in {string!r}") parts = string.split(sep, 1) - return tuple(parts) # type: ignore + return tuple(parts) # type: ignore[return-value] def redivide(string: str, pat: Union[str, Pattern]) -> Tuple[str, str]: @@ -577,7 +577,7 @@ def redivide(string: str, pat: Union[str, Pattern]) -> Tuple[str, str]: raise ValueError(f"{pat!r} has no matches in {string!r}") parts = pat.split(string, 1) - return tuple(parts) # type: ignore + return tuple(parts) # type: ignore[return-value] @overload diff --git a/domdf_python_tools/words.py b/domdf_python_tools/words.py index d8bc36c..e905f41 100644 --- a/domdf_python_tools/words.py +++ b/domdf_python_tools/words.py @@ -218,7 +218,7 @@ def __call__(self, text: str) -> str: return ''.join(self[char] for char in text) - def get(self, char: str, default: Optional[str] = None) -> str: # type: ignore + def get(self, char: str, default: Optional[str] = None) -> str: # type: ignore[override] """ Returns the given character in this font. @@ -595,13 +595,15 @@ class Plural(functools.partial): def __init__(self, singular: str, plural: str): pass - def __call__(self, n: int) -> str: # type: ignore + def __call__(self, n: int) -> str: """ Returns either the singular or plural form of the word depending on the value of ``n``. :param n: """ + return '' + # if PYPY: # pragma: no cover (!PyPy) if PYPY and sys.version_info < (3, 9): # pragma: no cover (!PyPy) diff --git a/tests/list_tests.py b/tests/list_tests.py index 2c6f705..49c5d78 100644 --- a/tests/list_tests.py +++ b/tests/list_tests.py @@ -104,7 +104,7 @@ def test_reversed(self): assert list(reversed(self.type2test())) == self.type2test() # Bug 3689: make sure list-reversed-iterator doesn't have __len__ with pytest.raises(TypeError): - len(reversed([1, 2, 3])) # type: ignore + len(reversed([1, 2, 3])) # type: ignore[arg-type] def test_setitem(self): a = self.type2test([0, 1]) diff --git a/tests/seq_tests.py b/tests/seq_tests.py index 0775490..cad4c30 100644 --- a/tests/seq_tests.py +++ b/tests/seq_tests.py @@ -244,7 +244,7 @@ def __getitem__(self, i): assert len(vv) == len(s2) # Create from various iteratables - for s2 in ("123", '', range(1000), ("do", 1.2), range(2000, 2200, 5)): # type: ignore + for s2 in ("123", '', range(1000), ("do", 1.2), range(2000, 2200, 5)): # type: ignore[assignment] for g in (Sequence, IterFunc, IterGen, itermulti, iterfunc): assert self.type2test(g(s2)) == self.type2test(s2) assert self.type2test(IterFuncStop(s2)) == self.type2test() @@ -405,7 +405,7 @@ def test_addmul(self): assert u2 + u2 + u2 == u2 * 3 assert u2 + u2 + u2 == 3 * u2 - class subclass(self.type2test): # type: ignore + class subclass(self.type2test): # type: ignore[name-defined] pass u3 = subclass([0, 1]) @@ -434,7 +434,7 @@ def test_imul(self): def test_getitemoverwriteiter(self): # Verify that __getitem__ overrides are not recognized by __iter__ - class T(self.type2test): # type: ignore + class T(self.type2test): # type: ignore[name-defined] def __getitem__(self, key: Any) -> str: return str(key) + "!!!" diff --git a/tests/test_dates.py b/tests/test_dates.py index df5d776..1921417 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -216,7 +216,7 @@ def test_parse_month(month_idx: int, month: str): def test_parse_month_errors(): for value in ["abc", 0, '0', -1, "-1", 13, "13"]: with pytest.raises(ValueError, match=fr"The given month \({value!r}\) is not recognised."): - dates.parse_month(value) # type: ignore + dates.parse_month(value) # type: ignore[arg-type] @pytest.mark.parametrize("month_idx, month", enumerate(dates.month_full_names)) diff --git a/tests/test_doctools.py b/tests/test_doctools.py index 55358b4..1eb0af1 100644 --- a/tests/test_doctools.py +++ b/tests/test_doctools.py @@ -112,12 +112,12 @@ def __init__(self): "pate, brandy and with a fried egg on top and spam." ) - @doctools.is_documented_by(Cafe.menu) # type: ignore + @doctools.is_documented_by(Cafe.menu) # type: ignore[prop-decorator] @property def menu(self): return super().menu + [self._todays_special] - @doctools.is_documented_by(Cafe.opening_hours) # type: ignore + @doctools.is_documented_by(Cafe.opening_hours) # type: ignore[prop-decorator] @property def opening_hours(self): return f"""Open Monday-Saturday {self._opens_at}am - {self._closes_at}pm @@ -209,15 +209,12 @@ def test_decorators(): assert SpamCafe.opening_hours.__doc__ == Cafe.opening_hours.__doc__ # set_opening_hours and ceil should have extra text at the beginning - assert SpamCafe.set_opening_hours.__doc__.startswith( # type: ignore - "I will not buy this record, it is scratched." - ) + assert (SpamCafe.set_opening_hours.__doc__ or '').startswith("I will not buy this record, it is scratched.") assert (doctools.deindent_string(SpamCafe.set_opening_hours.__doc__ )).endswith(doctools.deindent_string(Cafe.set_opening_hours.__doc__)) # Dedented both strings to be sure of equivalence - assert SpamCafe.ceil.__doc__.startswith( # type: ignore - "I don't know why the cafe has a ceil function, but we'd better document it properly.", - ) + assert (SpamCafe.ceil.__doc__ or '' + ).startswith("I don't know why the cafe has a ceil function, but we'd better document it properly.", ) assert doctools.deindent_string(SpamCafe.ceil.__doc__ ).rstrip().endswith(doctools.deindent_string(math.ceil.__doc__).rstrip()) # Dedented both strings to be sure of equivalence @@ -235,9 +232,9 @@ def test_decorators(): 'a': float, 'b': float, 'c': float, 'd': int, "return": float } - assert partially_documented_function.__doc__.startswith( # type: ignore - "This function works like ``documented_function`` except it returns the result telepathically.", - ) + assert (partially_documented_function.__doc__ or '').startswith( + "This function works like ``documented_function`` except it returns the result telepathically." + ) assert (doctools.deindent_string(partially_documented_function.__doc__ )).endswith(doctools.deindent_string(documented_function.__doc__)) # Dedented both strings to be sure of equivalence @@ -513,12 +510,12 @@ def test_prettify_with_method(): class F(Iterable): pass - assert prettify_docstrings(F).__getitem__.__doc__ != "Return ``self[key]``." # type: ignore + assert prettify_docstrings(F).__getitem__.__doc__ != "Return ``self[key]``." # type: ignore[misc] class G(Dictable): pass - assert prettify_docstrings(G).__getitem__.__doc__ != "Return ``self[key]``." # type: ignore + assert prettify_docstrings(G).__getitem__.__doc__ != "Return ``self[key]``." # type: ignore[misc] def test_prettify_namedtuple(): diff --git a/tests/test_getters.py b/tests/test_getters.py index dd43362..4f4df9e 100644 --- a/tests/test_getters.py +++ b/tests/test_getters.py @@ -12,7 +12,7 @@ # 3rd party import pytest -from funcy.funcs import rpartial # type: ignore +from funcy.funcs import rpartial # type: ignore[import-untyped] # this package import domdf_python_tools @@ -29,10 +29,10 @@ class A: pass a = A() - a.name = "john" # type: ignore + a.name = "john" # type: ignore[attr-defined] b = A() - b.name = "graham" # type: ignore + b.name = "graham" # type: ignore[attr-defined] f = attrgetter(0, "name") assert f([a, b]) == "john" @@ -41,13 +41,13 @@ class A: assert f([a, b]) == "graham" with pytest.raises(TypeError, match=r"__call__\(\) missing 1 required positional argument: 'obj'"): - f() # type: ignore + f() # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) takes 2 positional arguments but 3 were given"): - f(a, "cleese") # type: ignore + f(a, "cleese") # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) got an unexpected keyword argument 'surname'"): - f(a, surname="cleese") # type: ignore + f(a, surname="cleese") # type: ignore[call-arg] f = attrgetter(0, "rank") @@ -78,15 +78,15 @@ def __getattr__(self, name): # recursive gets a = A() - a.name = "john" # type: ignore - a.child = A() # type: ignore - a.child.name = "thomas" # type: ignore + a.name = "john" # type: ignore[attr-defined] + a.child = A() # type: ignore[attr-defined] + a.child.name = "thomas" # type: ignore[attr-defined] f = attrgetter(3, "child.name") assert f([1, 2, 3, a]) == "thomas" with pytest.raises(AttributeError, match="'A' object has no attribute 'child'"): - f([1, 2, 3, a.child]) # type: ignore + f([1, 2, 3, a.child]) # type: ignore[attr-defined] f = attrgetter(1, "child.name") @@ -105,8 +105,8 @@ def __getattr__(self, name): with pytest.raises(AttributeError, match="'A' object has no attribute ''"): f([a]) - a.child.child = A() # type: ignore - a.child.child.name = "johnson" # type: ignore + a.child.child = A() # type: ignore[attr-defined] + a.child.child.name = "johnson" # type: ignore[attr-defined] f = attrgetter(0, "child.child.name") assert f([a]) == "johnson" @@ -118,12 +118,12 @@ class A: pass a = A() - a.x = 'X' # type: ignore - a.y = 'Y' # type: ignore - a.z = 'Z' # type: ignore - a.t = A() # type: ignore - a.t.u = A() # type: ignore - a.t.u.v = 'V' # type: ignore + a.x = 'X' # type: ignore[attr-defined] + a.y = 'Y' # type: ignore[attr-defined] + a.z = 'Z' # type: ignore[attr-defined] + a.t = A() # type: ignore[attr-defined] + a.t.u = A() # type: ignore[attr-defined] + a.t.u.v = 'V' # type: ignore[attr-defined] f = attrgetter(0, 'x') f2 = copy(f, proto) @@ -150,13 +150,13 @@ def test_itemgetter(self): assert f([1, 2, a]) == 'C' with pytest.raises(TypeError, match=r"__call__\(\) missing 1 required positional argument: 'obj'"): - f() # type: ignore + f() # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) takes 2 positional arguments but 3 were given"): - f(a, 3) # type: ignore + f(a, 3) # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) got an unexpected keyword argument 'size'"): - f(a, size=3) # type: ignore + f(a, size=3) # type: ignore[call-arg] f = itemgetter(1, 10) @@ -186,16 +186,16 @@ def __getitem__(self, name): TypeError, match=r"__init__\(\) missing 2 required positional arguments: 'idx' and 'item'", ): - itemgetter() # type: ignore + itemgetter() # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__init__\(\) missing 1 required positional argument: 'item'"): - itemgetter(1) # type: ignore + itemgetter(1) # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__init__\(\) missing 1 required positional argument: 'item'"): - itemgetter("abc") # type: ignore + itemgetter("abc") # type: ignore[arg-type,call-arg] with pytest.raises(TypeError, match="'idx' must be an integer"): - itemgetter("abc", 2) # type: ignore + itemgetter("abc", 2) # type: ignore[arg-type] d = dict(key="val") @@ -250,19 +250,19 @@ def test_methodcaller(self): TypeError, match=r"__init__\(\) missing 2 required positional arguments: '_idx' and '_name'", ): - methodcaller() # type: ignore + methodcaller() # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__init__\(\) missing 1 required positional argument: '_name'"): - methodcaller(12) # type: ignore + methodcaller(12) # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__init__\(\) missing 1 required positional argument: '_name'"): - methodcaller("name") # type: ignore + methodcaller("name") # type: ignore[arg-type,call-arg] with pytest.raises(TypeError, match="'_idx' must be an integer"): - methodcaller("name", 12) # type: ignore + methodcaller("name", 12) # type: ignore[arg-type] with pytest.raises(TypeError, match="method name must be a string"): - methodcaller(0, 12) # type: ignore + methodcaller(0, 12) # type: ignore[arg-type] f = methodcaller(1, "foo") @@ -293,19 +293,19 @@ def baz(*args, **kwds): # noqa: MAN002 assert f([1, a]) == 3 with pytest.raises(TypeError, match=r"__call__\(\) missing 1 required positional argument: 'obj'"): - f() # type: ignore + f() # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) takes 2 positional arguments but 3 were given"): - f(a, 3) # type: ignore + f(a, 3) # type: ignore[call-arg] with pytest.raises(TypeError, match=r"__call__\(\) got an unexpected keyword argument 'spam'"): - f(a, spam=3) # type: ignore + f(a, spam=3) # type: ignore[call-arg] f = methodcaller(0, "bar") assert f([a]) == 42 with pytest.raises(TypeError, match=r"__call__\(\) takes 2 positional arguments but 3 were given"): - f([a], [a]) # type: ignore + f([a], [a]) # type: ignore[call-arg] f = methodcaller(0, "bar", f=5) assert f([a]) == 5 diff --git a/tests/test_import_tools.py b/tests/test_import_tools.py index 71fcc98..998aa48 100644 --- a/tests/test_import_tools.py +++ b/tests/test_import_tools.py @@ -22,7 +22,7 @@ sys.path.append("tests") # 3rd party -import discover_demo_module # type: ignore # noqa: E402 +import discover_demo_module # type: ignore[import-not-found] # noqa: E402 def test_discover(): diff --git a/tests/test_namedlist.py b/tests/test_namedlist.py index 81bdac2..edb398c 100644 --- a/tests/test_namedlist.py +++ b/tests/test_namedlist.py @@ -121,7 +121,7 @@ class TestNamedListFunction(NamedListTest): class TestNamedlistSubclassFunction: - class ShoppingList(namedlist()): # type: ignore + class ShoppingList(namedlist()): # type: ignore[misc] pass shopping_list = ShoppingList(["egg and bacon", "egg sausage and bacon", "egg and spam", "egg bacon and spam"]) diff --git a/tests/test_paths_stdlib.py b/tests/test_paths_stdlib.py index 82d0a85..9d0c1f0 100644 --- a/tests/test_paths_stdlib.py +++ b/tests/test_paths_stdlib.py @@ -30,14 +30,14 @@ import grp import pwd except ImportError: - grp = pwd = None # type: ignore + grp = pwd = None # type: ignore[assignment] if sys.version_info[:2] >= (3, 10): # stdlib from test.support.os_helper import TESTFN, can_symlink else: # stdlib - from test.support import TESTFN, can_symlink # type: ignore + from test.support import TESTFN, can_symlink # type: ignore[import-not-found] @pytest.fixture() @@ -159,7 +159,7 @@ def test_read_write_text(BASE: PathPlus): assert ((p / "fileA").read_text(encoding="utf-8", errors="ignore") == "bcdefg") # Check that trying to write bytes does not truncate the file. with pytest.raises(TypeError): - (p / "fileA").write_text(b"somebytes") # type: ignore + (p / "fileA").write_text(b"somebytes") # type: ignore[arg-type] assert ((p / "fileA").read_text(encoding="latin-1") == "รคbcdefg") diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 530cfcb..11813ce 100644 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -608,7 +608,7 @@ def test_sort_unorderable_values(self): assert FancyPrinter().pformat({Unorderable: 0, 1: 0}) == "{1: 0, " + repr(Unorderable) + ": 0}" # Issue 14998: TypeError on tuples with NoneTypes as dict keys. - keys = [(1, ), (None, )] # type: ignore + keys = [(1, ), (None, )] # type: ignore[list-item] assert FancyPrinter().pformat(dict.fromkeys(keys, 0)) == "{%r: 0, %r: 0}" % tuple(sorted(keys, key=id)) def test_sort_orderable_and_unorderable_values(self): @@ -618,8 +618,8 @@ def test_sort_orderable_and_unorderable_values(self): # self-test assert a < b assert str(type(b)) < str(type(a)) - assert sorted([b, a]) == [a, b] # type: ignore - assert sorted([a, b]) == [a, b] # type: ignore + assert sorted([b, a]) == [a, b] # type: ignore[type-var] + assert sorted([a, b]) == [a, b] # type: ignore[type-var] # set assert FancyPrinter(width=1).pformat({b, a}) == f"{{\n {a!r},\n {b!r},\n }}" assert FancyPrinter(width=1).pformat({a, b}) == f"{{\n {a!r},\n {b!r},\n }}" @@ -738,7 +738,7 @@ def test_compact_width(self): number = 10 o = [0] * number for i in range(levels - 1): - o = [o] # type: ignore + o = [o] # type: ignore[list-item] for w in range(levels * 2 + 1, levels + 3 * number - 1): lines = FancyPrinter(width=w, compact=True).pformat(o, ).splitlines() maxwidth = max(map(len, lines)) diff --git a/tests/test_stringlist.py b/tests/test_stringlist.py index 42e95b3..f129c24 100644 --- a/tests/test_stringlist.py +++ b/tests/test_stringlist.py @@ -24,7 +24,7 @@ def test_creation(self): assert StringList("1\n") == ['1', ''] with pytest.raises(TypeError, match="'int' object is not iterable"): - StringList(1) # type: ignore + StringList(1) # type: ignore[arg-type] def test_append(self): sl = StringList() @@ -458,10 +458,10 @@ def test_size(self): indent.size = 1 assert indent.size == 1 - indent.size = '2' # type: ignore + indent.size = '2' # type: ignore[assignment] assert indent.size == 2 - indent.size = 3.0 # type: ignore + indent.size = 3.0 # type: ignore[assignment] assert indent.size == 3 def test_type(self): @@ -473,7 +473,7 @@ def test_type(self): indent.type = ' ' assert indent.type == ' ' - indent.type = 1 # type: ignore + indent.type = 1 # type: ignore[assignment] assert indent.type == '1' indent.type = ">>> " diff --git a/tests/test_typing.py b/tests/test_typing.py index 8809883..2c2d089 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -76,7 +76,7 @@ class MyStr(str): __slots__ = () -class MyPath(type(pathlib.Path())): # type: ignore +class MyPath(type(pathlib.Path())): # type: ignore[misc] pass diff --git a/tests/test_userlist.py b/tests/test_userlist.py index 77bf9ea..337ba0f 100644 --- a/tests/test_userlist.py +++ b/tests/test_userlist.py @@ -25,7 +25,7 @@ class TestList(list_tests.CommonTest): def test_getslice(self): super().test_getslice() l = [0, 1, 2, 3, 4] - u = self.type2test(l) # type: ignore + u = self.type2test(l) # type: ignore[call-arg] for i in range(-3, 6): assert u[:i] == l[:i] assert u[i:] == l[i:] @@ -34,7 +34,7 @@ def test_getslice(self): def test_slice_type(self): l = [0, 1, 2, 3, 4] - u = self.type2test(l) # type: ignore + u = self.type2test(l) # type: ignore[call-arg] assert u[:] != u.__class__ assert u[:] == u @@ -42,7 +42,7 @@ def test_slice_type(self): def test_iadd(self): super().test_iadd() u = [0, 1] - u += self.type2test([0, 1]) # type: ignore + u += self.type2test([0, 1]) # type: ignore[call-arg] assert u == [0, 1, 0, 1] @no_type_check diff --git a/tests/test_versions.py b/tests/test_versions.py index deaa405..d0b0b4c 100644 --- a/tests/test_versions.py +++ b/tests/test_versions.py @@ -178,17 +178,17 @@ def test_too_many_values(): with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 5 were given"): Version.from_str("1.2.3.4") with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 5 were given"): - Version(1, 2, 3, 4) # type: ignore + Version(1, 2, 3, 4) # type: ignore[call-arg] with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 5 were given"): - Version('1', '2', '3', '4') # type: ignore + Version('1', '2', '3', '4') # type: ignore[call-arg] # with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 8 were given"): - # Version.from_tuple(("1", "5", "1", "2", "3", "4", "5")) # type: ignore + # Version.from_tuple(("1", "5", "1", "2", "3", "4", "5")) # type: ignore[call-arg] # with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 8 were given"): - # Version.from_tuple(["1", "5", "1", "2", "3", "4", "5"]) # type: ignore + # Version.from_tuple(["1", "5", "1", "2", "3", "4", "5"]) # type: ignore[call-arg] # with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 8 were given"): - # Version.from_tuple((1, 5, 1, 2, 3, 4, 5)) # type: ignore + # Version.from_tuple((1, 5, 1, 2, 3, 4, 5)) # type: ignore[call-arg] # with pytest.raises(TypeError, match=".* takes from 1 to 4 positional arguments but 8 were given"): - # Version.from_tuple([1, 5, 1, 2, 3, 4, 5]) # type: ignore + # Version.from_tuple([1, 5, 1, 2, 3, 4, 5]) # type: ignore[call-arg] @pytest.mark.parametrize( From c72931d7e84631a5f13e319440cc8d968ca8cf99 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 8 Dec 2025 12:01:21 +0000 Subject: [PATCH 09/16] Make UserFloat hashable again --- domdf_python_tools/bases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domdf_python_tools/bases.py b/domdf_python_tools/bases.py index 044a182..4aa4d72 100644 --- a/domdf_python_tools/bases.py +++ b/domdf_python_tools/bases.py @@ -544,8 +544,8 @@ def __float__(self) -> float: def __abs__(self: _F) -> _F: return self.__class__(float(self).__abs__()) - # def __hash__(self) -> int: - # return float(self).__hash__() + def __hash__(self) -> int: # type: ignore[override] + return float(self).__hash__() def __repr__(self) -> str: return str(self) From 09f0aee2d41f2aea815319f97a8c26c14dcd2830 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 8 Dec 2025 12:08:32 +0000 Subject: [PATCH 10/16] Bump deploy/linter Python version. --- .github/workflows/docs_test_action.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- repo_helper.yml | 2 -- tox.ini | 14 +++++++------- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docs_test_action.yml b/.github/workflows/docs_test_action.yml index 8f60ba5..761b41f 100644 --- a/.github/workflows/docs_test_action.yml +++ b/.github/workflows/docs_test_action.yml @@ -29,7 +29,7 @@ jobs: - '!tests/**' - name: Install and Build ๐Ÿ”ง - uses: sphinx-toolbox/sphinx-action@sphinx-3.3.1 + uses: sphinx-toolbox/sphinx-action@sphinx-3.3.1-py39 if: steps.changes.outputs.code == 'true' with: pre-build-command: python -m pip install tox diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index af1b394..5e67c5c 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -35,7 +35,7 @@ jobs: if: steps.changes.outputs.code == 'true' uses: "actions/setup-python@v5" with: - python-version: "3.8" + python-version: "3.9" - name: Install dependencies ๐Ÿ”ง if: steps.changes.outputs.code == 'true' diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index bd74787..ea0b4e5 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -40,7 +40,7 @@ jobs: if: steps.changes.outputs.code == 'true' uses: "actions/setup-python@v5" with: - python-version: "3.8" + python-version: "3.9" - name: Install dependencies ๐Ÿ”ง run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc4c3b7..f7cd6d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: hooks: - id: pyupgrade args: - - --py36-plus + - --py37-plus - --keep-runtime-typing - repo: https://github.com/Lucas-C/pre-commit-hooks diff --git a/pyproject.toml b/pyproject.toml index 35144b6..c59701f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,7 +149,7 @@ license-key = "MIT" additional-files = [ "include domdf_python_tools/google-10000-english-no-swears.txt",] [tool.mypy] -python_version = "3.8" +python_version = "3.9" namespace_packages = true check_untyped_defs = true warn_unused_ignores = true diff --git a/repo_helper.yml b/repo_helper.yml index e2f87c8..61f9a3f 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -8,8 +8,6 @@ username: "domdfcoding" license: 'MIT' short_desc: 'Helpful functions for Pythonโ€‚๐Ÿโ€‚๐Ÿ› ๏ธ' -python_deploy_version: 3.8 -requires_python: 3.6 min_coverage: 95 tox_testenv_extras: all pre_commit_exclude: "^domdf_python_tools/compat/importlib_resources.py$" diff --git a/tox.ini b/tox.ini index ce6eb92..147ddf7 100644 --- a/tox.ini +++ b/tox.ini @@ -57,7 +57,7 @@ test = pypy39 pypy310 qa = mypy, lint -cov = py38, coverage +cov = py39, coverage [testenv:.package] setenv = @@ -80,7 +80,7 @@ setenv = [testenv:docs] setenv = SHOW_TODOS = 1 passenv = SPHINX_BUILDER -basepython = python3.8 +basepython = python3.9 changedir = {toxinidir}/doc-source extras = all deps = -r{toxinidir}/doc-source/requirements.txt @@ -105,7 +105,7 @@ commands = check-wheel-contents dist/ [testenv:lint] -basepython = python3.8 +basepython = python3.9 changedir = {toxinidir} ignore_errors = True skip_install = False @@ -135,7 +135,7 @@ deps = commands = python3 -m flake8_rst_docstrings_sphinx domdf_python_tools tests --allow-toolbox {posargs} [testenv:perflint] -basepython = python3.8 +basepython = python3.9 changedir = {toxinidir} ignore_errors = True skip_install = True @@ -143,7 +143,7 @@ deps = perflint commands = python3 -m perflint domdf_python_tools {posargs} [testenv:mypy] -basepython = python3.8 +basepython = python3.9 ignore_errors = True changedir = {toxinidir} extras = all @@ -155,7 +155,7 @@ deps = commands = mypy domdf_python_tools tests {posargs} [testenv:pyup] -basepython = python3.8 +basepython = python3.9 skip_install = True ignore_errors = True changedir = {toxinidir} @@ -164,7 +164,7 @@ extras = all commands = pyup_dirs domdf_python_tools tests --py36-plus --recursive [testenv:coverage] -basepython = python3.8 +basepython = python3.9 skip_install = True ignore_errors = True whitelist_externals = /bin/bash From 8dca041e475eec90e5e629be6843cae71eeff378 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 8 Dec 2025 12:27:36 +0000 Subject: [PATCH 11/16] Lint --- domdf_python_tools/words.py | 2 +- tests/test_paths_stdlib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/domdf_python_tools/words.py b/domdf_python_tools/words.py index e905f41..40967f2 100644 --- a/domdf_python_tools/words.py +++ b/domdf_python_tools/words.py @@ -681,7 +681,7 @@ class PluralPhrase(NamedTuple): template: str words: Tuple[Plural, ...] - def __call__(self, n: int) -> str: # noqa: TYP004 # TODO + def __call__(self, n: int) -> str: """ Construct the phrase based on the value of ``n``. diff --git a/tests/test_paths_stdlib.py b/tests/test_paths_stdlib.py index 9d0c1f0..d464959 100644 --- a/tests/test_paths_stdlib.py +++ b/tests/test_paths_stdlib.py @@ -37,7 +37,7 @@ from test.support.os_helper import TESTFN, can_symlink else: # stdlib - from test.support import TESTFN, can_symlink # type: ignore[import-not-found] + from test.support import TESTFN, can_symlink # type: ignore @pytest.fixture() From 1dc8d5903eec40185dad36fec887adf4b3ececc4 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 12:43:56 +0000 Subject: [PATCH 12/16] Updated files with 'repo_helper'. (#140) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/mypy.yml | 2 +- .github/workflows/octocheese.yml | 3 +++ .github/workflows/python_ci.yml | 8 ++++---- .github/workflows/python_ci_linux.yml | 2 +- .pre-commit-config.yaml | 2 +- doc-source/requirements.txt | 2 ++ pyproject.toml | 2 +- tox.ini | 6 +++--- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index ea0b4e5..4c22a52 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - os: ['ubuntu-22.04', 'windows-2019'] + os: ['ubuntu-22.04', 'windows-2022'] fail-fast: false steps: diff --git a/.github/workflows/octocheese.yml b/.github/workflows/octocheese.yml index fd77cd0..a5b3f19 100644 --- a/.github/workflows/octocheese.yml +++ b/.github/workflows/octocheese.yml @@ -6,6 +6,9 @@ on: schedule: - cron: 0 12 * * * +permissions: + contents: write + jobs: Run: runs-on: ubuntu-latest diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index f6491d9..03f5a31 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -18,8 +18,8 @@ permissions: jobs: tests: - name: "windows-2019 / Python ${{ matrix.config.python-version }}" - runs-on: "windows-2019" + name: "windows-2022 / Python ${{ matrix.config.python-version }}" + runs-on: "windows-2022" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' @@ -36,8 +36,8 @@ jobs: - {python-version: "3.12", testenvs: "py312,build", experimental: False} - {python-version: "3.13", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - - {python-version: "pypy-3.9-v7.3.15", testenvs: "pypy39,build", experimental: True} + - {python-version: "pypy-3.8", testenvs: "pypy38", experimental: False} + - {python-version: "pypy-3.9-v7.3.15", testenvs: "pypy39", experimental: True} - {python-version: "pypy-3.10-v7.3.15", testenvs: "pypy310,build", experimental: True} steps: diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index fb7d97a..05f968a 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -162,7 +162,7 @@ jobs: - name: Upload distribution to PyPI ๐Ÿš€ if: startsWith(github.ref, 'refs/tags/') - uses: pypa/gh-action-pypi-publish@v1.4.2 + uses: pypa/gh-action-pypi-publish@v1.13.0 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7cd6d6..00f956a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/asottile/pyupgrade - rev: v2.12.0 + rev: v3.3.0 hooks: - id: pyupgrade args: diff --git a/doc-source/requirements.txt b/doc-source/requirements.txt index 254a696..faad183 100644 --- a/doc-source/requirements.txt +++ b/doc-source/requirements.txt @@ -1,4 +1,5 @@ default-values>=0.6.0 +docutils<0.22 domdf-sphinx-theme>=0.3.0 extras-require>=0.5.0 html-section>=0.3.0 @@ -6,6 +7,7 @@ pandas>=1.1.2 pytest>=6.2.0 pytest-regressions>=2.0.2 pytz>=2019.1 +roman>=4.0 seed-intersphinx-mapping>=1.2.2 sphinx>=3.0.3 sphinx-autofixture>=0.2.1 diff --git a/pyproject.toml b/pyproject.toml index c59701f..163c55c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "domdf_python_tools" version = "3.10.0" description = "Helpful functions for Pythonโ€‚๐Ÿโ€‚๐Ÿ› ๏ธ" readme = "README.rst" -requires-python = ">=3.6" +requires-python = ">=3.7" keywords = [ "utilities",] classifiers = [ "Development Status :: 5 - Production/Stable", diff --git a/tox.ini b/tox.ini index 147ddf7..24cfcdc 100644 --- a/tox.ini +++ b/tox.ini @@ -69,7 +69,6 @@ download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 - UNSAFE_PYO3_SKIP_VERSION_CHECK=1 [testenv:py312] download = True @@ -125,11 +124,12 @@ deps = flake8-sphinx-links>=0.0.4 flake8-strftime>=0.1.1 flake8-typing-imports>=1.10.0 + git+https://github.com/domdfcoding/restructuredtext-lint.git@fix-deprecations git+https://github.com/domdfcoding/flake8-rst-docstrings-sphinx.git git+https://github.com/domdfcoding/flake8-rst-docstrings.git git+https://github.com/python-formate/flake8-unused-arguments.git@magic-methods git+https://github.com/python-formate/flake8-missing-annotations.git - pydocstyle>=6.0.0 + git+https://github.com/domdfcoding/pydocstyle.git@stub-functions pygments>=2.7.1 importlib_metadata<4.5.0; python_version<'3.8' commands = python3 -m flake8_rst_docstrings_sphinx domdf_python_tools tests --allow-toolbox {posargs} @@ -212,7 +212,7 @@ inline-quotes = " multiline-quotes = """ docstring-quotes = """ count = True -min_python_version = 3.6 +min_python_version = 3.7 unused-arguments-ignore-abstract-functions = True unused-arguments-ignore-overload-functions = True unused-arguments-ignore-magic-methods = True From eea897740a9c6d943a595dd18e87bafb70cbd545 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 22:25:03 +0000 Subject: [PATCH 13/16] Updated files with 'repo_helper'. (#141) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/python_ci_macos.yml | 4 ++-- .pre-commit-config.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index d672cec..2afbc2b 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -28,14 +28,14 @@ jobs: fail-fast: False matrix: config: - - {python-version: "3.7", os-ver: "13", testenvs: "py37,build", experimental: False} + - {python-version: "3.7", os-ver: "15-intel", testenvs: "py37,build", experimental: False} - {python-version: "3.8", os-ver: "14", testenvs: "py38,build", experimental: False} - {python-version: "3.9", os-ver: "14", testenvs: "py39,build", experimental: False} - {python-version: "3.10", os-ver: "14", testenvs: "py310,build", experimental: False} - {python-version: "3.11", os-ver: "14", testenvs: "py311,build", experimental: False} - {python-version: "3.12", os-ver: "14", testenvs: "py312,build", experimental: False} - {python-version: "3.13", os-ver: "14", testenvs: "py313,build", experimental: False} - - {python-version: "pypy-3.7", os-ver: "13", testenvs: "pypy37,build", experimental: False} + - {python-version: "pypy-3.7", os-ver: "15-intel", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", os-ver: "14", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9", os-ver: "14", testenvs: "pypy39,build", experimental: True} - {python-version: "pypy-3.10", os-ver: "14", testenvs: "pypy310,build", experimental: True} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 00f956a..973fdef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: - id: bind-requirements - repo: https://github.com/python-formate/flake8-dunder-all - rev: v0.4.1 + rev: v0.5.0 hooks: - id: ensure-dunder-all files: ^domdf_python_tools/.*\.py$ From 5d128336cf68a9c29fe1e70a479e065b3effcb09 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Thu, 11 Dec 2025 17:28:46 +0000 Subject: [PATCH 14/16] Have mypy ignore test_is_socket_true on Windows. --- tests/test_paths_stdlib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_paths_stdlib.py b/tests/test_paths_stdlib.py index d464959..f157140 100644 --- a/tests/test_paths_stdlib.py +++ b/tests/test_paths_stdlib.py @@ -129,6 +129,9 @@ def test_stat(BASE: PathPlus): @pytest.mark.skipif(not hasattr(socket, "AF_UNIX"), reason="Unix sockets required") def test_is_socket_true(BASE: PathPlus): + if sys.platform == "win32": + return + P = PathPlus(BASE, "mysock") sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: From c21901393a32dcc638354a6335354e7c00c7d4e6 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:36:21 +0000 Subject: [PATCH 15/16] Updated files with 'repo_helper'. (#142) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/python_ci_linux.yml | 2 +- .github/workflows/python_ci_macos.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 05f968a..7b5f20c 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -38,7 +38,7 @@ jobs: - {python-version: "3.13", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - - {python-version: "pypy-3.9", testenvs: "pypy39,build", experimental: True} + - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} - {python-version: "pypy-3.10", testenvs: "pypy310,build", experimental: True} steps: diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index 2afbc2b..aa36a80 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -37,7 +37,7 @@ jobs: - {python-version: "3.13", os-ver: "14", testenvs: "py313,build", experimental: False} - {python-version: "pypy-3.7", os-ver: "15-intel", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", os-ver: "14", testenvs: "pypy38,build", experimental: False} - - {python-version: "pypy-3.9", os-ver: "14", testenvs: "pypy39,build", experimental: True} + - {python-version: "pypy-3.9", os-ver: "14", testenvs: "pypy39", experimental: True} - {python-version: "pypy-3.10", os-ver: "14", testenvs: "pypy310,build", experimental: True} steps: From da61843be731c917c16e287c58c3c87cd323df3c Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Thu, 11 Dec 2025 14:35:11 -0600 Subject: [PATCH 16/16] Fix Python 3.14 test failures (#137) * tests: fix pathlib.PurePosixPath repr on py3.14 ``` fish for i in python3.{9,10,11,12,13,14} echo -n "$i " $i -c 'import pathlib; print(repr(pathlib.PurePosixPath))' end ``` ``` python3.9 python3.10 python3.11 python3.12 python3.13 python3.14 ``` * words: fix alphabet_sort exception handling for py3.14 As of https://github.com/python/cpython/pull/121395, the format of the list.index exception has changed and parsing it like this no longer works. Relying on implementation details like this is always risky, so this solution should be more future-proof. * Skip pathlib.PurePath for from_uri test in Python 3.13 * Configure testing on Python 3.14 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Dominic Davis-Foster Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/python_ci.yml | 3 ++- .github/workflows/python_ci_linux.yml | 3 ++- .github/workflows/python_ci_macos.yml | 3 ++- domdf_python_tools/words.py | 15 +++++++-------- pyproject.toml | 3 ++- repo_helper.yml | 1 + tests/test_paths.py | 10 ++++++++-- tests/test_utils.py | 2 +- tox.ini | 2 ++ 9 files changed, 27 insertions(+), 15 deletions(-) diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index 03f5a31..a1e0828 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -22,7 +22,7 @@ jobs: runs-on: "windows-2022" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,3.14,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False @@ -35,6 +35,7 @@ jobs: - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12", testenvs: "py312,build", experimental: False} - {python-version: "3.13", testenvs: "py313,build", experimental: False} + - {python-version: "3.14", testenvs: "py314,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38", experimental: False} - {python-version: "pypy-3.9-v7.3.15", testenvs: "pypy39", experimental: True} diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 7b5f20c..4cc812e 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -23,7 +23,7 @@ jobs: runs-on: "ubuntu-22.04" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,3.14,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False @@ -36,6 +36,7 @@ jobs: - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12", testenvs: "py312,build", experimental: False} - {python-version: "3.13", testenvs: "py313,build", experimental: False} + - {python-version: "3.14", testenvs: "py314,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index aa36a80..d52b858 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -22,7 +22,7 @@ jobs: runs-on: "macos-${{ matrix.config.os-ver }}" continue-on-error: ${{ matrix.config.experimental }} env: - USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' + USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,3.14,pypy-3.7,pypy-3.8,pypy-3.9,pypy-3.10' strategy: fail-fast: False @@ -35,6 +35,7 @@ jobs: - {python-version: "3.11", os-ver: "14", testenvs: "py311,build", experimental: False} - {python-version: "3.12", os-ver: "14", testenvs: "py312,build", experimental: False} - {python-version: "3.13", os-ver: "14", testenvs: "py313,build", experimental: False} + - {python-version: "3.14", os-ver: "14", testenvs: "py314,build", experimental: False} - {python-version: "pypy-3.7", os-ver: "15-intel", testenvs: "pypy37,build", experimental: False} - {python-version: "pypy-3.8", os-ver: "14", testenvs: "pypy38,build", experimental: False} - {python-version: "pypy-3.9", os-ver: "14", testenvs: "pypy39", experimental: True} diff --git a/domdf_python_tools/words.py b/domdf_python_tools/words.py index 40967f2..ad2e5fe 100644 --- a/domdf_python_tools/words.py +++ b/domdf_python_tools/words.py @@ -171,14 +171,13 @@ def alpha_sort( alphabet_ = list(alphabet) - try: - return sorted(iterable, key=lambda attr: [alphabet_.index(letter) for letter in attr], reverse=reverse) - except ValueError as e: - m = re.match(r"'(.*)' is not in list", str(e)) - if m: - raise ValueError(f"The character {m.group(1)!r} was not found in the alphabet.") from None - else: # pragma: no cover - raise e + def _alphabet_index(letter: str) -> int: + try: + return alphabet_.index(letter) + except ValueError: + raise ValueError(f"The character {letter!r} was not found in the alphabet.") from None + + return sorted(iterable, key=lambda attr: [_alphabet_index(letter) for letter in attr], reverse=reverse) class Font(Dict[str, str]): diff --git a/pyproject.toml b/pyproject.toml index 163c55c..b1bdb9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", @@ -142,7 +143,7 @@ base-classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed", ] -python-versions = [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13",] +python-versions = [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" diff --git a/repo_helper.yml b/repo_helper.yml index 61f9a3f..dafed9a 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -25,6 +25,7 @@ python_versions: "3.11": "3.12": "3.13": + "3.14": pypy37: pypy38: pypy39: diff --git a/tests/test_paths.py b/tests/test_paths.py index 22e411b..7567941 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -20,7 +20,7 @@ # 3rd party import pytest from coincidence.regressions import AdvancedDataRegressionFixture -from coincidence.selectors import not_pypy, not_windows, only_windows +from coincidence.selectors import max_version, not_pypy, not_windows, only_windows # this package from domdf_python_tools import paths @@ -925,7 +925,13 @@ def test_sort_paths(): @pytest.mark.parametrize("path", _from_uri_paths) -@pytest.mark.parametrize("left_type", [pathlib.PurePath, pathlib.Path, PathPlus]) +@pytest.mark.parametrize( + "left_type", [ + pytest.param(pathlib.PurePath, marks=max_version((3, 13))), + pathlib.Path, + PathPlus, + ] + ) def test_pathplus_from_uri(path: str, left_type: Type): assert PathPlus.from_uri(left_type(path).as_uri()).as_posix() == path diff --git a/tests/test_utils.py b/tests/test_utils.py index 768bd83..08809e2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -124,7 +124,7 @@ def test_printr(obj, expects, capsys): assert re.match(expects, stdout[0]) -if sys.version_info >= (3, 13): +if sys.version_info[:2] == (3, 13): pure_posix_path_expected = "" else: pure_posix_path_expected = "" diff --git a/tox.ini b/tox.ini index 24cfcdc..3422d70 100644 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,7 @@ envlist = py311 py312 py313 + py314 pypy37 pypy38 pypy39 @@ -52,6 +53,7 @@ test = py311 py312 py313 + py314 pypy37 pypy38 pypy39