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/33] 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/33] 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/33] [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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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 From de089e8bcbc70d0ca4287e0af5712ccecb4de06b Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Tue, 6 Jan 2026 20:12:42 +0000 Subject: [PATCH 17/33] Emit warning if a string is passed to PathPlus.write_lines --- domdf_python_tools/paths.py | 7 +++++++ tests/test_paths.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/domdf_python_tools/paths.py b/domdf_python_tools/paths.py index f248c0d..aa972c6 100644 --- a/domdf_python_tools/paths.py +++ b/domdf_python_tools/paths.py @@ -56,6 +56,7 @@ import sys import tempfile import urllib.parse +import warnings from collections import defaultdict, deque from operator import methodcaller from typing import ( @@ -532,6 +533,12 @@ def write_lines( .. versionchanged:: 2.4.0 Added the ``trailing_whitespace`` option. """ + if isinstance(data, str): + warnings.warn( + "Passing a string to PathPlus.write_lines writes each character to its own line.\n" + "That probably isn't what you intended." + ) + if trailing_whitespace: data = list(data) if data[-1].strip(): diff --git a/tests/test_paths.py b/tests/test_paths.py index 7567941..c01696e 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -521,6 +521,11 @@ def test_write_lines(tmp_pathplus): content = tmp_file.read_text() assert content == "this\nis\na\nlist\nof\nwords\nto\nwrite\nto\nthe\nfile\n" + with pytest.warns(UserWarning, match="Passing a string to PathPlus.write_lines writes each character to its own line."): + tmp_file.write_lines("abcdefg") + + assert tmp_file.read_text() == "a\nb\nc\nd\ne\nf\ng\n" + def test_write_lines_trailing_whitespace(tmp_pathplus: PathPlus): tmp_file = tmp_pathplus / "test.txt" From bf8ea76dbb0fa75748a2a72835cfa34e2d0d3d65 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Tue, 6 Jan 2026 21:41:08 +0000 Subject: [PATCH 18/33] Lint --- domdf_python_tools/bases.py | 21 ++-- domdf_python_tools/dates.py | 4 +- domdf_python_tools/delegators.py | 8 +- domdf_python_tools/import_tools.py | 2 +- domdf_python_tools/pagesizes/classes.py | 2 + domdf_python_tools/pagesizes/units.py | 10 +- domdf_python_tools/paths.py | 26 +++-- domdf_python_tools/pretty_print.py | 6 +- domdf_python_tools/stringlist.py | 14 ++- domdf_python_tools/terminal.py | 2 +- domdf_python_tools/typing.py | 18 +-- domdf_python_tools/utils.py | 4 +- domdf_python_tools/versions.py | 28 ++--- domdf_python_tools/words.py | 3 +- tests/seq_tests.py | 26 ++--- tests/test_bases.py | 25 +++-- tests/test_dates.py | 10 +- tests/test_dir_comparator.py | 23 ++-- tests/test_doctools.py | 37 ++++--- tests/test_getters.py | 15 +-- tests/test_import_tools.py | 45 ++++---- tests/test_iterative.py | 29 +++-- tests/test_paths.py | 137 +++++++++++++---------- tests/test_paths_stdlib.py | 17 ++- tests/test_pretty_print.py | 140 +++++++++++++----------- tests/test_stringlist.py | 27 ++++- tests/test_typing.py | 21 ++-- tests/test_utils.py | 137 +++++++++++++---------- tests/test_versions.py | 126 +++++++-------------- tests/test_words.py | 23 ++-- tox.ini | 14 ++- 31 files changed, 528 insertions(+), 472 deletions(-) diff --git a/domdf_python_tools/bases.py b/domdf_python_tools/bases.py index 4aa4d72..cf59ebf 100644 --- a/domdf_python_tools/bases.py +++ b/domdf_python_tools/bases.py @@ -80,7 +80,7 @@ @prettify_docstrings -class Dictable(Iterable[Tuple[str, _V]]): +class Dictable(Iterable[Tuple[str, _V]]): # noqa: PRM002 """ The basic structure of a class that can be converted into a dictionary. """ @@ -119,7 +119,7 @@ def __deepcopy__(self, memodict={}): def __dict__(self): # type: ignore[override] return dict() # pragma: no cover (abc) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # noqa: MAN001 if isinstance(other, self.__class__): return is_match_with(other.__dict__, self.__dict__) @@ -266,6 +266,8 @@ def __copy__(self): def append(self, item: _T) -> None: """ Append ``item`` to the end of the :class:`~.domdf_python_tools.bases.UserList`. + + :param item: """ self.data.append(item) @@ -273,6 +275,9 @@ def append(self, item: _T) -> None: def insert(self, i: int, item: _T) -> None: """ Insert ``item`` at position ``i`` in the :class:`~.domdf_python_tools.bases.UserList`. + + :param i: + :param item: """ self.data.insert(i, item) @@ -281,6 +286,8 @@ def pop(self, i: int = -1) -> _T: """ Removes and returns the item at index ``i``. + :param i: + :raises IndexError: if list is empty or index is out of range. """ @@ -315,7 +322,7 @@ def copy(self: _S) -> _S: return self.__class__(self) - def count(self, item: _T) -> int: + def count(self, item: _T) -> int: # noqa: PRM002 """ Returns the number of occurrences of ``item`` in the :class:`~.domdf_python_tools.bases.UserList`. """ @@ -323,11 +330,11 @@ def count(self, item: _T) -> int: return self.data.count(item) def index(self, item: _T, *args: Any) -> int: - """ + r""" Returns the index of the fist element matching ``item``. :param item: - :param args: + :param \*args: :raises ValueError: if the item is not present. """ @@ -341,7 +348,7 @@ def reverse(self) -> None: self.data.reverse() - def sort(self, *, key=None, reverse: bool = False) -> None: + def sort(self, *, key=None, reverse: bool = False) -> None: # noqa: PRM002 """ Sort the list in ascending order and return :py:obj:`None`. @@ -628,7 +635,7 @@ def replace(self: _LU, what: _T, with_: _T) -> _LU: return self - def sort( # type: ignore[override] + def sort( # type: ignore[override] # noqa: PRM002 self: _LU, *, key=None, diff --git a/domdf_python_tools/dates.py b/domdf_python_tools/dates.py index e8253f1..71575b4 100644 --- a/domdf_python_tools/dates.py +++ b/domdf_python_tools/dates.py @@ -383,7 +383,7 @@ def is_bst(the_date: Union[time.struct_time, datetime.date]) -> bool: '{word_join(_pytz_functions)}' require pytz (https://pypi.org/project/pytz/), but it could not be imported. The error was: {e}. - """ + """, ) else: @@ -394,7 +394,7 @@ class SelfWrapper(ModuleType): def __getattr__(self, name): if name in _pytz_functions: raise ImportError( - f"{name!r} requires pytz (https://pypi.org/project/pytz/), but it could not be imported." + f"{name!r} requires pytz (https://pypi.org/project/pytz/), but it could not be imported.", ) else: return getattr(_actual_module, name) diff --git a/domdf_python_tools/delegators.py b/domdf_python_tools/delegators.py index 7ce12df..d8fd724 100644 --- a/domdf_python_tools/delegators.py +++ b/domdf_python_tools/delegators.py @@ -83,8 +83,8 @@ def _f(f: _C) -> _C: del to_params[param] f.__signature__ = from_sig.replace( # type: ignore[attr-defined] - parameters=[*from_params.values(), *to_params.values()] - ) + parameters=[*from_params.values(), *to_params.values()], + ) f.__annotations__ = {**to_annotations, **from_annotations} return f @@ -121,8 +121,8 @@ def _f(f: _C) -> _C: elif tuple(from_params.keys()) == ("self", "args", "kwargs"): f.__signature__ = from_sig.replace( # type: ignore[attr-defined] - parameters=[from_params["self"], *to_sig.parameters.values()] - ) + parameters=[from_params["self"], *to_sig.parameters.values()], + ) copy_annotations(f) diff --git a/domdf_python_tools/import_tools.py b/domdf_python_tools/import_tools.py index fc63118..45b1223 100644 --- a/domdf_python_tools/import_tools.py +++ b/domdf_python_tools/import_tools.py @@ -53,7 +53,7 @@ "discover_in_module", "discover_entry_points", "discover_entry_points_by_name", - "iter_submodules" + "iter_submodules", ] diff --git a/domdf_python_tools/pagesizes/classes.py b/domdf_python_tools/pagesizes/classes.py index b2c56cc..8f048be 100644 --- a/domdf_python_tools/pagesizes/classes.py +++ b/domdf_python_tools/pagesizes/classes.py @@ -110,6 +110,8 @@ def from_pt(cls, size: Tuple[float, float]): def from_size(cls, size: Tuple[AnyNumber, AnyNumber]) -> "BaseSize": """ Create a :class:`~domdf_python_tools.pagesizes.classes.BaseSize` object from a tuple. + + :param size: """ return cls(*size) diff --git a/domdf_python_tools/pagesizes/units.py b/domdf_python_tools/pagesizes/units.py index 0a7f2d8..2792f78 100644 --- a/domdf_python_tools/pagesizes/units.py +++ b/domdf_python_tools/pagesizes/units.py @@ -39,7 +39,7 @@ # stdlib import math from decimal import ROUND_HALF_UP, Decimal -from typing import SupportsFloat, Union +from typing import NoReturn, SupportsFloat, Union # this package from domdf_python_tools.doctools import prettify_docstrings @@ -199,12 +199,12 @@ class Unit(float): name: str = "pt" _in_pt: float = 1 - def __repr__(self): + def __repr__(self) -> str: value = _rounders(float(self), "0.000") as_pt = _rounders(self.as_pt(), "0.000") return f"" - def __str__(self): + def __str__(self) -> str: value = _rounders(float(self), "0.000") as_pt = _rounders(self.as_pt(), "0.000") return f"" @@ -251,10 +251,10 @@ def __mod__(self, other: Union[float, "Unit"]) -> "Unit": return self.__class__(super().__mod__(other)) - def __pow__(self, power, modulo=None): + def __pow__(self, power, modulo=None) -> NoReturn: # noqa: MAN001 raise NotImplementedError("Powers are not supported for units.") - def __rtruediv__(self, other): + def __rtruediv__(self, other) -> NoReturn: # noqa: MAN001 raise NotImplementedError("Dividing by a unit is not allowed.") __rdiv__ = __rtruediv__ diff --git a/domdf_python_tools/paths.py b/domdf_python_tools/paths.py index aa972c6..6ca924c 100644 --- a/domdf_python_tools/paths.py +++ b/domdf_python_tools/paths.py @@ -143,7 +143,7 @@ """ -def append(var: str, filename: PathLike, **kwargs) -> int: +def append(var: str, filename: PathLike, **kwargs) -> int: # noqa: PRM002 """ Append ``var`` to the file ``filename`` in the current directory. @@ -205,7 +205,7 @@ def copytree( return dst -def delete(filename: PathLike, **kwargs): +def delete(filename: PathLike, **kwargs): # noqa: PRM002 """ Delete the file in the current directory. @@ -261,7 +261,7 @@ def parent_path(path: PathLike) -> pathlib.Path: return path.parent -def read(filename: PathLike, **kwargs) -> str: +def read(filename: PathLike, **kwargs) -> str: # noqa: PRM002 """ Read a file in the current directory (in text mode). @@ -308,7 +308,7 @@ def relpath(path: PathLike, relative_to: Optional[PathLike] = None) -> pathlib.P return abs_path -def write(var: str, filename: PathLike, **kwargs) -> None: +def write(var: str, filename: PathLike, **kwargs) -> None: # noqa: PRM002 """ Write a variable to file in the current directory. @@ -518,7 +518,7 @@ def write_lines( encoding: Optional[str] = "UTF-8", errors: Optional[str] = None, *, - trailing_whitespace: bool = False + trailing_whitespace: bool = False, ) -> None: """ Write the given list of lines to the file without trailing whitespace. @@ -536,7 +536,7 @@ def write_lines( if isinstance(data, str): warnings.warn( "Passing a string to PathPlus.write_lines writes each character to its own line.\n" - "That probably isn't what you intended." + "That probably isn't what you intended.", ) if trailing_whitespace: @@ -592,7 +592,7 @@ def open( # type: ignore # noqa: A003 # pylint: disable=redefined-builtin encoding: Optional[str] = "UTF-8", errors: Optional[str] = None, newline: Optional[str] = NEWLINE_DEFAULT, - ) -> IO[Any]: + ) -> IO[Any]: """ Open the file pointed by this path and return a file object, as the built-in :func:`open` function does. @@ -790,6 +790,8 @@ def unlink(self, missing_ok: bool = False) -> None: If the path is a directory, use :meth:`~domdf_python_tools.paths.PathPlus.rmdir()` instead. + :param missing_ok: + .. versionadded:: 0.3.8 for Python 3.8 and above .. versionadded:: 0.11.0 for Python 3.6 and Python 3.7 """ @@ -1125,6 +1127,10 @@ class TemporaryPathPlus(tempfile.TemporaryDirectory): Unlike :func:`tempfile.TemporaryDirectory` this class is based around a :class:`~.PathPlus` object. + :param suffix: A str suffix for the directory name. + :param prefix: A str prefix for the directory name. + :param dir: A directory to create this temp dir in. + .. versionadded:: 2.4.0 .. autosummary-widths:: 6/16 """ @@ -1170,12 +1176,12 @@ def __enter__(self) -> PathPlus: def sort_paths(*paths: PathLike) -> List[PathPlus]: - """ + r""" Sort the ``paths`` by directory, then by file. .. versionadded:: 2.6.0 - :param paths: + :param \*paths: """ directories: Dict[str, List[PathPlus]] = defaultdict(list) @@ -1259,7 +1265,7 @@ def phase4(self) -> None: # noqa: D102 "left_only": filecmp.dircmp.phase1, "right_only": filecmp.dircmp.phase1, "left_list": filecmp.dircmp.phase0, - "right_list": filecmp.dircmp.phase0 + "right_list": filecmp.dircmp.phase0, } methodmap = _methodmap # type: ignore[assignment] diff --git a/domdf_python_tools/pretty_print.py b/domdf_python_tools/pretty_print.py index a67bef0..9fe8d16 100644 --- a/domdf_python_tools/pretty_print.py +++ b/domdf_python_tools/pretty_print.py @@ -150,7 +150,7 @@ def _pprint_dict( allowance + 1, context, level, - ) + ) write(self._make_close('}', indent, obj)) @@ -250,7 +250,7 @@ def _format_attribute_items(self, items, stream, indent, allowance, context, lev allowance if last else 1, context, level, - ) + ) if not last: write(delimnl) @@ -263,7 +263,7 @@ def simple_repr(*attributes: str, show_module: bool = False, **kwargs): r""" Adds a simple ``__repr__`` method to the decorated class. - :param attributes: The attributes to include in the ``__repr__``. + :param \*attributes: The attributes to include in the ``__repr__``. :param show_module: Whether to show the name of the module in the ``__repr__``. :param \*\*kwargs: Keyword arguments passed on to :class:`pprint.PrettyPrinter`. """ diff --git a/domdf_python_tools/stringlist.py b/domdf_python_tools/stringlist.py index 920bb2c..b50a82f 100644 --- a/domdf_python_tools/stringlist.py +++ b/domdf_python_tools/stringlist.py @@ -108,7 +108,7 @@ def __repr__(self) -> str: return f"{type(self).__name__}(size={self.size}, type={self.type!r})" - def __eq__(self, other): + def __eq__(self, other) -> bool: # noqa: MAN001 if isinstance(other, Indent): return other.size == self.size and other.type == self.type elif isinstance(other, str): @@ -252,7 +252,7 @@ def __setitem__(self, index: Union[SupportsIndex, slice], line: Union[String, It for lline, idx in zip( reversed(line), reversed(range(index.start or 0, index.stop + 1, index.step or 1)), - ): + ): self[idx] = lline else: line = cast(String, line) @@ -304,7 +304,7 @@ def blankline(self, ensure_single: bool = False): self.append('') - def set_indent_size(self, size: int = 0): + def set_indent_size(self, size: int = 0) -> None: """ Sets the size of the indent to insert at the beginning of new lines. @@ -313,7 +313,7 @@ def set_indent_size(self, size: int = 0): self.indent.size = int(size) - def set_indent_type(self, indent_type: str = '\t'): + def set_indent_type(self, indent_type: str = '\t') -> None: """ Sets the type of the indent to insert at the beginning of new lines. @@ -322,7 +322,7 @@ def set_indent_type(self, indent_type: str = '\t'): self.indent.type = str(indent_type) - def set_indent(self, indent: Union[String, Indent], size: int = 0): + def set_indent(self, indent: Union[String, Indent], size: int = 0) -> None: """ Sets the indent to insert at the beginning of new lines. @@ -386,7 +386,7 @@ def __bytes__(self) -> bytes: return str(self).encode("UTF-8") - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # noqa: PRM002 """ Returns whether the other object is equal to this :class:`~domdf_python_tools.stringlist.StringList`. """ @@ -467,6 +467,8 @@ def splitlines(self, keepends: bool = False) -> List[str]: """ Analagous to :meth:`str.splitlines`. + :param keepends: + .. versionadded:: 3.8.0 """ diff --git a/domdf_python_tools/terminal.py b/domdf_python_tools/terminal.py index 2717938..a0c0ac3 100644 --- a/domdf_python_tools/terminal.py +++ b/domdf_python_tools/terminal.py @@ -179,7 +179,7 @@ def __enter__(self): self.locals_on_entry = self.parent_frame.f_locals.copy() # type: ignore[union-attr] - def __exit__(self, *args, **kwargs): + def __exit__(self, *args, **kwargs): # noqa: PRM002 """ Called when exiting the context manager. """ diff --git a/domdf_python_tools/typing.py b/domdf_python_tools/typing.py index a87878e..c32d7ef 100644 --- a/domdf_python_tools/typing.py +++ b/domdf_python_tools/typing.py @@ -134,7 +134,7 @@ def dumps( sort_keys: bool = ..., **kwds: Any, ) -> str: - """ + r""" Serialize ``obj`` to a JSON formatted :class:`str`. :param obj: @@ -147,7 +147,7 @@ def dumps( :param separators: :param default: :param sort_keys: - :param kwds: + :param \*\*kwds: """ @staticmethod @@ -162,7 +162,7 @@ def loads( object_pairs_hook: Optional[Callable[[List[Tuple[Any, Any]]], Any]] = ..., **kwds: Any, ) -> Any: - """ + r""" Deserialize ``s`` to a Python object. :param s: @@ -172,7 +172,7 @@ def loads( :param parse_int: :param parse_constant: :param object_pairs_hook: - :param kwds: + :param \*\*kwds: :rtype: @@ -225,7 +225,7 @@ def head(self: "HasHead", n: int = 5) -> "HasHead": :return: The first n rows of the caller object. """ - def to_string(self, *args, **kwargs) -> Optional[str]: + def to_string(self, *args, **kwargs) -> Optional[str]: # noqa: PRM002 """ Render the object to a console-friendly tabular output. """ @@ -254,7 +254,7 @@ class SupportsLessThan(Protocol): .. versionadded:: 3.0.0 """ - def __lt__(self, __other: Any) -> bool: + def __lt__(self, __other: Any) -> bool: # noqa: PRM002 """ Return ``self < value``. """ @@ -267,7 +267,7 @@ class SupportsLessEqual(Protocol): .. versionadded:: 3.0.0 """ - def __le__(self, __other: Any) -> bool: + def __le__(self, __other: Any) -> bool: # noqa: PRM002 """ Return ``self <= value``. """ @@ -280,7 +280,7 @@ class SupportsGreaterThan(Protocol): .. versionadded:: 3.0.0 """ - def __gt__(self, __other: Any) -> bool: + def __gt__(self, __other: Any) -> bool: # noqa: PRM002 """ Return ``self > value``. """ @@ -293,7 +293,7 @@ class SupportsGreaterEqual(Protocol): .. versionadded:: 3.0.0 """ - def __ge__(self, __other: Any) -> bool: + def __ge__(self, __other: Any) -> bool: # noqa: PRM002 """ Return ``self >= value``. """ diff --git a/domdf_python_tools/utils.py b/domdf_python_tools/utils.py index 54589a5..533fd9c 100644 --- a/domdf_python_tools/utils.py +++ b/domdf_python_tools/utils.py @@ -139,7 +139,7 @@ SPACE_PLACEHOLDER = '␣' -def cmp(x, y) -> int: +def cmp(x, y) -> int: # noqa: PRM002 """ Implementation of ``cmp`` for Python 3. @@ -278,6 +278,8 @@ def strtobool(val: Union[str, int]) -> bool: :py:obj:`False` values are ``'n'``, ``'no'``, ``'f'``, ``'false'``, ``'off'``, ``'0'``, and ``0``. + :param val: + :raises: :py:exc:`ValueError` if ``val`` is anything else. """ diff --git a/domdf_python_tools/versions.py b/domdf_python_tools/versions.py index fe5cc7f..a2d74ea 100644 --- a/domdf_python_tools/versions.py +++ b/domdf_python_tools/versions.py @@ -78,18 +78,18 @@ class Version(Tuple[int, int, int]): """ @property # type: ignore - def major(self): # noqa: D102 + def major(self) -> int: # noqa: D102 return self[0] @property # type: ignore - def minor(self): # noqa: D102 + def minor(self) -> int: # noqa: D102 return self[1] @property # type: ignore - def patch(self): # noqa: D102 + def patch(self) -> int: # noqa: D102 return self[2] - def __new__(cls: Type[_V], major=0, minor=0, patch=0) -> _V: # noqa: D102 + def __new__(cls: Type[_V], major=0, minor=0, patch=0) -> _V: # noqa: D102,MAN001 t: _V = super().__new__(cls, (int(major), int(minor), int(patch))) return t @@ -123,14 +123,14 @@ def __int__(self) -> int: return self.major - def __getnewargs__(self): + def __getnewargs__(self) -> Tuple[int, int, int]: """ Return Version as a plain tuple. Used by copy and pickle. """ - return tuple(self) + return tuple(self) # type: ignore[return-value] - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # noqa: MAN001,PRM002 """ Returns whether this version is equal to the other version. @@ -145,7 +145,7 @@ def __eq__(self, other) -> bool: shortest = min(len(self), (len(other))) return self[:shortest] == other[:shortest] - def __gt__(self, other) -> bool: + def __gt__(self, other) -> bool: # noqa: MAN001,PRM002 """ Returns whether this version is greater than the other version. @@ -159,7 +159,7 @@ def __gt__(self, other) -> bool: else: return tuple(self) > other - def __lt__(self, other) -> bool: + def __lt__(self, other) -> bool: # noqa: MAN001,PRM002 """ Returns whether this version is less than the other version. @@ -173,7 +173,7 @@ def __lt__(self, other) -> bool: else: return tuple(self) < other - def __ge__(self, other) -> bool: + def __ge__(self, other) -> bool: # noqa: MAN001,PRM002 """ Returns whether this version is greater than or equal to the other version. @@ -187,7 +187,7 @@ def __ge__(self, other) -> bool: else: return tuple(self)[:len(other)] >= other - def __le__(self, other) -> bool: + def __le__(self, other) -> bool: # noqa: MAN001,PRM002 """ Returns whether this version is less than or equal to the other version. @@ -256,12 +256,12 @@ def _asdict(self) -> Dict[str, int]: } def _replace(self: _V, **kwargs) -> _V: - """ + r""" Return a new instance of the named tuple replacing specified fields with new values. .. versionadded:: 1.4.0 - :param kwargs: + :param \*\*kwargs: """ return self.__class__(**{**self._asdict(), **kwargs}) @@ -303,7 +303,7 @@ def _iter_float(version_float: float) -> Generator[int, None, None]: return _iter_string(str(version_float)) -def _prep_for_eq(other: Union[str, float, Version], ) -> Tuple[int, ...]: +def _prep_for_eq(other: Union[str, float, Version]) -> Tuple[int, ...]: # noqa: PRM002 """ Prepare 'other' for use in ``__eq__``, ``__le__``, ``__ge__``, ``__gt__``, and ``__lt__``. """ diff --git a/domdf_python_tools/words.py b/domdf_python_tools/words.py index ad2e5fe..d7dc7d8 100644 --- a/domdf_python_tools/words.py +++ b/domdf_python_tools/words.py @@ -39,7 +39,6 @@ # stdlib import functools import random -import re import sys from gettext import ngettext from reprlib import recursive_repr @@ -614,7 +613,7 @@ def __new__(cls, singular: str, plural: str): # noqa: D102 return functools.partial.__new__(cls, ngettext, singular, plural) @recursive_repr() - def __repr__(self): + def __repr__(self) -> str: qualname = type(self).__qualname__ args: List[str] = [] args.extend(repr(x) for x in self.args) diff --git a/tests/seq_tests.py b/tests/seq_tests.py index cad4c30..c981bb2 100644 --- a/tests/seq_tests.py +++ b/tests/seq_tests.py @@ -12,7 +12,7 @@ import pickle import sys from itertools import chain -from typing import Any, List +from typing import Any, Callable, Iterator, List, Tuple, Type # 3rd party import pytest @@ -180,16 +180,16 @@ def itermulti(seqn): return chain(map(lambda x: x, iterfunc(IterGen(Sequence(seqn))))) -class LyingTuple(tuple): +class LyingTuple(Tuple[int]): __slots__ = () - def __iter__(self): + def __iter__(self) -> Iterator[int]: yield 1 -class LyingList(list): +class LyingList(List[int]): - def __iter__(self): + def __iter__(self) -> Iterator[int]: yield 1 @@ -197,13 +197,13 @@ class CommonTest: # The type to be tested type2test: type - def assertEqual(self, left, right): + def assertEqual(self, left: Any, right: Any): assert left == right - def assertNotEqual(self, left, right): + def assertNotEqual(self, left: Any, right: Any): assert left != right - def assertRaises(self, what, func, *args): + def assertRaises(self, what: Type[Exception], func: Callable, *args): with pytest.raises(what): func(*args) @@ -226,13 +226,13 @@ def test_constructors(self): class OtherSeq: - def __init__(self, initseq): + def __init__(self, initseq) -> None: self.__data = initseq - def __len__(self): + def __len__(self) -> int: return len(self.__data) - def __getitem__(self, i): + def __getitem__(self, i): # noqa: MAN001,MAN002 return self.__data[i] s = OtherSeq(u0) @@ -500,7 +500,7 @@ class BadExc(Exception): class BadCmp: - def __eq__(self, other): + def __eq__(self, other) -> bool: if other == 2: raise BadExc() return False @@ -540,7 +540,7 @@ class BadExc(Exception): class BadCmp: - def __eq__(self, other): + def __eq__(self, other) -> bool: if other == 2: raise BadExc() return False diff --git a/tests/test_bases.py b/tests/test_bases.py index e0f22d6..ff4eec6 100644 --- a/tests/test_bases.py +++ b/tests/test_bases.py @@ -10,7 +10,7 @@ import copy import pickle # nosec: B101 from numbers import Number, Real -from typing import no_type_check +from typing import Any, Dict, Optional, no_type_check # 3rd party import pytest @@ -22,13 +22,13 @@ class Person(Dictable): - def __init__(self, name, age, occupation=None): + def __init__(self, name: str, age: int, occupation: Optional[str] = None): self.name = str(name) self.age = int(age) self.occupation = occupation @property - def __dict__(self): # type: ignore[override] + def __dict__(self) -> Dict[str, Any]: # type: ignore[override] return dict( name=self.name, age=self.age, @@ -38,13 +38,13 @@ def __dict__(self): # type: ignore[override] class Child(Person): - def __init__(self, name, age, school): + def __init__(self, name: str, age: int, school: str): super().__init__(name, age, "Student") self.school = "school" @property - def __dict__(self): # type: ignore[override] + def __dict__(self) -> Dict[str, Any]: # type: ignore[override] class_dict = super().__dict__ class_dict["School"] = self.school return class_dict @@ -57,12 +57,13 @@ def alice(): class TestDictable: - def test_creation(self, alice): + @no_type_check + def test_creation(self, alice: Dictable): assert alice.name == "Alice" assert alice.age == 20 assert alice.occupation == "IRC Lurker" - def test_str(self, alice: object): + def test_str(self, alice: Dictable): assert str(alice).startswith(" None: pass doctools.document_object_from_another(funA, str) diff --git a/tests/test_getters.py b/tests/test_getters.py index 4f4df9e..0f5c2d5 100644 --- a/tests/test_getters.py +++ b/tests/test_getters.py @@ -354,15 +354,12 @@ def baz(*args, **kwds): def test_repr(self): assert repr(methodcaller(0, "lower")) == "domdf_python_tools.getters.methodcaller(0, 'lower')" assert repr(methodcaller(1, "__iter__")) == "domdf_python_tools.getters.methodcaller(1, '__iter__')" - assert repr( - methodcaller(1, "__iter__", "arg1") - ) == "domdf_python_tools.getters.methodcaller(1, '__iter__', 'arg1')" - assert repr( - methodcaller(1, "__iter__", kw1="kwarg1") - ) == "domdf_python_tools.getters.methodcaller(1, '__iter__', kw1='kwarg1')" - assert repr( - methodcaller(1, "__iter__", "arg1", "arg2", kw1="kwarg1", kw2="kwarg2") - ) == "domdf_python_tools.getters.methodcaller(1, '__iter__', 'arg1', 'arg2', kw1='kwarg1', kw2='kwarg2')" + expected = "domdf_python_tools.getters.methodcaller(1, '__iter__', 'arg1')" + assert repr(methodcaller(1, "__iter__", "arg1")) == expected + expected = "domdf_python_tools.getters.methodcaller(1, '__iter__', kw1='kwarg1')" + assert repr(methodcaller(1, "__iter__", kw1="kwarg1")) == expected + expected = "domdf_python_tools.getters.methodcaller(1, '__iter__', 'arg1', 'arg2', kw1='kwarg1', kw2='kwarg2')" + assert repr(methodcaller(1, "__iter__", "arg1", "arg2", kw1="kwarg1", kw2="kwarg2")) == expected evaluate(repr(methodcaller(0, "lower"))) evaluate(repr(methodcaller(1, "__iter__"))) diff --git a/tests/test_import_tools.py b/tests/test_import_tools.py index 998aa48..35b1479 100644 --- a/tests/test_import_tools.py +++ b/tests/test_import_tools.py @@ -4,6 +4,7 @@ import re import sys from contextlib import contextmanager +from typing import Any, ContextManager # 3rd party import pytest @@ -39,7 +40,8 @@ def test_discover(): def test_discover_function_only(): # Alphabetical order regardless of order in the module. assert discover( - discover_demo_module, match_func=inspect.isfunction + discover_demo_module, + match_func=inspect.isfunction, ) == [ discover_demo_module.foo_in_init, discover_demo_module.submodule_a.bar, @@ -50,7 +52,8 @@ def test_discover_function_only(): def test_discover_class_only(): # Alphabetical order regardless of order in the module. assert discover( - discover_demo_module, match_func=inspect.isclass + discover_demo_module, + match_func=inspect.isclass, ) == [ discover_demo_module.submodule_b.Alice, discover_demo_module.submodule_b.Bob, @@ -59,7 +62,7 @@ def test_discover_class_only(): def test_discover_hasattr(): - def match_func(obj): + def match_func(obj: Any): return hasattr(obj, "foo") assert discover(discover_demo_module, match_func=match_func) == [] @@ -102,7 +105,7 @@ def raises_attribute_error(obj, **kwargs): pytest.param(HasPath, haspath_error, id="HasPath"), ], ) -def test_discover_errors(obj, expects): +def test_discover_errors(obj: Any, expects: ContextManager): with expects: discover(obj) @@ -114,14 +117,16 @@ def test_discover_entry_points(advanced_data_regression: AdvancedDataRegressionF def test_discover_entry_points_by_name_object_match_func(advanced_data_regression: AdvancedDataRegressionFixture): entry_points = discover_entry_points_by_name( - "flake8.extension", object_match_func=lambda f: f.__name__.startswith("break") + "flake8.extension", + object_match_func=lambda f: f.__name__.startswith("break"), ) advanced_data_regression.check({k: v.__name__ for k, v in entry_points.items()}) def test_discover_entry_points_by_name_name_match_func(advanced_data_regression: AdvancedDataRegressionFixture): entry_points = discover_entry_points_by_name( - "flake8.extension", name_match_func=lambda n: n.startswith("pycodestyle.") + "flake8.extension", + name_match_func=lambda n: n.startswith("pycodestyle."), ) advanced_data_regression.check({k: v.__name__ for k, v in entry_points.items()}) @@ -134,46 +139,46 @@ def test_discover_entry_points_by_name_name_match_func(advanced_data_regression: 3.7, marks=[ only_version(3.7, reason="Output differs on Python 3.7"), - not_pypy("Output differs on PyPy") - ] + not_pypy("Output differs on PyPy"), + ], ), pytest.param( "3.7-pypy", marks=[ only_version(3.7, reason="Output differs on Python 3.7"), - only_pypy("Output differs on PyPy") - ] + only_pypy("Output differs on PyPy"), + ], ), pytest.param( 3.8, marks=[ only_version(3.8, reason="Output differs on Python 3.8"), - not_pypy("Output differs on PyPy 3.8") - ] + not_pypy("Output differs on PyPy 3.8"), + ], ), pytest.param( "3.8_pypy", marks=[ only_version(3.8, reason="Output differs on Python 3.8"), - only_pypy("Output differs on PyPy 3.8") - ] + only_pypy("Output differs on PyPy 3.8"), + ], ), pytest.param( 3.9, marks=[ only_version(3.9, reason="Output differs on Python 3.9"), - not_pypy("Output differs on PyPy 3.9") - ] + not_pypy("Output differs on PyPy 3.9"), + ], ), pytest.param( "3.9_pypy", marks=[ only_version(3.9, reason="Output differs on Python 3.9"), - only_pypy("Output differs on PyPy 3.9") - ] + only_pypy("Output differs on PyPy 3.9"), + ], ), pytest.param("3.10", marks=only_version("3.10", reason="Output differs on Python 3.10")), - ] + ], ) @@ -242,7 +247,7 @@ def freedesktop_os_release(): [ pytest.param('', marks=pytest.mark.skipif(on_alt_linux, reason="Not for ALT Linux")), pytest.param("altlinux", marks=pytest.mark.skipif(not on_alt_linux, reason="Only for ALT Linux")), - ] + ], ) def test_iter_submodules_asyncio( platform, diff --git a/tests/test_iterative.py b/tests/test_iterative.py index ca7b352..37290a2 100644 --- a/tests/test_iterative.py +++ b/tests/test_iterative.py @@ -21,7 +21,7 @@ from itertools import islice from random import shuffle from types import GeneratorType -from typing import Any, Iterable, List, Optional, Sequence, Tuple, TypeVar +from typing import Any, Iterable, List, MutableSequence, Optional, Sequence, Tuple, TypeVar # 3rd party import pytest @@ -138,7 +138,7 @@ def test_len(capsys): ([((1, 2), (3, 4)), ((5, 6), (7, 8))], [1, 2, 3, 4, 5, 6, 7, 8]), ((((1, 2), (3, 4)), ((5, 6), (7, 8))), [1, 2, 3, 4, 5, 6, 7, 8]), ((("12", "34"), ("56", "78")), ['1', '2', '3', '4', '5', '6', '7', '8']), - ] + ], ) def test_double_chain(value, expects): assert list(double_chain(value)) == expects @@ -164,9 +164,9 @@ def test_make_tree(advanced_file_regression: AdvancedFileRegressionFixture): ], ], "domdf_python_tools==2.2.0", - ]) + ]), ), - advanced_file_regression + advanced_file_regression, ) @@ -177,9 +177,9 @@ def test_make_tree(advanced_file_regression: AdvancedFileRegressionFixture): ["abc", "def", ["ghi", "jkl", "mno", "pqr"]], ["abc", "def", "ghi", "jkl", ["mno", "pqr"]], ["abc", "def", "ghi", "jkl", "mno", "pqr"], - ] + ], ) -def test_flatten(data, advanced_data_regression: AdvancedDataRegressionFixture): +def test_flatten(data: List, advanced_data_regression: AdvancedDataRegressionFixture): advanced_data_regression.check(list(flatten(data))) @@ -191,9 +191,9 @@ def test_flatten(data, advanced_data_regression: AdvancedDataRegressionFixture): pytest.param(['1', '3', '5', '7', '9'], id="numerical_strings"), pytest.param(["1.2", "3.4", "5.6", "7.8", "9.0"], id="float strings"), pytest.param(["0.9", "0.12.4", '1', "2.5"], id="versions"), - ] + ], ) -def test_natmin(data): +def test_natmin(data: MutableSequence): orig_data = data[:] for _ in range(5): shuffle(data) @@ -208,9 +208,9 @@ def test_natmin(data): pytest.param(['1', '3', '5', '7', '9'], id="numerical_strings"), pytest.param(["1.2", "3.4", "5.6", "7.8", "9.0"], id="float strings"), pytest.param(["0.9", "0.12.4", '1', "2.5"], id="versions"), - ] + ], ) -def test_natmax(data): +def test_natmax(data: MutableSequence): orig_data = data[:] for _ in range(5): shuffle(data) @@ -233,9 +233,8 @@ def test_groupfloats(): def test_ranges_from_iterable(): expects = [(170.0, 170.15), (171.05, 171.2)] - assert list( - ranges_from_iterable([170.0, 170.05, 170.1, 170.15, 171.05, 171.1, 171.15, 171.2], step=0.05) - ) == expects + ranges = ranges_from_iterable([170.0, 170.05, 170.1, 170.15, 171.05, 171.1, 171.15, 171.2], step=0.05) + assert list(ranges) == expects expects = [(1, 5), (7, 10)] assert list(ranges_from_iterable([1, 2, 3, 4, 5, 7, 8, 9, 10])) == expects @@ -255,7 +254,7 @@ def _extend_param(sequence: str, expects: Any): _extend_param("abcde", "abcde"), pytest.param(('a', 'b', 'c', 'd', 'e'), "abcde", id="tuple"), pytest.param(['a', 'b', 'c', 'd', 'e'], "abcde", id="list"), - ] + ], ) def test_extend(sequence: Sequence[str], expects: str): assert ''.join(extend(sequence, 4)) == expects @@ -271,7 +270,7 @@ def test_extend(sequence: Sequence[str], expects: str): _extend_param("abcde", "abcde"), pytest.param(('a', 'b', 'c', 'd', 'e'), "abcde", id="tuple"), pytest.param(['a', 'b', 'c', 'd', 'e'], "abcde", id="list"), - ] + ], ) def test_extend_with(sequence: Sequence[str], expects: str): assert ''.join(extend_with(sequence, 4, 'z')) == expects diff --git a/tests/test_paths.py b/tests/test_paths.py index c01696e..4639250 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -35,9 +35,10 @@ sort_paths, traverse_to_file ) +from domdf_python_tools.typing import PathLike -def test_maybe_make(tmp_pathplus): +def test_maybe_make(tmp_pathplus: PathPlus): test_dir = tmp_pathplus / "maybe_make" assert test_dir.exists() is False @@ -64,7 +65,7 @@ def test_maybe_make(tmp_pathplus): assert test_dir.is_file() -def test_maybe_make_pathplus(tmp_pathplus): +def test_maybe_make_pathplus(tmp_pathplus: PathPlus): test_dir = tmp_pathplus / "maybe_make" assert test_dir.exists() is False @@ -91,7 +92,7 @@ def test_maybe_make_pathplus(tmp_pathplus): assert test_dir.is_file() -def test_maybe_make_string(tmp_pathplus): +def test_maybe_make_string(tmp_pathplus: PathPlus): test_dir = tmp_pathplus / "maybe_make" assert test_dir.exists() is False @@ -118,7 +119,7 @@ def test_maybe_make_string(tmp_pathplus): assert test_dir.is_file() -def test_maybe_make_parents(tmp_pathplus): +def test_maybe_make_parents(tmp_pathplus: PathPlus): test_dir = tmp_pathplus / "maybe_make" / "child1" / "child2" assert test_dir.exists() is False @@ -134,7 +135,7 @@ def test_maybe_make_parents(tmp_pathplus): assert test_dir.exists() -def test_maybe_make_parents_pathplus(tmp_pathplus): +def test_maybe_make_parents_pathplus(tmp_pathplus: PathPlus): test_dir = tmp_pathplus / "maybe_make" / "child1" / "child2" assert test_dir.exists() is False @@ -150,7 +151,7 @@ def test_maybe_make_parents_pathplus(tmp_pathplus): assert test_dir.exists() -def test_parent_path(tmp_pathplus): +def test_parent_path(tmp_pathplus: PathPlus): dir1 = tmp_pathplus / "dir1" dir2 = dir1 / "dir2" dir3 = dir2 / "dir3" @@ -172,7 +173,7 @@ def test_parent_path(tmp_pathplus): (None, pathlib.Path("/home/username/Documents/letter.doc")), ], ) -def test_relpath(relto, relpath): +def test_relpath(relto: PathLike, relpath: PathLike): path = "/home/username/Documents/letter.doc" assert paths.relpath(path, relative_to=relto) == pathlib.Path(relpath) assert isinstance(paths.relpath(path, relative_to=relto), pathlib.Path) @@ -186,13 +187,13 @@ def test_relpath(relto, relpath): ("c:/users/username/Documents", "letter.doc"), ( pathlib.Path("c:/users/username/Documents/games/chess.py"), - "c:/users/username/Documents/letter.doc" + "c:/users/username/Documents/letter.doc", ), (pathlib.Path("c:/users/username/Documents"), "letter.doc"), (None, pathlib.Path("c:/users/username/Documents/letter.doc")), ], ) -def test_relpath_windows(relto, relpath): +def test_relpath_windows(relto: PathLike, relpath: PathLike): path = "c:/users/username/Documents/letter.doc" assert paths.relpath(path, relative_to=relto) == pathlib.Path(relpath) assert isinstance(paths.relpath(path, relative_to=relto), pathlib.Path) @@ -245,7 +246,7 @@ def teardown_class(cls): pathlib.Path(file).unlink() -def test_clean_writer(tmp_pathplus): +def test_clean_writer(tmp_pathplus: PathPlus): tempfile = tmp_pathplus / "tmpfile.txt" test_string = '\n'.join([ @@ -287,36 +288,45 @@ def test_clean_writer(tmp_pathplus): @pytest.mark.parametrize( "input_string, output_string", - [([ - "Top line", - " ", - "Line with whitespace ", - "Line with tabs\t\t\t\t ", - "No newline at end of file", - ], [ - "Top line", - '', - "Line with whitespace", - "Line with tabs", - "No newline at end of file", - '', - ]), - ([ - "Top line", - " ", - "Line with whitespace ", - "Line with tabs\t\t\t\t ", - "Too many newlines\n\n\n\n\n\n\n" - ], [ - "Top line", - '', - "Line with whitespace", - "Line with tabs", - "Too many newlines", - '', - ]), ([], [''])] + [ + ( + [ + "Top line", + " ", + "Line with whitespace ", + "Line with tabs\t\t\t\t ", + "No newline at end of file", + ], + [ + "Top line", + '', + "Line with whitespace", + "Line with tabs", + "No newline at end of file", + '', + ], + ), + ( + [ + "Top line", + " ", + "Line with whitespace ", + "Line with tabs\t\t\t\t ", + "Too many newlines\n\n\n\n\n\n\n", + ], + [ + "Top line", + '', + "Line with whitespace", + "Line with tabs", + "Too many newlines", + '', + ], + ), + ([], ['']), + ], ) -def test_pathplus_write_clean(tmp_pathplus, input_string, output_string): +def test_pathplus_write_clean(tmp_pathplus: PathPlus, input_string: str, output_string: str): tempfile = tmp_pathplus / "tmpfile.txt" tempfile.write_clean('\n'.join(input_string)) @@ -360,7 +370,7 @@ def test_instantiate_wrong_platform(): paths.WindowsPathPlus() -def test_copytree(tmp_pathplus): +def test_copytree(tmp_pathplus: PathPlus): srcdir = tmp_pathplus / "src" srcdir.mkdir() @@ -411,7 +421,7 @@ def test_copytree(tmp_pathplus): assert (destdir / 'c' / "c.txt").is_file() -def test_copytree_exists(tmp_pathplus): +def test_copytree_exists(tmp_pathplus: PathPlus): srcdir = tmp_pathplus / "src" srcdir.mkdir() @@ -465,7 +475,7 @@ def test_copytree_exists(tmp_pathplus): condition=(sys.version_info < (3, 6, 9) and platform.python_implementation() == "PyPy"), reason="Fails with unrelated error on PyPy 7.1.1 / 3.6.1", ) -def test_copytree_exists_stdlib(tmp_pathplus): +def test_copytree_exists_stdlib(tmp_pathplus: PathPlus): srcdir = tmp_pathplus / "src" srcdir.mkdir() @@ -499,7 +509,7 @@ def test_copytree_exists_stdlib(tmp_pathplus): shutil.copytree(srcdir, destdir) -def test_write_lines(tmp_pathplus): +def test_write_lines(tmp_pathplus: PathPlus): tmp_file = tmp_pathplus / "test.txt" contents = [ @@ -521,9 +531,12 @@ def test_write_lines(tmp_pathplus): content = tmp_file.read_text() assert content == "this\nis\na\nlist\nof\nwords\nto\nwrite\nto\nthe\nfile\n" - with pytest.warns(UserWarning, match="Passing a string to PathPlus.write_lines writes each character to its own line."): + with pytest.warns( + UserWarning, + match="Passing a string to PathPlus.write_lines writes each character to its own line.", + ): tmp_file.write_lines("abcdefg") - + assert tmp_file.read_text() == "a\nb\nc\nd\ne\nf\ng\n" @@ -572,7 +585,7 @@ def test_read_lines(tmp_pathplus: PathPlus): assert tmp_file.read_lines() == expected -def test_dump_json(tmpdir): +def test_dump_json(tmpdir: str): tmpdir_p = PathPlus(tmpdir) tmp_file = tmpdir_p / "test.txt" @@ -592,7 +605,7 @@ def test_dump_json(tmpdir): """) -def test_dump_json_gzip(tmpdir): +def test_dump_json_gzip(tmpdir: str): tmpdir_p = PathPlus(tmpdir) tmp_file = tmpdir_p / "test.txt" @@ -604,7 +617,7 @@ def test_dump_json_gzip(tmpdir): assert tmp_file.load_json(decompress=True) == {"key": "value", "int": 1234, "float": 12.34} -def test_load_json(tmpdir): +def test_load_json(tmpdir: str): tmpdir_p = PathPlus(tmpdir) tmp_file = tmpdir_p / "test.txt" @@ -613,12 +626,13 @@ def test_load_json(tmpdir): assert tmp_file.load_json() == {"key": "value", "int": 1234, "float": 12.34} - tmp_file.write_text(dedent("""\ + json_string = dedent("""\ { "key": "value", "int": 1234, "float": 12.34 - }""")) + }""") + tmp_file.write_text(json_string) assert tmp_file.load_json() == {"key": "value", "int": 1234, "float": 12.34} @@ -647,7 +661,7 @@ def test_in_directory(tmp_pathplus: PathPlus): ("foo/foo.yml", "foo"), ("foo/bar/foo.yml", "foo/bar"), ("foo/bar/baz/foo.yml", "foo/bar/baz"), - ] + ], ) def test_traverse_to_file(tmp_pathplus: PathPlus, location: str, expected: str): (tmp_pathplus / location).parent.maybe_make(parents=True) @@ -716,7 +730,13 @@ def test_iterchildren_match(advanced_data_regression: AdvancedDataRegressionFixt child_paths = sorted(p.relative_to(repo_path).as_posix() for p in children) for exclude_filename in { - ".coverage", "pathtype_demo.py", "dist", "htmlcov", "conda", ".idea", "mutdef.py" + ".coverage", + "pathtype_demo.py", + "dist", + "htmlcov", + "conda", + ".idea", + "mutdef.py", }: if exclude_filename in child_paths: child_paths.remove(exclude_filename) @@ -817,7 +837,7 @@ def test_iterchildren_no_exclusions(tmp_pathplus: PathPlus): ( "/home/domdf/Python/01 GitHub Repos/03 Libraries/domdf_python_tools/**/*.py", "/home/domdf/Python/01 GitHub Repos/03 Libraries/domdf_python_tools/domdf_python_tools/pagesizes/units.py", - True + True, ), ("domdf_python_tools/**/*.py", "domdf_python_tools/domdf_python_tools/pagesizes/units.py", True), ("**/*.py", ".pre-commit-config.yaml", False), @@ -836,7 +856,7 @@ def test_iterchildren_no_exclusions(tmp_pathplus: PathPlus): ("**/.tox/*", "foo/bar/.tox/build", True), ("**/.tox/**", "foo/bar/.tox/build", True), ("**/.tox/**", "foo/bar/.tox/build/baz", True), - ] + ], ) def test_matchglob(pattern: str, filename: str, match: bool): assert matchglob(filename, pattern) is match @@ -923,7 +943,7 @@ def test_sort_paths(): "c:/a/bΓ©", "//some/share/", "//some/share/a/b.c", - "//some/share/a/b%#cΓ©" + "//some/share/a/b%#cΓ©", ] else: _from_uri_paths = ['/', "/home/domdf/β˜ƒ.txt", "/a/b.c", "/a/b%#c"] @@ -931,11 +951,12 @@ def test_sort_paths(): @pytest.mark.parametrize("path", _from_uri_paths) @pytest.mark.parametrize( - "left_type", [ + "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 @@ -973,7 +994,7 @@ def test_write_text_line_endings(tmp_pathplus: PathPlus): @pytest.fixture() -def move_example_file(tmp_pathplus) -> PathPlus: +def move_example_file(tmp_pathplus: PathPlus) -> PathPlus: src_file = tmp_pathplus / "tmpdir/foo" src_file.parent.maybe_make(parents=True) src_file.write_bytes(b"spam") diff --git a/tests/test_paths_stdlib.py b/tests/test_paths_stdlib.py index f157140..b90877e 100644 --- a/tests/test_paths_stdlib.py +++ b/tests/test_paths_stdlib.py @@ -24,6 +24,7 @@ # this package from domdf_python_tools.compat import PYPY from domdf_python_tools.paths import PathPlus, PosixPathPlus, WindowsPathPlus +from domdf_python_tools.typing import PathLike try: # stdlib @@ -63,11 +64,11 @@ def BASE(tmp_pathplus: PathPlus) -> Iterator[PathPlus]: if os.name == "nt": # Workaround for http://bugs.python.org/issue13772. - def dirlink(src, dest): + def dirlink(src: PathLike, dest: PathLike): os.symlink(src, dest, target_is_directory=True) else: - def dirlink(src, dest): + def dirlink(src: PathLike, dest: PathLike): os.symlink(src, dest) os.mkdir(join("dirA")) @@ -100,17 +101,13 @@ def dirlink(src, dest): shutil.rmtree(top_dir) -def assertEqualNormCase(path_a, path_b): - assert (os.path.normcase(path_a) == os.path.normcase(path_b)) - - if os.name == "nt": # Workaround for http://bugs.python.org/issue13772. - def dirlink(src, dest): + def dirlink(src: PathLike, dest: PathLike) -> None: os.symlink(src, dest, target_is_directory=True) else: - def dirlink(src, dest): + def dirlink(src: PathLike, dest: PathLike) -> None: os.symlink(src, dest) @@ -151,7 +148,7 @@ def test_cwd(): p = PathPlus.cwd() q = PathPlus(os.getcwd()) assert (p == q) - assertEqualNormCase(str(p), str(q)) + assert (os.path.normcase(str(p)) == os.path.normcase(str(q))) assert (type(p) is type(q)) assert (p.is_absolute()) @@ -440,7 +437,7 @@ def test_mkdir_concurrent_parent_creation(BASE: PathPlus): real_mkdir = os.mkdir - def my_mkdir(path, mode=0o777): + def my_mkdir(path: PathLike, mode: int = 0o777): path = str(path) # Emulate another process that would create the directory # just before we try to create it ourselves. We do it diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index 11813ce..256ef37 100644 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -13,7 +13,7 @@ import random import types from textwrap import dedent -from typing import no_type_check +from typing import Any, no_type_check # 3rd party import pytest @@ -32,13 +32,13 @@ class list2(list): class list3(list): - def __repr__(self): + def __repr__(self) -> str: return list.__repr__(self) class list_custom_repr(list): - def __repr__(self): + def __repr__(self) -> str: return '*' * len(list.__repr__(self)) @@ -49,14 +49,14 @@ class tuple2(tuple): class tuple3(tuple): __slots__ = () - def __repr__(self): + def __repr__(self) -> str: return tuple.__repr__(self) class tuple_custom_repr(tuple): __slots__ = () - def __repr__(self): + def __repr__(self) -> str: return '*' * len(tuple.__repr__(self)) @@ -66,13 +66,13 @@ class set2(set): class set3(set): - def __repr__(self): + def __repr__(self) -> str: return set.__repr__(self) class set_custom_repr(set): - def __repr__(self): + def __repr__(self) -> str: return '*' * len(set.__repr__(self)) @@ -82,13 +82,13 @@ class frozenset2(frozenset): class frozenset3(frozenset): - def __repr__(self): + def __repr__(self) -> str: return frozenset.__repr__(self) class frozenset_custom_repr(frozenset): - def __repr__(self): + def __repr__(self) -> str: return '*' * len(frozenset.__repr__(self)) @@ -98,19 +98,19 @@ class dict2(dict): class dict3(dict): - def __repr__(self): + def __repr__(self) -> str: return dict.__repr__(self) class dict_custom_repr(dict): - def __repr__(self): + def __repr__(self) -> str: return '*' * len(dict.__repr__(self)) class Unorderable: - def __repr__(self): + def __repr__(self) -> str: return str(id(self)) @@ -120,25 +120,25 @@ class Orderable: def __init__(self, hash): # noqa: A002 # pylint: disable=redefined-builtin self._hash = hash - def __lt__(self, other): + def __lt__(self, other) -> bool: return False - def __gt__(self, other): + def __gt__(self, other) -> bool: return self != other - def __le__(self, other): + def __le__(self, other) -> bool: return self == other - def __ge__(self, other): + def __ge__(self, other) -> bool: return True - def __eq__(self, other): + def __eq__(self, other) -> bool: return self is other - def __ne__(self, other): + def __ne__(self, other) -> bool: return self is not other - def __hash__(self): + def __hash__(self) -> int: return self._hash @@ -175,7 +175,7 @@ def test_list(self): 'tomato', 'cherry', 'blackcurrant', - ]""" + ]""", ) @no_type_check @@ -212,7 +212,7 @@ def test_init(self): ..., list(range(100)), list(range(200)), - ] + ], ) def test_basic(self, safe): # Verify .isrecursive() and .isreadable() w/o recursion @@ -328,7 +328,7 @@ def test_basic_line_wrap(self): "controldesk_runtime_us": 0, "main_code_runtime_us": 0, "read_io_runtime_us": 0, - "write_io_runtime_us": 43690 + "write_io_runtime_us": 43690, } exp = """\ { @@ -417,7 +417,7 @@ def test_width(self): assert FancyPrinter(width=16).pformat(o) == expected assert FancyPrinter(width=25).pformat(o) == expected assert FancyPrinter(width=14).pformat( - o + o, ) == """\ [ [ @@ -506,7 +506,7 @@ def test_mapping_proxy(self): d = dict(zip(words, itertools.count())) m = types.MappingProxyType(d) assert FancyPrinter().pformat( - m + m, ) == """\ mappingproxy({ 'the': 0, @@ -522,7 +522,7 @@ def test_mapping_proxy(self): d = collections.OrderedDict(zip(words, itertools.count())) m = types.MappingProxyType(d) assert FancyPrinter().pformat( - m + m, ) == """\ mappingproxy(OrderedDict([ ('the', 0), @@ -556,9 +556,9 @@ def test_subclassing(self, advanced_file_regression: AdvancedFileRegressionFixtu pytest.param(set(range(7)), 20, id="case_1"), pytest.param(set2(range(7)), 20, id="case_2"), pytest.param(set3(range(7)), 20, id="case_3"), - ] + ], ) - def test_set_reprs(self, value, width, advanced_file_regression: AdvancedFileRegressionFixture): + def test_set_reprs(self, value, width: int, advanced_file_regression: AdvancedFileRegressionFixture): assert FancyPrinter().pformat(set()) == "set()" assert FancyPrinter().pformat(set(range(3))) == "{0, 1, 2}" advanced_file_regression.check(FancyPrinter(width=width).pformat(value)) @@ -569,9 +569,14 @@ def test_set_reprs(self, value, width, advanced_file_regression: AdvancedFileReg pytest.param(frozenset(range(7)), 20, id="case_1"), pytest.param(frozenset2(range(7)), 20, id="case_2"), pytest.param(frozenset3(range(7)), 20, id="case_3"), - ] + ], ) - def test_frozenset_reprs(self, value, width, advanced_file_regression: AdvancedFileRegressionFixture): + def test_frozenset_reprs( + self, + value: frozenset, + width: int, + advanced_file_regression: AdvancedFileRegressionFixture, + ): assert FancyPrinter().pformat(frozenset()) == "frozenset()" assert FancyPrinter().pformat(frozenset(range(3))) == "frozenset({0, 1, 2})" advanced_file_regression.check(FancyPrinter(width=width).pformat(value)) @@ -601,8 +606,8 @@ def test_sort_unorderable_values(self): assert clean(FancyPrinter().pformat(set(keys))) == '{' + ','.join(map(repr, skeys)) + ",}" assert clean(FancyPrinter().pformat(frozenset(keys))) == "frozenset({" + ','.join(map(repr, skeys)) + ",})" - assert clean(FancyPrinter().pformat(dict.fromkeys(keys)) - ) == '{' + ','.join("%r:None" % k for k in keys) + ",}" + expected = '{' + ','.join("%r:None" % k for k in keys) + ",}" + assert clean(FancyPrinter().pformat(dict.fromkeys(keys))) == expected # Issue 10017: TypeError on user-defined types as dict keys. assert FancyPrinter().pformat({Unorderable: 0, 1: 0}) == "{1: 0, " + repr(Unorderable) + ": 0}" @@ -630,8 +635,9 @@ def test_sort_orderable_and_unorderable_values(self): def test_str_wrap(self): # pprint tries to wrap strings intelligently fox = "the quick brown fox jumped over a lazy dog" - assert FancyPrinter(width=19 - ).pformat(fox) == """\ + assert FancyPrinter( + width=19, + ).pformat(fox) == """\ ('the quick brown ' 'fox jumped over ' 'a lazy dog')""" @@ -651,13 +657,13 @@ def test_str_wrap(self): special = "Portons dix bons \"whiskys\"\nΓ  l'avocat goujat\t qui fumait au zoo" assert FancyPrinter(width=68).pformat(special) == repr(special) assert FancyPrinter(width=31).pformat( - special + special, ) == """\ ('Portons dix bons "whiskys"\\n' "Γ  l'avocat goujat\\t qui " 'fumait au zoo')""" assert FancyPrinter(width=20).pformat( - special + special, ) == """\ ('Portons dix bons ' '"whiskys"\\n' @@ -731,7 +737,7 @@ def test_compact(self): 14, 15], [], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]""" - assert FancyPrinter(width=47, compact=True).pformat(o, ) == expected + assert FancyPrinter(width=47, compact=True).pformat(o) == expected def test_compact_width(self): levels = 20 @@ -740,7 +746,7 @@ def test_compact_width(self): for i in range(levels - 1): 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() + lines = FancyPrinter(width=w, compact=True).pformat(o).splitlines() maxwidth = max(map(len, lines)) assert maxwidth <= w maxwidth > w - 3 # pylint: disable=pointless-statement @@ -764,31 +770,31 @@ def test_bytes_wrap(self): special = bytes(range(16)) assert FancyPrinter(width=61).pformat(special) == repr(special) assert FancyPrinter(width=48).pformat( - special + special, ) == """\ (b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b' b'\\x0c\\r\\x0e\\x0f')""" assert FancyPrinter(width=32).pformat( - special + special, ) == """\ (b'\\x00\\x01\\x02\\x03' b'\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b' b'\\x0c\\r\\x0e\\x0f')""" assert FancyPrinter(width=1).pformat( - special + special, ) == """\ (b'\\x00\\x01\\x02\\x03' b'\\x04\\x05\\x06\\x07' b'\\x08\\t\\n\\x0b' b'\\x0c\\r\\x0e\\x0f')""" - assert FancyPrinter(width=21).pformat({'a': 1, 'b': letters, 'c': 2} == """\ + assert FancyPrinter(width=21).pformat({'a': 1, 'b': letters, 'c': 2}) == """\ { 'a': 1, 'b': b'abcdefghijkl' b'mnopqrstuvwx' b'yz', 'c': 2, - }""") + }""" assert FancyPrinter(width=20).pformat({'a': 1, 'b': letters, 'c': 2}) == """\ { 'a': 1, @@ -852,9 +858,9 @@ def test_bytes_wrap(self): ), pytest.param([[[[[bytearray(b"abcdefghijklmnopqrstuvwxyz")]]]]], 37, id="case_11"), pytest.param([[[[[bytearray(range(16))]]]]], 50, id="case_12"), - ] + ], ) - def test_bytearray_wrap(self, value, width, advanced_file_regression: AdvancedFileRegressionFixture): + def test_bytearray_wrap(self, value, width: int, advanced_file_regression: AdvancedFileRegressionFixture): advanced_file_regression.check(FancyPrinter(width=width).pformat(value)) def test_default_dict(self, advanced_file_regression: AdvancedFileRegressionFixture): @@ -892,7 +898,7 @@ def test_deque(self): words = "the quick brown fox jumped over a lazy dog".split() d = collections.deque(zip(words, itertools.count())) assert FancyPrinter().pformat( - d + d, ) == """\ deque([('the', 0), ('quick', 1), @@ -905,7 +911,7 @@ def test_deque(self): ('dog', 8)])""" d = collections.deque(zip(words, itertools.count()), maxlen=7) assert FancyPrinter().pformat( - d + d, ) == """\ deque([('brown', 2), ('fox', 3), @@ -930,6 +936,12 @@ def test_user_list(self, advanced_file_regression: AdvancedFileRegressionFixture d = collections.UserList(zip(words, itertools.count())) advanced_file_regression.check(FancyPrinter().pformat(d)) + _sl_str_param = StringList([ + "('the quick brown '", + " 'fox jumped over '", + " 'a lazy dog')", + ]) + @pytest.mark.parametrize( "value, width, expects", [ @@ -937,27 +949,25 @@ def test_user_list(self, advanced_file_regression: AdvancedFileRegressionFixture ( collections.UserString("the quick brown fox jumped over a lazy dog"), 20, - str(StringList([ - "('the quick brown '", - " 'fox jumped over '", - " 'a lazy dog')", - ])) + str(_sl_str_param), + ), + ( + {1: collections.UserString("the quick brown fox jumped over a lazy dog")}, + 20, + str( + StringList([ + '{', + " 1: 'the quick '", + " 'brown fox '", + " 'jumped over a '", + " 'lazy dog',", + " }", + ]), + ), ), - ({1: collections.UserString("the quick brown fox jumped over a lazy dog")}, - 20, - str( - StringList([ - '{', - " 1: 'the quick '", - " 'brown fox '", - " 'jumped over a '", - " 'lazy dog',", - " }" - ]) - )), - ] + ], ) - def test_user_string(self, value, width, expects): + def test_user_string(self, value: Any, width: int, expects: str): assert FancyPrinter(width=width).pformat(value) == expects diff --git a/tests/test_stringlist.py b/tests/test_stringlist.py index f129c24..1700b52 100644 --- a/tests/test_stringlist.py +++ b/tests/test_stringlist.py @@ -2,7 +2,7 @@ import pickle import textwrap from textwrap import dedent -from typing import no_type_check +from typing import List, Tuple, no_type_check # 3rd party import pytest @@ -64,7 +64,22 @@ def test_insert(self): sl.insert(100, "end") assert sl == [ - "1234", "foo", "bar", '', "a line", '', '', '', "hello", "world", '', '', '', "1234", "baz", "end" + "1234", + "foo", + "bar", + '', + "a line", + '', + '', + '', + "hello", + "world", + '', + '', + '', + "1234", + "baz", + "end", ] def test_setitem(self): @@ -215,7 +230,7 @@ def bar(self, listicle: List[Item]): def __repr__(self) -> str: return "Foo()" - """ + """, ) sl = StringList() @@ -551,17 +566,17 @@ def test_delimitedlist(): ("abc\ndef\r\nghi\n\r", [("abc", '\n'), ("def", "\r\n"), ("ghi", '\n'), ('', '\r')]), ("\nabc\ndef\r\nghi\n\r", [('', '\n'), ("abc", '\n'), ("def", "\r\n"), ("ghi", '\n'), ('', '\r')]), ("abcdef", [("abcdef", '')]), - ] + ], ) @joinlines_splitlines_param -def test_splitlines(string, lines): +def test_splitlines(string: str, lines: List[str]): assert splitlines(string) == lines @joinlines_splitlines_param -def test_joinlines(string, lines): +def test_joinlines(string: str, lines: List[Tuple[str, str]]): assert string == joinlines(lines) diff --git a/tests/test_typing.py b/tests/test_typing.py index 2c2d089..6815d52 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -9,7 +9,7 @@ # stdlib import os import pathlib -from typing import Dict, List, Sequence, Set, Tuple, Union +from typing import Any, Dict, List, Sequence, Set, Tuple, Union # 3rd party import pytest @@ -35,7 +35,7 @@ (12.34, Dict[int, float]), ], ) -def test_check_membership_true(obj, type_): +def test_check_membership_true(obj: Any, type_): # todo: Positions for Tuple and Dict assert check_membership(obj, type_) @@ -57,19 +57,19 @@ def test_check_membership_true(obj, type_): (12.34, Dict[int, str]), ], ) -def test_check_membership_false(obj, type_): +def test_check_membership_false(obj: Any, type_): # todo: Positions for Tuple and Dict assert not check_membership(obj, type_) class MyPathLike(os.PathLike): - def __init__(self, directory, filename): + def __init__(self, directory: PathLike, filename: PathLike): self.directory = str(directory) self.filename = str(filename) - def __fspath__(self): - os.path.join(self.directory, self.filename) + def __fspath__(self) -> str: + return os.path.join(self.directory, self.filename) class MyStr(str): @@ -97,19 +97,20 @@ class MyPath(type(pathlib.Path())): # type: ignore[misc] MyPathLike('.', "test_typing.py"), ], ) -def test_pathlike_true(obj): +def test_pathlike_true(obj: PathLike): assert check_membership(obj, PathLike) @pytest.mark.parametrize( - "obj", [ + "obj", + [ 1234, 12.34, [1, 2, 3, 4, 5], {1, 2, 3, 4, 5}, (1, 2, 3, 4, 5), {'a': 1, 'b': 2}, - ] + ], ) -def test_pathlike_false(obj): +def test_pathlike_false(obj: Any): assert not check_membership(obj, PathLike) diff --git a/tests/test_utils.py b/tests/test_utils.py index 08809e2..2cd0498 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -13,6 +13,7 @@ import re import sys from collections import namedtuple +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union # 3rd party import click @@ -60,7 +61,7 @@ class TestList2Str: (['a', 2, pathlib.Path("foo.txt")], "a,2,foo.txt"), ], ) - def test_list2str(self, value, expects): + def test_list2str(self, value: Any, expects: str): str_representation = list2str(value) assert isinstance(str_representation, str) assert str_representation == expects @@ -74,7 +75,7 @@ def test_list2str(self, value, expects): (['a', 2, pathlib.Path("foo.txt")], "a;2;foo.txt"), ], ) - def test_list2str_semicolon(self, value, expects): + def test_list2str_semicolon(self, value: Any, expects: str): str_representation = list2str(value, sep=';') assert isinstance(str_representation, str) assert str_representation == expects @@ -85,7 +86,7 @@ class CustomRepr: def __init__(self): pass - def __repr__(self): + def __repr__(self) -> str: return "This is my custom __repr__!" @@ -98,7 +99,7 @@ def __init__(self): no_repr_instance = NoRepr() -def get_mem_addr(obj): +def get_mem_addr(obj: object): if sys.platform == "win32" and platform.python_implementation() != "PyPy": return f"0x0*{hex(id(obj))[2:].upper()}" else: @@ -116,7 +117,7 @@ def get_mem_addr(obj): (no_repr_instance, f""), ], ) -def test_printr(obj, expects, capsys): +def test_printr(obj: Any, expects: str, capsys): printr(obj) captured = capsys.readouterr() @@ -141,7 +142,7 @@ def test_printr(obj, expects, capsys): (no_repr_instance, ""), ], ) -def test_printt(obj, expects, capsys): +def test_printt(obj: Any, expects: str, capsys): printt(obj) captured = capsys.readouterr() @@ -160,7 +161,7 @@ def test_printt(obj, expects, capsys): (no_repr_instance, f""), ], ) -def test_stderr_writer(obj, expects, capsys): +def test_stderr_writer(obj: Any, expects: str, capsys): stderr_writer(obj) captured = capsys.readouterr() @@ -188,7 +189,7 @@ def test_str2tuple(self, value, expects): ("1; 2; 3", (1, 2, 3)), # tests with semicolon ], ) - def test_str2tuple_semicolon(self, value, expects): + def test_str2tuple_semicolon(self, value: str, expects: Tuple[int, ...]): assert isinstance(str2tuple(value, sep=';'), tuple) assert str2tuple(value, sep=';') == expects @@ -196,7 +197,7 @@ def test_str2tuple_semicolon(self, value, expects): class TestStrToBool: @coincidence.testing_boolean_values(extra_truthy=[50, -1]) - def test_strtobool(self, boolean_string, expected_boolean): + def test_strtobool(self, boolean_string, expected_boolean: bool): assert strtobool(boolean_string) == expected_boolean @pytest.mark.parametrize( @@ -210,7 +211,7 @@ def test_strtobool(self, boolean_string, expected_boolean): (0.0, AttributeError), ], ) - def test_strtobool_errors(self, obj, expects): + def test_strtobool_errors(self, obj: Any, expects: Type[Exception]): with pytest.raises(expects): strtobool(obj) @@ -236,7 +237,7 @@ def test_strtobool_errors(self, obj, expects): ("Hello World", "'Hello World'"), ], ) -def test_enquote_value(obj, expects): +def test_enquote_value(obj: Any, expects: Any): assert enquote_value(obj) == expects @@ -268,7 +269,7 @@ def test_cmp(): assert cmp(20, 20) == 0 -def demo_function(arg1, arg2, arg3): +def demo_function(arg1, arg2, arg3): # noqa: MAN001,MAN002 pass @@ -281,29 +282,45 @@ def demo_function(arg1, arg2, arg3): ((1, 2, 3), ("arg1", "arg2", "arg3"), {}, {"arg1": 1, "arg2": 2, "arg3": 3}), ((1, 2, 3), ("arg1", "arg2", "arg3"), None, {"arg1": 1, "arg2": 2, "arg3": 3}), ((1, 2, 3), ("arg1", "arg2", "arg3"), {"arg4": 4}, {"arg1": 1, "arg2": 2, "arg3": 3, "arg4": 4}), - ((1, 2, 3), demo_function, None, { - "arg1": 1, - "arg2": 2, - "arg3": 3, - }), - ((cwd, "wb", -1, "UTF-8"), - pathlib.Path.open, - None, { - "self": cwd, - "mode": "wb", - "buffering": -1, - "encoding": "UTF-8", - }), - (("wb", -1, "UTF-8"), - pathlib.Path().open, - None, { - "mode": "wb", - "buffering": -1, - "encoding": "UTF-8", - }), - ] + ( + (1, 2, 3), + demo_function, + None, + { + "arg1": 1, + "arg2": 2, + "arg3": 3, + }, + ), + ( + (cwd, "wb", -1, "UTF-8"), + pathlib.Path.open, + None, + { + "self": cwd, + "mode": "wb", + "buffering": -1, + "encoding": "UTF-8", + }, + ), + ( + ("wb", -1, "UTF-8"), + pathlib.Path().open, + None, + { + "mode": "wb", + "buffering": -1, + "encoding": "UTF-8", + }, + ), + ], ) -def test_posargs2kwargs(args, posarg_names, kwargs, expects): +def test_posargs2kwargs( + args: Sequence, + posarg_names: Union[str, Callable], + kwargs: Optional[Dict[str, Any]], + expects: Dict[str, Any], + ): assert posargs2kwargs(args, posarg_names, kwargs) == expects @@ -351,41 +368,43 @@ def test_protocol_pandas(self): @pytest.mark.parametrize( "args, expects", [ - ((foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), ), - "foo(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, ...)"), - ((foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), 13), - "foo(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11, l=12, m=13)"), - ] + ( + (foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), ), + "foo(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, ...)", + ), + ( + (foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), 13), + "foo(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11, l=12, m=13)", + ), + ], ) - def test_namedtuple(self, args, expects): + def test_namedtuple(self, args: Sequence, expects: str): assert head(*args) == expects @pytest.mark.parametrize( "args, expects", [ (((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), ), "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)"), - (( - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), - 13, + ( + ((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), 13), + "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)", ), - "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)"), - ] + ], ) - def test_tuple(self, args, expects): + def test_tuple(self, args: Sequence, expects: str): assert head(*args) == expects @pytest.mark.parametrize( "args, expects", [ (([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ), "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"), - (( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], - 13, + ( + ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 13), + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]", ), - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]"), - ] + ], ) - def test_list(self, args, expects): + def test_list(self, args: Sequence, expects: str): assert head(*args) == expects def test_data_frame(self): @@ -396,7 +415,7 @@ def test_data_frame(self): columns=["Name", "Age", "Occupation"], ) assert head( - df + df, ) == """ Name Age Occupation 0 Bob 20 Apprentice 1 Alice 23 Secretary @@ -451,7 +470,7 @@ def test_trim_precision(): ("don't", "\"don't\""), ("Here's a single quote \"", "\"Here's a single quote \\\"\""), (enquote_value('β˜ƒ'), "\"'β˜ƒ'\""), - ] + ], ) def test_double_repr_string(value: str, expects: str): assert double_repr_string(value) == expects @@ -488,7 +507,7 @@ def test_redirect_output_combine(): ("hello = world", " = "), ("hello: world", ':'), ("hello: world", ": "), - ] + ], ) def test_divide(string: str, sep: str, advanced_data_regression: AdvancedDataRegressionFixture): data = dict(divide(e, sep) for e in [string, string, string]) @@ -509,7 +528,7 @@ def test_divide_errors(): ("hello = world", '='), ("hello: world", r":\s?"), ("hello: world", r"\s?:\s?"), - ] + ], ) def test_redivide(string: str, sep: str, advanced_data_regression: AdvancedDataRegressionFixture): data = dict(redivide(e, sep) for e in [string, string, string]) @@ -530,9 +549,9 @@ def test_redivide_errors(): (("foo", "bar"), ["bar", "foo"]), (("foo", "foo", "bar"), ["bar", "foo"]), (("foo", "bar", "bar"), ["bar", "foo"]), - ] + ], ) -def test_unique_sorted(values, expected): +def test_unique_sorted(values: Sequence[str], expected: List[str]): assert unique_sorted(values) == expected @@ -545,7 +564,7 @@ def test_unique_sorted(values, expected): ("\b\u000b", "^H^K"), ("\u001a", "^Z^?"), ('\x81', "M+A"), - ] + ], ) def test_replace_nonprinting(the_string: str, expected: str): assert replace_nonprinting(the_string) == expected diff --git a/tests/test_versions.py b/tests/test_versions.py index d0b0b4c..96a33ff 100644 --- a/tests/test_versions.py +++ b/tests/test_versions.py @@ -3,7 +3,7 @@ import pickle import platform import sys -from typing import Any +from typing import Any, Sequence, Tuple # 3rd party import pytest @@ -28,7 +28,7 @@ ("15", (15, )), ], ) -def test_iter_string(string, expects): +def test_iter_string(string: str, expects: Tuple[int, ...]): assert tuple(_iter_string(string)) == expects @@ -47,7 +47,7 @@ def test_iter_string(string, expects): ("15", (15, )), ], ) -def test_iter_float(float_, expects): +def test_iter_float(float_: float, expects: Version): assert tuple(_iter_float(float_)) == expects @@ -70,39 +70,31 @@ def test_iter_float(float_, expects): (('1', '5', '1'), (1, 5, 1)), (('1', '5', '1', '2', '3', '4', '5'), (1, 5, 1, 2, 3, 4, 5)), (("15", ), (15, )), - ([ - '1', - ], (1, )), + (['1'], (1, )), (['1', '0'], (1, 0)), (['1', '5'], (1, 5)), (['1', '5', '1'], (1, 5, 1)), (['1', '5', '1', '2', '3', '4', '5'], (1, 5, 1, 2, 3, 4, 5)), - ([ - "15", - ], (15, )), + (["15"], (15, )), ((1, ), (1, )), ((1, 0), (1, 0)), ((1, 5), (1, 5)), ((1, 5, 1), (1, 5, 1)), ((1, 5, 1, 2, 3, 4, 5), (1, 5, 1, 2, 3, 4, 5)), ((15, ), (15, )), - ([ - 1, - ], (1, )), + ([1], (1, )), ([1, 0], (1, 0)), ([1, 5], (1, 5)), ([1, 5, 1], (1, 5, 1)), ([1, 5, 1, 2, 3, 4, 5], (1, 5, 1, 2, 3, 4, 5)), - ([ - 15, - ], (15, )), + ([15], (15, )), (Version(1, 2, 3), (1, 2, 3)), (Version(1, 2), (1, 2, 0)), (Version(1), (1, 0, 0)), ], ) -def test_prep_for_eq(other, expects): - assert _prep_for_eq(other) == expects +def test_prep_for_eq(other: Sequence, expects: Tuple[int, ...]): + assert _prep_for_eq(other) == expects # type: ignore[arg-type] @pytest.mark.parametrize( @@ -115,7 +107,7 @@ def test_prep_for_eq(other, expects): (2.0, Version(2)), ], ) -def test_from_float(float_, expects): +def test_from_float(float_: float, expects: Version): assert Version.from_float(float_) == expects @@ -133,7 +125,7 @@ def test_from_float(float_, expects): ("1.5.2", Version(1, 5, 2)), ], ) -def test_from_str(string, expects): +def test_from_str(string: str, expects: Version): assert Version.from_str(string) == expects @@ -145,33 +137,25 @@ def test_from_str(string, expects): (('1', '5'), Version(1, 5)), (('1', '5', '1'), Version(1, 5, 1)), (("15", ), Version(15)), - ([ - '1', - ], Version(1)), + (['1'], Version(1)), (['1', '0'], Version(1, 0)), (['1', '5'], Version(1, 5)), (['1', '5', '1'], Version(1, 5, 1)), - ([ - "15", - ], Version(15)), + (["15"], Version(15)), ((1, ), Version(1)), ((1, 0), Version(1, 0)), ((1, 5), Version(1, 5)), ((1, 5, 1), Version(1, 5, 1)), ((15, ), Version(15)), - ([ - 1, - ], Version(1)), + ([1], Version(1)), ([1, 0], Version(1, 0)), ([1, 5], Version(1, 5)), ([1, 5, 1], Version(1, 5, 1)), - ([ - 15, - ], Version(15)), + ([15], Version(15)), ], ) -def test_from_tuple(tuple_, expects): - assert Version.from_tuple(tuple_) == expects +def test_from_tuple(tuple_: Sequence, expects: Version): + assert Version.from_tuple(tuple_) == expects # type: ignore[arg-type] def test_too_many_values(): @@ -199,29 +183,21 @@ def test_too_many_values(): (('1', '5'), Version(1, 5)), (('1', '5', '1'), Version(1, 5, 1)), (("15", ), Version(15)), - ([ - '1', - ], Version(1)), + (['1'], Version(1)), (['1', '0'], Version(1, 0)), (['1', '5'], Version(1, 5)), (['1', '5', '1'], Version(1, 5, 1)), - ([ - "15", - ], Version(15)), + (["15"], Version(15)), ((1, ), Version(1)), ((1, 0), Version(1, 0)), ((1, 5), Version(1, 5)), ((1, 5, 1), Version(1, 5, 1)), ((15, ), Version(15)), - ([ - 1, - ], Version(1)), + ([1], Version(1)), ([1, 0], Version(1, 0)), ([1, 5], Version(1, 5)), ([1, 5, 1], Version(1, 5, 1)), - ([ - 15, - ], Version(15)), + ([15], Version(15)), ('1', Version(1)), ("1.0", Version(1, 0)), ("1.5", Version(1, 5)), @@ -235,7 +211,7 @@ def test_too_many_values(): (15, Version(15)), ], ) -def test_equals(value, version): +def test_equals(value: Any, version: Version): assert version == value assert value == version @@ -279,14 +255,12 @@ def test_lt(value: Any, version: Version): ((1, ), Version(1.1)), ((0, 9), Version(1)), ((0, 8, 8), Version(0, 9)), - ([ - 1, - ], Version(1.1)), + ([1], Version(1.1)), ([0, 9], Version(1)), ([0, 8, 8], Version(0, 9)), ], ) -def test_gt(value, version): +def test_gt(value: Any, version: Version): assert value < version assert version > value @@ -315,29 +289,21 @@ def test_gt(value, version): (('1', '5'), Version(1, 5)), (('1', '5', '1'), Version(1, 5, 1)), (("15", ), Version(15)), - ([ - '1', - ], Version(1)), + (['1'], Version(1)), (['1', '0'], Version(1, 0)), (['1', '5'], Version(1, 5)), (['1', '5', '1'], Version(1, 5, 1)), - ([ - "15", - ], Version(15)), + (["15"], Version(15)), ((1, ), Version(1)), ((1, 0), Version(1, 0)), ((1, 5), Version(1, 5)), ((1, 5, 1), Version(1, 5, 1)), ((15, ), Version(15)), - ([ - 1, - ], Version(1)), + ([1], Version(1)), ([1, 0], Version(1, 0)), ([1, 5], Version(1, 5)), ([1, 5, 1], Version(1, 5, 1)), - ([ - 15, - ], Version(15)), + ([15], Version(15)), ('1', Version(1)), ("1.0", Version(1, 0)), ("1.5", Version(1, 5)), @@ -351,7 +317,7 @@ def test_gt(value, version): ("1.5.1.2.3.4.5", Version(1, 5, 1)), ], ) -def test_le(value, version): +def test_le(value: Any, version: Version): assert value >= version assert version <= value @@ -369,9 +335,7 @@ def test_le(value, version): ((1, ), Version(1.1)), ((0, 9), Version(1)), ((0, 8, 8), Version(0, 9)), - ([ - 1, - ], Version(1.1)), + ([1], Version(1.1)), ([0, 9], Version(1)), ([0, 8, 8], Version(0, 9)), (('1', ), Version(1)), @@ -379,29 +343,21 @@ def test_le(value, version): (('1', '5'), Version(1, 5)), (('1', '5', '1'), Version(1, 5, 1)), (("15", ), Version(15)), - ([ - '1', - ], Version(1)), + (['1'], Version(1)), (['1', '0'], Version(1, 0)), (['1', '5'], Version(1, 5)), (['1', '5', '1'], Version(1, 5, 1)), - ([ - "15", - ], Version(15)), + (["15"], Version(15)), ((1, ), Version(1)), ((1, 0), Version(1, 0)), ((1, 5), Version(1, 5)), ((1, 5, 1), Version(1, 5, 1)), ((15, ), Version(15)), - ([ - 1, - ], Version(1)), + ([1], Version(1)), ([1, 0], Version(1, 0)), ([1, 5], Version(1, 5)), ([1, 5, 1], Version(1, 5, 1)), - ([ - 15, - ], Version(15)), + ([15], Version(15)), ('1', Version(1)), ("1.0", Version(1, 0)), ("1.5", Version(1, 5)), @@ -415,7 +371,7 @@ def test_le(value, version): ("1.5.0.2.3.4.5", Version(1, 5, 1)), ], ) -def test_ge(value, version): +def test_ge(value: Any, version: Version): assert value <= version assert version >= value @@ -431,7 +387,7 @@ def test_ge(value, version): (Version(minor=3, patch=4), "Version(major=0, minor=3, patch=4)"), ], ) -def test_repr(version, expects): +def test_repr(version: Version, expects: Any): assert repr(version) == expects @@ -446,7 +402,7 @@ def test_repr(version, expects): (Version(minor=3, patch=4), "v0.3.4"), ], ) -def test_str(version, expects): +def test_str(version: Version, expects: Any): assert str(version) == expects @@ -461,7 +417,7 @@ def test_str(version, expects): (Version(minor=3, patch=4), 0.3), ], ) -def test_float(version, expects): +def test_float(version: Version, expects: Any): assert float(version) == expects @@ -476,7 +432,7 @@ def test_float(version, expects): (Version(minor=3, patch=4), 0), ], ) -def test_int(version, expects): +def test_int(version: Version, expects: Any): assert int(version) == expects @@ -492,7 +448,7 @@ def test_int(version, expects): Version(1, 2, 3), ], ) -def test_pickle(obj): +def test_pickle(obj: Version): assert pickle.loads(pickle.dumps(obj)) == obj # nosec: B301 @@ -508,5 +464,5 @@ def test_pickle(obj): Version(1, 2, 3), ], ) -def test_copy(obj): +def test_copy(obj: Version): assert copy.copy(obj) == obj diff --git a/tests/test_words.py b/tests/test_words.py index 9e17bee..31583d5 100644 --- a/tests/test_words.py +++ b/tests/test_words.py @@ -3,7 +3,7 @@ import pathlib import random import string -from typing import List +from typing import Any, Dict, List, Sequence # 3rd party import pytest @@ -116,7 +116,7 @@ def test_alpha_sort(): (decimal.Decimal("1234"), "1234"), ], ) -def test_as_text(value, expects): +def test_as_text(value: Any, expects: str): assert words.as_text(value) == expects @@ -143,17 +143,23 @@ def test_as_text(value, expects): ((["bob"], ), {"use_repr": True, "oxford": True}, "'bob'"), ((["bob", "alice"], ), {"use_repr": True, "oxford": True}, "'bob' and 'alice'"), ((["bob", "alice", "fred"], ), {"use_repr": True, "oxford": True}, "'bob', 'alice', and 'fred'"), - ((["bob", "alice", "fred"], ), {"use_repr": True, "oxford": True, "delimiter": ';'}, - "'bob'; 'alice'; and 'fred'"), - ((["bob", "alice", "fred"], ), {"use_repr": True, "oxford": True, "connective": "or"}, - "'bob', 'alice', or 'fred'"), + ( + (["bob", "alice", "fred"], ), + {"use_repr": True, "oxford": True, "delimiter": ';'}, + "'bob'; 'alice'; and 'fred'", + ), + ( + (["bob", "alice", "fred"], ), + {"use_repr": True, "oxford": True, "connective": "or"}, + "'bob', 'alice', or 'fred'", + ), ((["bob", "alice"], ), {"connective": "or"}, "bob or alice"), ((("bob", ), ), {"use_repr": True, "oxford": True}, "'bob'"), ((("bob", "alice"), ), {"use_repr": True, "oxford": True}, "'bob' and 'alice'"), ((("bob", "alice", "fred"), ), {"use_repr": True, "oxford": True}, "'bob', 'alice', and 'fred'"), ], ) -def test_word_join(args, kwargs, expects): +def test_word_join(args: Sequence, kwargs: Dict[str, Any], expects: str): assert words.word_join(*args, **kwargs) == expects @@ -182,7 +188,8 @@ def test_pluralphrase(): phrase2 = PluralPhrase("The farmer has {n} {0}.", (Plural("cow", "cows"), )) phrase3 = PluralPhrase("The proposed {1} {0} to ...", (Plural("is", "are"), Plural("change", "changes"))) phrase4 = PluralPhrase( - "The farmer has {n} {0}. The {0} {1} brown.", (Plural("cow", "cows"), Plural("is", "are")) + "The farmer has {n} {0}. The {0} {1} brown.", + (Plural("cow", "cows"), Plural("is", "are")), ) n = 1 assert phrase1(n) == "The proposed change is to ..." diff --git a/tox.ini b/tox.ini index 3422d70..1d85712 100644 --- a/tox.ini +++ b/tox.ini @@ -203,12 +203,14 @@ rst-roles = inline-code manpage per-file-ignores = - tests/*: D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 - */*.pyi: E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 - tests/list_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 - tests/seq_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 - tests/test_paths_stdlib.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 - tests/test_pretty_print.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 + tests/list_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + tests/seq_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + tests/test_doctools.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + tests/test_paths_stdlib.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 PRM002 PRM003 + tests/test_pretty_print.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + tests/test_getters.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + domdf_python_tools/pretty_print.py: MAN001 MAN002 + domdf_python_tools/compat/importlib_resources.py: PRM002 pytest-parametrize-names-type = csv inline-quotes = " multiline-quotes = """ From 37ab6ac0bcf207eff890de9438d85009749fa0d1 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:04:02 +0000 Subject: [PATCH 19/33] Updated files with 'repo_helper'. (#144) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/conda_ci.yml | 5 ++--- .github/workflows/docs_test_action.yml | 5 ++--- .github/workflows/flake8.yml | 5 ++--- .github/workflows/mypy.yml | 5 ++--- .github/workflows/octocheese.yml | 5 ++--- .github/workflows/python_ci.yml | 8 +++---- .github/workflows/python_ci_linux.yml | 20 +++++++++++------ .github/workflows/python_ci_macos.yml | 8 +++---- .gitignore | 1 - .pre-commit-config.yaml | 7 +++--- .style.yapf | 2 +- README.rst | 2 +- doc-source/index.rst | 2 +- formate.toml | 31 +++++--------------------- justfile | 3 +++ tox.ini | 14 +++++++----- 16 files changed, 54 insertions(+), 69 deletions(-) diff --git a/.github/workflows/conda_ci.yml b/.github/workflows/conda_ci.yml index 57ba7b5..3f83e5f 100644 --- a/.github/workflows/conda_ci.yml +++ b/.github/workflows/conda_ci.yml @@ -6,12 +6,11 @@ on: push: branches: ["master"] -permissions: - contents: read - jobs: tests: name: "Conda" + permissions: + contents: read runs-on: ubuntu-22.04 defaults: run: diff --git a/.github/workflows/docs_test_action.yml b/.github/workflows/docs_test_action.yml index 761b41f..39ae450 100644 --- a/.github/workflows/docs_test_action.yml +++ b/.github/workflows/docs_test_action.yml @@ -9,11 +9,10 @@ on: - 'imgbot' pull_request: -permissions: - contents: read - jobs: docs: + permissions: + contents: read runs-on: ubuntu-latest steps: - name: Checkout πŸ›ŽοΈ diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 5e67c5c..1ecb7b9 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -10,12 +10,11 @@ on: - 'imgbot' pull_request: -permissions: - contents: read - jobs: Run: name: "Flake8" + permissions: + contents: read runs-on: "ubuntu-22.04" steps: diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 4c22a52..b889816 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -10,11 +10,10 @@ on: - 'imgbot' pull_request: -permissions: - contents: read - jobs: Run: + permissions: + contents: read name: "mypy / ${{ matrix.os }}" runs-on: ${{ matrix.os }} diff --git a/.github/workflows/octocheese.yml b/.github/workflows/octocheese.yml index a5b3f19..67e2bbb 100644 --- a/.github/workflows/octocheese.yml +++ b/.github/workflows/octocheese.yml @@ -6,11 +6,10 @@ on: schedule: - cron: 0 12 * * * -permissions: - contents: write - jobs: Run: + permissions: + contents: write runs-on: ubuntu-latest steps: - uses: domdfcoding/octocheese@master diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index a1e0828..b545fee 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -11,13 +11,11 @@ on: pull_request: -permissions: - actions: write - issues: write - contents: read - jobs: tests: + permissions: + actions: write + contents: read name: "windows-2022 / Python ${{ matrix.config.python-version }}" runs-on: "windows-2022" continue-on-error: ${{ matrix.config.experimental }} diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 4cc812e..7e8493e 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -12,13 +12,11 @@ on: - '*' pull_request: -permissions: - actions: write - issues: write - contents: read - jobs: tests: + permissions: + actions: write + contents: read name: "ubuntu-22.04 / Python ${{ matrix.config.python-version }}" runs-on: "ubuntu-22.04" continue-on-error: ${{ matrix.config.experimental }} @@ -87,6 +85,9 @@ jobs: Coverage: needs: tests + permissions: + actions: write + contents: read runs-on: "ubuntu-22.04" steps: - name: Checkout πŸ›ŽοΈ @@ -136,7 +137,10 @@ jobs: Deploy: needs: tests - + permissions: + actions: write + issues: write + contents: read runs-on: "ubuntu-22.04" steps: - name: Checkout πŸ›ŽοΈ @@ -167,7 +171,7 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} - skip_existing: true + skip-existing: true - name: Close milestone πŸšͺ if: startsWith(github.ref, 'refs/tags/') @@ -181,6 +185,8 @@ jobs: Conda: needs: deploy + permissions: + contents: read runs-on: ubuntu-22.04 if: startsWith(github.ref, 'refs/tags/') || (startsWith(github.event.head_commit.message, 'Bump version') != true) steps: diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index d52b858..7cfd5dc 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -11,13 +11,11 @@ on: pull_request: -permissions: - actions: write - issues: write - contents: read - jobs: tests: + permissions: + actions: write + contents: read name: "macos-${{ matrix.config.os-ver }} / Python ${{ matrix.config.python-version }}" runs-on: "macos-${{ matrix.config.os-ver }}" continue-on-error: ${{ matrix.config.experimental }} diff --git a/.gitignore b/.gitignore index b340122..ce4c4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ docs/_build/ doc/build target/ .ipynb_checkpoints -.python-version celerybeat-schedule celerybeat.pid *.sage.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 973fdef..0e10275 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.13.0 + rev: v0.14.0 hooks: - id: reformat-pyproject @@ -18,7 +18,6 @@ repos: - id: check-added-large-files - id: check-ast - id: fix-byte-order-marker - - id: check-byte-order-marker - id: check-case-conflict - id: check-executables-have-shebangs - id: check-json @@ -85,9 +84,11 @@ repos: hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ + additional_dependencies: + - formate-trailing-commas>=0.1.0 - repo: https://github.com/python-coincidence/dep_checker - rev: v0.8.0 + rev: v0.9.0 hooks: - id: dep_checker args: diff --git a/.style.yapf b/.style.yapf index e64be7c..9378f04 100644 --- a/.style.yapf +++ b/.style.yapf @@ -312,7 +312,7 @@ split_before_dict_set_generator=True # # foo = ('This is a really long string: {}, {}, {}, {}' # .format(a, b, c, d)) -split_before_dot=False +split_before_dot=True # Split after the opening paren which surrounds an expression if it doesn't # fit on a single line. diff --git a/README.rst b/README.rst index 641fd16..8849702 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,7 @@ domdf_python_tools :target: https://github.com/domdfcoding/domdf_python_tools/commit/master :alt: GitHub last commit -.. |maintained| image:: https://img.shields.io/maintenance/yes/2025 +.. |maintained| image:: https://img.shields.io/maintenance/yes/2026 :alt: Maintenance .. |pypi-downloads| image:: https://img.shields.io/pypi/dm/domdf_python_tools diff --git a/doc-source/index.rst b/doc-source/index.rst index 12f8540..9263acb 100644 --- a/doc-source/index.rst +++ b/doc-source/index.rst @@ -115,7 +115,7 @@ domdf_python_tools :last-commit: :alt: GitHub last commit - .. |maintained| maintained-shield:: 2025 + .. |maintained| maintained-shield:: 2026 :alt: Maintenance .. |pypi-downloads| pypi-shield:: diff --git a/formate.toml b/formate.toml index 95093bc..5893251 100644 --- a/formate.toml +++ b/formate.toml @@ -12,6 +12,12 @@ priority = 30 [hooks.yapf.kwargs] yapf_style = ".style.yapf" +[hooks.trailing_commas] +priority = 21 + +[hooks.trailing_commas.kwargs] +format_ImportFrom = false + [hooks.isort] priority = 50 @@ -27,31 +33,6 @@ lines_between_types = 0 use_parentheses = true remove_redundant_aliases = true default_section = "THIRDPARTY" -known_third_party = [ - "click", - "coincidence", - "consolekit", - "coverage", - "coverage_pyver_pragma", - "faker", - "flake8", - "funcy", - "github", - "importlib_metadata", - "importlib_resources", - "natsort", - "pandas", - "pydash", - "pytest", - "pytest_cov", - "pytest_randomly", - "pytest_regressions", - "pytest_rerunfailures", - "pytest_timeout", - "pytz", - "requests", - "typing_extensions", -] known_first_party = [ "domdf_python_tools",] [config] diff --git a/justfile b/justfile index e8ed871..6a4d335 100644 --- a/justfile +++ b/justfile @@ -12,6 +12,9 @@ unused-imports: incomplete-defs: tox -e lint -- --select MAN +commas: + tox -e lint -- --select C812,C813,C814,C815,C816 + vdiff: git diff $(repo-helper show version -q)..HEAD diff --git a/tox.ini b/tox.ini index 1d85712..1267903 100644 --- a/tox.ini +++ b/tox.ini @@ -118,7 +118,7 @@ deps = flake8-dunder-all>=0.1.1 flake8-encodings>=0.1.0 flake8-github-actions>=0.1.0 - flake8-noqa>=1.1.0,<=1.2.2 + git+https://github.com/python-formate/flake8-noqa.git@v1.2.2-python-formate.0 flake8-pyi>=20.10.0,<=22.8.0 flake8-pytest-style>=1.3.0,<2 flake8-quotes>=3.3.0 @@ -126,6 +126,8 @@ deps = flake8-sphinx-links>=0.0.4 flake8-strftime>=0.1.1 flake8-typing-imports>=1.10.0 + flake8-params>=0.1.0 + git+https://github.com/python-formate/flake8-commas.git@4.0.0-python-formate.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 @@ -186,7 +188,7 @@ commands = [flake8] max-line-length = 120 -select = E111 E112 E113 E121 E122 E125 E127 E128 E129 E131 E133 E201 E202 E203 E211 E222 E223 E224 E225 E225 E226 E227 E228 E231 E241 E242 E251 E261 E262 E265 E271 E272 E303 E304 E306 E402 E502 E703 E711 E712 E713 E714 E721 W291 W292 W293 W391 W504 YTT101 YTT102 YTT103 YTT201 YTT202 YTT203 YTT204 YTT301 YTT302 YTT303 STRFTIME001 STRFTIME002 SXL001 PT001 PT002 PT003 PT006 PT007 PT008 PT009 PT010 PT011 PT012 PT013 PT014 PT015 PT016 PT017 PT018 PT019 PT020 PT021 RST201 RST202 RST203 RST204 RST205 RST206 RST207 RST208 RST210 RST211 RST212 RST213 RST214 RST215 RST216 RST217 RST218 RST219 RST299 RST301 RST302 RST303 RST304 RST305 RST306 RST399 RST401 RST499 RST900 RST901 RST902 RST903 Q001 Q002 Q003 A001 A002 TYP001 TYP002 TYP003 TYP004 TYP005 TYP006 ENC001 ENC002 ENC003 ENC004 ENC011 ENC012 ENC021 ENC022 ENC023 ENC024 ENC025 ENC026 Y001,Y002 Y003 Y004 Y005 Y006 Y007 Y008 Y009 Y010 Y011 Y012 Y013 Y014 Y015 Y090 Y091 NQA001 NQA002 NQA003 NQA004 NQA005 NQA102 NQA103 E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 +select = E111 E112 E113 E121 E122 E125 E127 E128 E129 E131 E133 E201 E202 E203 E211 E222 E223 E224 E225 E225 E226 E227 E228 E231 E241 E242 E251 E261 E262 E265 E271 E272 E303 E304 E306 E402 E502 E703 E711 E712 E713 E714 E721 W291 W292 W293 W391 W504 YTT101 YTT102 YTT103 YTT201 YTT202 YTT203 YTT204 YTT301 YTT302 YTT303 STRFTIME001 STRFTIME002 SXL001 PT001 PT002 PT003 PT006 PT007 PT008 PT009 PT010 PT011 PT012 PT013 PT014 PT015 PT016 PT017 PT018 PT019 PT020 PT021 RST201 RST202 RST203 RST204 RST205 RST206 RST207 RST208 RST210 RST211 RST212 RST213 RST214 RST215 RST216 RST217 RST218 RST219 RST299 RST301 RST302 RST303 RST304 RST305 RST306 RST399 RST401 RST499 RST900 RST901 RST902 RST903 Q001 Q002 Q003 A001 A002 TYP001 TYP002 TYP003 TYP004 TYP005 TYP006 ENC001 ENC002 ENC003 ENC004 ENC011 ENC012 ENC021 ENC022 ENC023 ENC024 ENC025 ENC026 Y001,Y002 Y003 Y004 Y005 Y006 Y007 Y008 Y009 Y010 Y011 Y012 Y013 Y014 Y015 Y090 Y091 NQA001 NQA002 NQA003 NQA004 NQA005 NQA102 NQA103 C818 C819 E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 PRM001 PRM002 PRM003 extend-exclude = doc-source,old,build,dist,__pkginfo__.py,setup.py,venv rst-directives = TODO @@ -203,14 +205,16 @@ rst-roles = inline-code manpage per-file-ignores = + tests/*: D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 PRM001 PRM002 PRM003 + */*.pyi: E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 PRM001 PRM002 PRM003 + domdf_python_tools/compat/importlib_resources.py: PRM002 + domdf_python_tools/pretty_print.py: MAN001 MAN002 tests/list_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 tests/seq_tests.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 tests/test_doctools.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 + tests/test_getters.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 tests/test_paths_stdlib.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 PRM002 PRM003 tests/test_pretty_print.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 - tests/test_getters.py: PT011 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 MAN001 MAN002 PRM002 PRM003 - domdf_python_tools/pretty_print.py: MAN001 MAN002 - domdf_python_tools/compat/importlib_resources.py: PRM002 pytest-parametrize-names-type = csv inline-quotes = " multiline-quotes = """ From e9ed1757b5bd7abf81937b13222a9aa91549facc Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 13:16:10 +0000 Subject: [PATCH 20/33] Updated files with 'repo_helper'. (#145) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- README.rst | 2 +- justfile | 2 +- tox.ini | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e10275..6c9f5c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -80,12 +80,12 @@ repos: - id: snippet-fmt - repo: https://github.com/python-formate/formate - rev: v0.8.0 + rev: v0.9.0 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ additional_dependencies: - - formate-trailing-commas>=0.1.0 + - formate-trailing-commas>=0.1.1 - repo: https://github.com/python-coincidence/dep_checker rev: v0.9.0 diff --git a/README.rst b/README.rst index 8849702..c6c0b5d 100644 --- a/README.rst +++ b/README.rst @@ -112,7 +112,7 @@ domdf_python_tools :alt: Maintenance .. |pypi-downloads| image:: https://img.shields.io/pypi/dm/domdf_python_tools - :target: https://pypi.org/project/domdf_python_tools/ + :target: https://pypistats.org/packages/domdf_python_tools :alt: PyPI - Downloads .. end shields diff --git a/justfile b/justfile index 6a4d335..9cd8b78 100644 --- a/justfile +++ b/justfile @@ -13,7 +13,7 @@ incomplete-defs: tox -e lint -- --select MAN commas: - tox -e lint -- --select C812,C813,C814,C815,C816 + tox -e lint -- --select C810,C812,C813,C814,C815,C816 vdiff: git diff $(repo-helper show version -q)..HEAD diff --git a/tox.ini b/tox.ini index 1267903..3250392 100644 --- a/tox.ini +++ b/tox.ini @@ -127,6 +127,7 @@ deps = flake8-strftime>=0.1.1 flake8-typing-imports>=1.10.0 flake8-params>=0.1.0 + flake8-unused-fstrings>=2.0.0 git+https://github.com/python-formate/flake8-commas.git@4.0.0-python-formate.0 git+https://github.com/domdfcoding/restructuredtext-lint.git@fix-deprecations git+https://github.com/domdfcoding/flake8-rst-docstrings-sphinx.git @@ -188,7 +189,7 @@ commands = [flake8] max-line-length = 120 -select = E111 E112 E113 E121 E122 E125 E127 E128 E129 E131 E133 E201 E202 E203 E211 E222 E223 E224 E225 E225 E226 E227 E228 E231 E241 E242 E251 E261 E262 E265 E271 E272 E303 E304 E306 E402 E502 E703 E711 E712 E713 E714 E721 W291 W292 W293 W391 W504 YTT101 YTT102 YTT103 YTT201 YTT202 YTT203 YTT204 YTT301 YTT302 YTT303 STRFTIME001 STRFTIME002 SXL001 PT001 PT002 PT003 PT006 PT007 PT008 PT009 PT010 PT011 PT012 PT013 PT014 PT015 PT016 PT017 PT018 PT019 PT020 PT021 RST201 RST202 RST203 RST204 RST205 RST206 RST207 RST208 RST210 RST211 RST212 RST213 RST214 RST215 RST216 RST217 RST218 RST219 RST299 RST301 RST302 RST303 RST304 RST305 RST306 RST399 RST401 RST499 RST900 RST901 RST902 RST903 Q001 Q002 Q003 A001 A002 TYP001 TYP002 TYP003 TYP004 TYP005 TYP006 ENC001 ENC002 ENC003 ENC004 ENC011 ENC012 ENC021 ENC022 ENC023 ENC024 ENC025 ENC026 Y001,Y002 Y003 Y004 Y005 Y006 Y007 Y008 Y009 Y010 Y011 Y012 Y013 Y014 Y015 Y090 Y091 NQA001 NQA002 NQA003 NQA004 NQA005 NQA102 NQA103 C818 C819 E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 PRM001 PRM002 PRM003 +select = E111 E112 E113 E121 E122 E125 E127 E128 E129 E131 E133 E201 E202 E203 E211 E222 E223 E224 E225 E225 E226 E227 E228 E231 E241 E242 E251 E261 E262 E265 E271 E272 E303 E304 E306 E402 E502 E703 E711 E712 E713 E714 E721 W291 W292 W293 W391 W504 YTT101 YTT102 YTT103 YTT201 YTT202 YTT203 YTT204 YTT301 YTT302 YTT303 STRFTIME001 STRFTIME002 SXL001 NUF001 PT001 PT002 PT003 PT006 PT007 PT008 PT009 PT010 PT011 PT012 PT013 PT014 PT015 PT016 PT017 PT018 PT019 PT020 PT021 RST201 RST202 RST203 RST204 RST205 RST206 RST207 RST208 RST210 RST211 RST212 RST213 RST214 RST215 RST216 RST217 RST218 RST219 RST299 RST301 RST302 RST303 RST304 RST305 RST306 RST399 RST401 RST499 RST900 RST901 RST902 RST903 Q001 Q002 Q003 A001 A002 TYP001 TYP002 TYP003 TYP004 TYP005 TYP006 ENC001 ENC002 ENC003 ENC004 ENC011 ENC012 ENC021 ENC022 ENC023 ENC024 ENC025 ENC026 Y001,Y002 Y003 Y004 Y005 Y006 Y007 Y008 Y009 Y010 Y011 Y012 Y013 Y014 Y015 Y090 Y091 NQA001 NQA002 NQA003 NQA004 NQA005 NQA102 NQA103 C818 C819 E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 PRM001 PRM002 PRM003 extend-exclude = doc-source,old,build,dist,__pkginfo__.py,setup.py,venv rst-directives = TODO From bc555228e391be621e94efefe1b9b6768b79e2b6 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 16 Jan 2026 12:36:21 +0000 Subject: [PATCH 21/33] Recommend installing conda-forge build with conda. --- README.rst | 21 ++++++--------------- doc-source/index.rst | 10 +++++----- repo_helper.yml | 1 + 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index c6c0b5d..c0c747d 100644 --- a/README.rst +++ b/README.rst @@ -85,12 +85,12 @@ domdf_python_tools :target: https://pypi.org/project/domdf_python_tools/ :alt: PyPI - Wheel -.. |conda-version| image:: https://img.shields.io/conda/v/domdfcoding/domdf_python_tools?logo=anaconda - :target: https://anaconda.org/domdfcoding/domdf_python_tools +.. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf_python_tools?logo=anaconda + :target: https://anaconda.org/conda-forge/domdf_python_tools :alt: Conda - Package Version -.. |conda-platform| image:: https://img.shields.io/conda/pn/domdfcoding/domdf_python_tools?label=conda%7Cplatform - :target: https://anaconda.org/domdfcoding/domdf_python_tools +.. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf_python_tools?label=conda%7Cplatform + :target: https://anaconda.org/conda-forge/domdf_python_tools :alt: Conda - Platform .. |license| image:: https://img.shields.io/github/license/domdfcoding/domdf_python_tools @@ -134,17 +134,8 @@ To install with ``pip``: To install with ``conda``: - * First add the required channels - - .. code-block:: bash - - $ conda config --add channels https://conda.anaconda.org/conda-forge - $ conda config --add channels https://conda.anaconda.org/domdfcoding - - * Then install - - .. code-block:: bash +.. code-block:: bash - $ conda install domdf_python_tools + $ conda install -c conda-forge domdf_python_tools .. end installation diff --git a/doc-source/index.rst b/doc-source/index.rst index 9263acb..e7322fa 100644 --- a/doc-source/index.rst +++ b/doc-source/index.rst @@ -91,12 +91,12 @@ domdf_python_tools :wheel: :alt: PyPI - Wheel - .. |conda-version| image:: https://img.shields.io/conda/v/domdfcoding/domdf_python_tools?logo=anaconda - :target: https://anaconda.org/domdfcoding/domdf_python_tools + .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf_python_tools?logo=anaconda + :target: https://anaconda.org/conda-forge/domdf_python_tools :alt: Conda - Package Version - .. |conda-platform| image:: https://img.shields.io/conda/pn/domdfcoding/domdf_python_tools?label=conda%7Cplatform - :target: https://anaconda.org/domdfcoding/domdf_python_tools + .. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf_python_tools?label=conda%7Cplatform + :target: https://anaconda.org/conda-forge/domdf_python_tools :alt: Conda - Platform .. |license| github-shield:: @@ -142,7 +142,7 @@ Installation :pypi: :github: :anaconda: - :conda-channels: conda-forge, domdfcoding + :conda-channels: conda-forge .. end installation diff --git a/repo_helper.yml b/repo_helper.yml index dafed9a..0cdae3d 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -13,6 +13,7 @@ tox_testenv_extras: all pre_commit_exclude: "^domdf_python_tools/compat/importlib_resources.py$" docs_fail_on_warning: true use_hatch: true +on_conda_forge: true conda_channels: - conda-forge From 03cba5d94ade7afed38e3581042480198b1ceac5 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 16 Jan 2026 12:54:37 +0000 Subject: [PATCH 22/33] Use hyphenated package name for conda-forge --- README.rst | 12 ++++++------ doc-source/index.rst | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index c0c747d..8220282 100644 --- a/README.rst +++ b/README.rst @@ -85,12 +85,12 @@ domdf_python_tools :target: https://pypi.org/project/domdf_python_tools/ :alt: PyPI - Wheel -.. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf_python_tools?logo=anaconda - :target: https://anaconda.org/conda-forge/domdf_python_tools +.. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf-python-tools?logo=anaconda + :target: https://anaconda.org/conda-forge/domdf-python-tools :alt: Conda - Package Version -.. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf_python_tools?label=conda%7Cplatform - :target: https://anaconda.org/conda-forge/domdf_python_tools +.. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf-python-tools?label=conda%7Cplatform + :target: https://anaconda.org/conda-forge/domdf-python-tools :alt: Conda - Platform .. |license| image:: https://img.shields.io/github/license/domdfcoding/domdf_python_tools @@ -130,12 +130,12 @@ To install with ``pip``: .. code-block:: bash - $ python -m pip install domdf_python_tools + $ python -m pip install domdf-python-tools To install with ``conda``: .. code-block:: bash - $ conda install -c conda-forge domdf_python_tools + $ conda install -c conda-forge domdf-python-tools .. end installation diff --git a/doc-source/index.rst b/doc-source/index.rst index e7322fa..78aa235 100644 --- a/doc-source/index.rst +++ b/doc-source/index.rst @@ -91,12 +91,12 @@ domdf_python_tools :wheel: :alt: PyPI - Wheel - .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf_python_tools?logo=anaconda - :target: https://anaconda.org/conda-forge/domdf_python_tools + .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf-python-tools?logo=anaconda + :target: https://anaconda.org/conda-forge/domdf-python-tools :alt: Conda - Package Version - .. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf_python_tools?label=conda%7Cplatform - :target: https://anaconda.org/conda-forge/domdf_python_tools + .. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/domdf-python-tools?label=conda%7Cplatform + :target: https://anaconda.org/conda-forge/domdf-python-tools :alt: Conda - Platform .. |license| github-shield:: @@ -139,9 +139,9 @@ Installation .. start installation .. installation:: domdf_python_tools - :pypi: + :pypi: domdf-python-tools :github: - :anaconda: + :anaconda: domdf-python-tools :conda-channels: conda-forge .. end installation From c72ef5684d1a0e61137ed781558ce0e8a27034bf Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 16 Jan 2026 13:33:46 +0000 Subject: [PATCH 23/33] Fix hyphenated package name for conda-forge --- doc-source/index.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc-source/index.rst b/doc-source/index.rst index 78aa235..610a95c 100644 --- a/doc-source/index.rst +++ b/doc-source/index.rst @@ -139,9 +139,11 @@ Installation .. start installation .. installation:: domdf_python_tools - :pypi: domdf-python-tools + :pypi: + :pypi-name: domdf-python-tools :github: - :anaconda: domdf-python-tools + :anaconda: + :conda-name: domdf-python-tools :conda-channels: conda-forge .. end installation From 18b07e1ff96016536c13e650e965da1addb8f441 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 16 Jan 2026 13:41:44 +0000 Subject: [PATCH 24/33] Stop doing our own conda builds in favour of conda-forge. --- .github/workflows/conda_ci.yml | 67 --------------------------- .github/workflows/python_ci_linux.yml | 65 -------------------------- 2 files changed, 132 deletions(-) delete mode 100644 .github/workflows/conda_ci.yml diff --git a/.github/workflows/conda_ci.yml b/.github/workflows/conda_ci.yml deleted file mode 100644 index 3f83e5f..0000000 --- a/.github/workflows/conda_ci.yml +++ /dev/null @@ -1,67 +0,0 @@ -# This file is managed by 'repo_helper'. Don't edit it directly. ---- -name: Conda Tests - -on: - push: - branches: ["master"] - -jobs: - tests: - name: "Conda" - permissions: - contents: read - runs-on: ubuntu-22.04 - defaults: - run: - shell: bash -l {0} - - steps: - - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" - - - name: Setup Python 🐍 - uses: "actions/setup-python@v5" - with: - python-version: "3.11" - - - name: Setup Conda - uses: conda-incubator/setup-miniconda@v2.1.1 - with: - activate-environment: env - conda-build-version: 3.28.4 - miniconda-version: py311_24.1.2-0 - python-version: "3.11" - miniforge-variant: Mambaforge - - - name: Install dependencies πŸ”§ - run: | - python -VV - python -m site - python -m pip install --upgrade pip setuptools wheel - python -m pip install --upgrade "whey-conda" "hatch-requirements-txt" - # $CONDA is an environment variable pointing to the root of the miniconda directory - $CONDA/bin/conda update -n base conda - $CONDA/bin/conda config --add channels conda-forge - $CONDA/bin/conda config --add channels domdfcoding - - - name: "Build and index channel" - run: | - python -m whey --builder whey_conda --out-dir conda-bld/noarch - $CONDA/bin/conda index ./conda-bld || exit 1 - - - name: "Search for package" - run: | - $CONDA/bin/conda search -c file://$(pwd)/conda-bld domdf_python_tools - $CONDA/bin/conda search -c file://$(pwd)/conda-bld --override-channels domdf_python_tools - - - name: "Install package" - run: | - $CONDA/bin/conda install -c file://$(pwd)/conda-bld domdf_python_tools=3.10.0=py_1 -y || exit 1 - - - name: "Run Tests" - run: | - rm -rf domdf_python_tools - $CONDA/bin/conda install pytest coincidence || exit 1 - pip install -r tests/requirements.txt - pytest tests/ diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index 7e8493e..b0504f9 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -180,68 +180,3 @@ jobs: python .github/milestones.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - - Conda: - needs: deploy - permissions: - contents: read - runs-on: ubuntu-22.04 - if: startsWith(github.ref, 'refs/tags/') || (startsWith(github.event.head_commit.message, 'Bump version') != true) - steps: - - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" - - - name: Setup Python 🐍 - uses: "actions/setup-python@v5" - with: - python-version: 3.11 - - - name: Setup Conda - uses: conda-incubator/setup-miniconda@v2.1.1 - with: - activate-environment: env - conda-build-version: 3.28.4 - miniconda-version: py311_24.1.2-0 - python-version: "3.11" - miniforge-variant: Mambaforge - - - name: Install dependencies πŸ”§ - run: | - python -VV - python -m site - python -m pip install --upgrade pip setuptools wheel - python -m pip install --upgrade "mkrecipe" "hatch-requirements-txt" - # $CONDA is an environment variable pointing to the root of the miniconda directory - $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 - - $CONDA/bin/conda config --remove channels defaults - - - name: Build Conda Package πŸ“¦ - run: | - python -m mkrecipe --type wheel || exit 1 - $CONDA/bin/conda build conda -c conda-forge -c domdfcoding --output-folder conda/dist - - - name: Deploy Conda Package πŸš€ - if: startsWith(github.ref, 'refs/tags/') - run: | - $CONDA/bin/conda config --set always_yes yes --set changeps1 no - $CONDA/bin/conda install anaconda-client - $CONDA/bin/conda info -a - - for f in conda/dist/noarch/domdf_python_tools-*.tar.bz2; do - [ -e "$f" ] || continue - echo "$f" - conda install "$f" || exit 1 - echo "Deploying to Anaconda.org..." - $CONDA/bin/anaconda -t "$ANACONDA_TOKEN" upload "$f" || exit 1 - echo "Successfully deployed to Anaconda.org." - done - env: - ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} From b7a446d2863508fce4680985c67c6910800f6d7e Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Fri, 16 Jan 2026 14:45:03 +0000 Subject: [PATCH 25/33] Stop doing our own conda builds in favour of conda-forge (part 2) --- repo_helper.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/repo_helper.yml b/repo_helper.yml index 0cdae3d..2b40716 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -14,9 +14,7 @@ pre_commit_exclude: "^domdf_python_tools/compat/importlib_resources.py$" docs_fail_on_warning: true use_hatch: true on_conda_forge: true - -conda_channels: - - conda-forge +enable_conda: false python_versions: 3.7: From d11b5d2cf204197ac01fbc0ace363bd24b9cc200 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:09:39 +0000 Subject: [PATCH 26/33] Updated files with 'repo_helper'. (#146) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .bumpversion.cfg | 4 ---- .pre-commit-config.yaml | 4 ++-- formate.toml | 1 + pyproject.toml | 5 ----- tox.ini | 1 - 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6001e21..b460192 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -18,7 +18,3 @@ replace = : str = "{new_version}" search = version = "{current_version}" replace = version = "{new_version}" - -[bumpversion:file:.github/workflows/conda_ci.yml] -search = ={current_version}=py_1 -replace = ={new_version}=py_1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c9f5c0..14d757f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - id: end-of-file-fixer - repo: https://github.com/domdfcoding/pre-commit-hooks - rev: v0.4.0 + rev: v0.5.0 hooks: - id: requirements-txt-sorter args: @@ -80,7 +80,7 @@ repos: - id: snippet-fmt - repo: https://github.com/python-formate/formate - rev: v0.9.0 + rev: v1.1.2 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ diff --git a/formate.toml b/formate.toml index 5893251..20307c5 100644 --- a/formate.toml +++ b/formate.toml @@ -5,6 +5,7 @@ reformat-generics = 40 noqa-reformat = 60 ellipsis-reformat = 70 squish_stubs = 80 +newline_after_equals = 90 [hooks.yapf] priority = 30 diff --git a/pyproject.toml b/pyproject.toml index b1bdb9a..0370ff3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,11 +49,6 @@ dates = [ "pytz>=2019.1",] testing = [] all = [ "pytz>=2019.1",] -[tool.mkrecipe] -conda-channels = [ "conda-forge", "domdfcoding",] -extras = [ "dates",] -license-key = "MIT" - [tool.sphinx-pyproject] github_username = "domdfcoding" github_repository = "domdf_python_tools" diff --git a/tox.ini b/tox.ini index 3250392..8ab4c40 100644 --- a/tox.ini +++ b/tox.ini @@ -136,7 +136,6 @@ deps = git+https://github.com/python-formate/flake8-missing-annotations.git 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} [testenv:perflint] From f6d097c99f933aa09d2b8d072e79b11b9c67921d Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 21:04:09 +0000 Subject: [PATCH 27/33] [repo-helper] Configuration Update (#147) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/octocheese.yml | 2 +- README.rst | 20 ++++++++++---------- doc-source/index.rst | 10 +++++----- doc-source/license.rst | 2 +- pyproject.toml | 3 ++- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/octocheese.yml b/.github/workflows/octocheese.yml index 67e2bbb..3bc6e0c 100644 --- a/.github/workflows/octocheese.yml +++ b/.github/workflows/octocheese.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: domdfcoding/octocheese@master with: - pypi_name: "domdf_python_tools" + pypi_name: "domdf-python-tools" env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} if: startsWith(github.ref, 'refs/tags/') != true diff --git a/README.rst b/README.rst index 8220282..a5037a4 100644 --- a/README.rst +++ b/README.rst @@ -69,20 +69,20 @@ domdf_python_tools :target: https://www.codefactor.io/repository/github/domdfcoding/domdf_python_tools :alt: CodeFactor Grade -.. |pypi-version| image:: https://img.shields.io/pypi/v/domdf_python_tools - :target: https://pypi.org/project/domdf_python_tools/ +.. |pypi-version| image:: https://img.shields.io/pypi/v/domdf-python-tools + :target: https://pypi.org/project/domdf-python-tools/ :alt: PyPI - Package Version -.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/domdf_python_tools?logo=python&logoColor=white - :target: https://pypi.org/project/domdf_python_tools/ +.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/domdf-python-tools?logo=python&logoColor=white + :target: https://pypi.org/project/domdf-python-tools/ :alt: PyPI - Supported Python Versions -.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/domdf_python_tools - :target: https://pypi.org/project/domdf_python_tools/ +.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/domdf-python-tools + :target: https://pypi.org/project/domdf-python-tools/ :alt: PyPI - Supported Implementations -.. |wheel| image:: https://img.shields.io/pypi/wheel/domdf_python_tools - :target: https://pypi.org/project/domdf_python_tools/ +.. |wheel| image:: https://img.shields.io/pypi/wheel/domdf-python-tools + :target: https://pypi.org/project/domdf-python-tools/ :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/domdf-python-tools?logo=anaconda @@ -111,8 +111,8 @@ domdf_python_tools .. |maintained| image:: https://img.shields.io/maintenance/yes/2026 :alt: Maintenance -.. |pypi-downloads| image:: https://img.shields.io/pypi/dm/domdf_python_tools - :target: https://pypistats.org/packages/domdf_python_tools +.. |pypi-downloads| image:: https://img.shields.io/pypi/dm/domdf-python-tools + :target: https://pypistats.org/packages/domdf-python-tools :alt: PyPI - Downloads .. end shields diff --git a/doc-source/index.rst b/doc-source/index.rst index 610a95c..772bb33 100644 --- a/doc-source/index.rst +++ b/doc-source/index.rst @@ -72,22 +72,22 @@ domdf_python_tools :alt: CodeFactor Grade .. |pypi-version| pypi-shield:: - :project: domdf_python_tools + :project: domdf-python-tools :version: :alt: PyPI - Package Version .. |supported-versions| pypi-shield:: - :project: domdf_python_tools + :project: domdf-python-tools :py-versions: :alt: PyPI - Supported Python Versions .. |supported-implementations| pypi-shield:: - :project: domdf_python_tools + :project: domdf-python-tools :implementations: :alt: PyPI - Supported Implementations .. |wheel| pypi-shield:: - :project: domdf_python_tools + :project: domdf-python-tools :wheel: :alt: PyPI - Wheel @@ -119,7 +119,7 @@ domdf_python_tools :alt: Maintenance .. |pypi-downloads| pypi-shield:: - :project: domdf_python_tools + :project: domdf-python-tools :downloads: month :alt: PyPI - Downloads diff --git a/doc-source/license.rst b/doc-source/license.rst index 42e3d40..bc0b0dc 100644 --- a/doc-source/license.rst +++ b/doc-source/license.rst @@ -7,4 +7,4 @@ License .. license-info:: MIT .. license:: - :py: domdf_python_tools + :py: domdf-python-tools diff --git a/pyproject.toml b/pyproject.toml index 0370ff3..f0a9cd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = [ "hatch-requirements-txt",] build-backend = "hatchling.build" [project] -name = "domdf_python_tools" +name = "domdf-python-tools" version = "3.10.0" description = "Helpful functions for Pythonβ€‚πŸβ€‚πŸ› οΈ" readme = "README.rst" @@ -143,6 +143,7 @@ python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" additional-files = [ "include domdf_python_tools/google-10000-english-no-swears.txt",] +package = "domdf_python_tools" [tool.mypy] python_version = "3.9" From d7954b54eb25640832dd561521f55b5545e70fa7 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:36:04 +0000 Subject: [PATCH 28/33] [repo-helper] Configuration Update (#148) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 7 +++++-- doc-source/requirements.txt | 1 + tox.ini | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14d757f..16f7499 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - id: end-of-file-fixer - repo: https://github.com/domdfcoding/pre-commit-hooks - rev: v0.5.0 + rev: v0.6.0 hooks: - id: requirements-txt-sorter args: @@ -80,12 +80,15 @@ repos: - id: snippet-fmt - repo: https://github.com/python-formate/formate - rev: v1.1.2 + rev: v1.2.0 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ additional_dependencies: - formate-trailing-commas>=0.1.1 + types_or: + - python + - pyi - repo: https://github.com/python-coincidence/dep_checker rev: v0.9.0 diff --git a/doc-source/requirements.txt b/doc-source/requirements.txt index faad183..cae1d8b 100644 --- a/doc-source/requirements.txt +++ b/doc-source/requirements.txt @@ -9,6 +9,7 @@ pytest-regressions>=2.0.2 pytz>=2019.1 roman>=4.0 seed-intersphinx-mapping>=1.2.2 +setuptools<81 sphinx>=3.0.3 sphinx-autofixture>=0.2.1 sphinx-copybutton>=0.2.12 diff --git a/tox.ini b/tox.ini index 8ab4c40..d80004e 100644 --- a/tox.ini +++ b/tox.ini @@ -42,7 +42,7 @@ requires = pip>=21,!=22.2 tox-envlist>=0.2.1 tox~=3.0 - virtualenv!=20.16.0 + virtualenv!=20.16.0,<20.39 [envlists] test = From 759ccd446791af14cef1e185879cda4c52dc325d Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:46:41 +0100 Subject: [PATCH 29/33] [repo-helper] Configuration Update (#150) Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> --- .github/workflows/python_ci.yml | 4 ++-- .pre-commit-config.yaml | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index b545fee..5e2f58e 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -36,8 +36,8 @@ jobs: - {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} - - {python-version: "pypy-3.10-v7.3.15", testenvs: "pypy310,build", experimental: True} + - {python-version: "pypy-3.9-v7.3.16", testenvs: "pypy39", experimental: True} + - {python-version: "pypy-3.10-v7.3.19", testenvs: "pypy310,build", experimental: True} steps: - name: Checkout πŸ›ŽοΈ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 16f7499..952ff8f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - id: end-of-file-fixer - repo: https://github.com/domdfcoding/pre-commit-hooks - rev: v0.6.0 + rev: v0.7.0 hooks: - id: requirements-txt-sorter args: @@ -40,6 +40,9 @@ repos: - id: check-docstring-first exclude: ^(doc-source/conf|__pkginfo__|setup|tests/.*)\.py$ - id: bind-requirements + args: + - --python-min + - '3.7' - repo: https://github.com/python-formate/flake8-dunder-all rev: v0.5.0 @@ -60,8 +63,8 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 + - repo: https://github.com/python-formate/pyupgrade + rev: bbe3007 hooks: - id: pyupgrade args: @@ -80,7 +83,7 @@ repos: - id: snippet-fmt - repo: https://github.com/python-formate/formate - rev: v1.2.0 + rev: v1.2.1 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ From 568c6a056fe6956f59006030364ed82c225d6279 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Wed, 8 Jul 2026 15:33:49 +0100 Subject: [PATCH 30/33] Don't parameterize tests using non-Collection iterables (#151) This is deprecated as of pytest 9.1; see https://docs.pytest.org/en/stable/deprecations.html#class-scoped-fixture-as-instance-method. --- tests/test_dates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 320d470..42e2847 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -201,7 +201,7 @@ def test_utc_offset_no_pytz(): # from domdf_python_tools.dates import get_utc_offset -@pytest.mark.parametrize("month_idx, month", enumerate(dates.month_full_names)) +@pytest.mark.parametrize("month_idx, month", list(enumerate(dates.month_full_names))) def test_parse_month(month_idx: int, month: str): month_idx += 1 # to make 1-indexed @@ -219,7 +219,7 @@ def test_parse_month_errors(): dates.parse_month(value) # type: ignore[arg-type] -@pytest.mark.parametrize("month_idx, month", enumerate(dates.month_full_names)) +@pytest.mark.parametrize("month_idx, month", list(enumerate(dates.month_full_names))) def test_get_month_number_from_name(month_idx: int, month: str): month_idx += 1 # to make 1-indexed From b82422356b56c4ead83b1da450cd21f7afe8fd31 Mon Sep 17 00:00:00 2001 From: "repo-helper[bot]" <74742576+repo-helper[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 08:54:54 +0100 Subject: [PATCH 31/33] [repo-helper] Configuration Update (#153) * Updated files with 'repo_helper'. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/docs_test_action.yml | 4 +-- .github/workflows/flake8.yml | 6 ++-- .github/workflows/mypy.yml | 6 ++-- .github/workflows/python_ci.yml | 8 +++--- .github/workflows/python_ci_linux.yml | 14 +++++----- .github/workflows/python_ci_macos.yml | 6 ++-- .pre-commit-config.yaml | 12 ++++++-- doc-source/api/bases.rst | 2 +- domdf_python_tools/doctools.py | 4 +-- domdf_python_tools/getters.py | 20 +++++++------- domdf_python_tools/iterative.py | 6 ++-- domdf_python_tools/pagesizes/units.py | 38 +++++++++++++------------- domdf_python_tools/stringlist.py | 6 ++-- domdf_python_tools/words.py | 6 ++-- justfile | 5 ++++ tox.ini | 2 -- 16 files changed, 78 insertions(+), 67 deletions(-) diff --git a/.github/workflows/docs_test_action.yml b/.github/workflows/docs_test_action.yml index 39ae450..f47c729 100644 --- a/.github/workflows/docs_test_action.yml +++ b/.github/workflows/docs_test_action.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 1ecb7b9..1f5be86 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -19,10 +19,10 @@ jobs: steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" @@ -32,7 +32,7 @@ jobs: - name: Setup Python 🐍 if: steps.changes.outputs.code == 'true' - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "3.9" diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index b889816..2b5e4f8 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -24,10 +24,10 @@ jobs: steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" @@ -37,7 +37,7 @@ jobs: - name: Setup Python 🐍 if: steps.changes.outputs.code == 'true' - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "3.9" diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index 5e2f58e..de52382 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -37,15 +37,15 @@ jobs: - {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.16", testenvs: "pypy39", experimental: True} - - {python-version: "pypy-3.10-v7.3.19", testenvs: "pypy310,build", experimental: True} + - {python-version: "pypy-3.10-v7.3.19", testenvs: "pypy310", experimental: True} steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" @@ -56,7 +56,7 @@ jobs: - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "${{ matrix.config.python-version }}" diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index b0504f9..a0a591d 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -42,11 +42,11 @@ jobs: steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" @@ -57,7 +57,7 @@ jobs: - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "${{ matrix.config.python-version }}" @@ -91,10 +91,10 @@ jobs: runs-on: "ubuntu-22.04" steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Setup Python 🐍 - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: 3.8 @@ -144,11 +144,11 @@ jobs: runs-on: "ubuntu-22.04" steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" if: startsWith(github.ref, 'refs/tags/') - name: Setup Python 🐍 - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" if: startsWith(github.ref, 'refs/tags/') with: python-version: 3.8 diff --git a/.github/workflows/python_ci_macos.yml b/.github/workflows/python_ci_macos.yml index 7cfd5dc..a6b734d 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -41,11 +41,11 @@ jobs: steps: - name: Checkout πŸ›ŽοΈ - uses: "actions/checkout@v4" + uses: "actions/checkout@v6" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: changes with: list-files: "json" @@ -56,7 +56,7 @@ jobs: - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} - uses: "actions/setup-python@v5" + uses: "actions/setup-python@v6" with: python-version: "${{ matrix.config.python-version }}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 952ff8f..bf00d0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,7 @@ exclude: ^domdf_python_tools/compat/importlib_resources.py$ ci: autoupdate_schedule: quarterly + skip: [taplo-lint] repos: - repo: https://github.com/repo-helper/pyproject-parser @@ -21,7 +22,6 @@ repos: - id: check-case-conflict - id: check-executables-have-shebangs - id: check-json - - id: check-toml - id: check-yaml - id: check-merge-conflict - id: check-symlinks @@ -31,6 +31,12 @@ repos: - id: mixed-line-ending - id: end-of-file-fixer + - repo: https://github.com/domdfcoding/taplo-pre-commit + rev: v0.10.0 + hooks: + - id: taplo-lint + args: [] + - repo: https://github.com/domdfcoding/pre-commit-hooks rev: v0.7.0 hooks: @@ -78,9 +84,11 @@ repos: - id: forbid-crlf - repo: https://github.com/python-formate/snippet-fmt - rev: v0.1.5 + rev: v0.3.1 hooks: - id: snippet-fmt + additional_dependencies: + - formate-trailing-commas>=0.1.1 - repo: https://github.com/python-formate/formate rev: v1.2.1 diff --git a/doc-source/api/bases.rst b/doc-source/api/bases.rst index 2669e00..6c77ecc 100644 --- a/doc-source/api/bases.rst +++ b/doc-source/api/bases.rst @@ -63,7 +63,7 @@ from :class:`NamedList` rather than from :func:`~.namedlist`. For example, do th .. code-block:: python >>> class ShoppingList(NamedList): - ... pass + ... pass >>> and not this: diff --git a/domdf_python_tools/doctools.py b/domdf_python_tools/doctools.py index 6ae998c..048523f 100644 --- a/domdf_python_tools/doctools.py +++ b/domdf_python_tools/doctools.py @@ -216,8 +216,8 @@ def sphinxify_docstring() -> Callable[[_F], _F]: .. code-block:: python intersphinx_mapping = { - "python": ("https://docs.python.org/3/", None), - } + "python": ("https://docs.python.org/3/", None), + } """ def wrapper(target: _F) -> _F: diff --git a/domdf_python_tools/getters.py b/domdf_python_tools/getters.py index 32f9884..f77ae07 100644 --- a/domdf_python_tools/getters.py +++ b/domdf_python_tools/getters.py @@ -55,9 +55,9 @@ class attrgetter: .. code-block:: python >>> from pathlib import Path - >>> attrgetter(0, 'name')([Path("dir/code.py")]) + >>> attrgetter(0, "name")([Path("dir/code.py")]) 'code.py' - >>> attrgetter(2, 'parent.name')([Path("dir/coincidence.py"), Path("dir/wheel.py"), Path("dir/operator.py")]) + >>> attrgetter(2, "parent.name")([Path("dir/coincidence.py"), Path("dir/wheel.py"), Path("dir/operator.py")]) 'dir' .. seealso:: :func:`operator.attrgetter` and :func:`operator.itemgetter` @@ -110,14 +110,14 @@ class itemgetter: .. code-block:: python - >>> itemgetter(0, 1)(['ABCDEFG']) + >>> itemgetter(0, 1)(["ABCDEFG"]) 'B' - >>> itemgetter(1, 2)(['ABC', 'DEF']) + >>> itemgetter(1, 2)(["ABC", "DEF"]) 'F' - >>> itemgetter(0, slice(2, None))(['ABCDEFG']) + >>> itemgetter(0, slice(2, None))(["ABCDEFG"]) 'CDEFG' - >>> army = [dict(rank='captain', name='Blackadder'), dict(rank='Private', name='Baldrick')] - >>> itemgetter(0, 'rank')(army) + >>> army = [dict(rank="captain", name="Blackadder"), dict(rank="Private", name="Baldrick")] + >>> itemgetter(0, "rank")(army) 'captain' .. seealso:: :func:`operator.itemgetter` @@ -158,11 +158,11 @@ class methodcaller: .. code-block:: python >>> from datetime import date - >>> methodcaller(0, 'upper')(["hello", "world"]) + >>> methodcaller(0, "upper")(["hello", "world"]) 'HELLO' - >>> methodcaller(1, 'center', 9, "=")(["hello", "world"]) + >>> methodcaller(1, "center", 9, '=')(["hello", "world"]) '==world==' - >>> methodcaller(0, 'replace', year=2019)([date(2021, 7, 6)]) + >>> methodcaller(0, "replace", year=2019)([date(2021, 7, 6)]) datetime.date(2019, 7, 6) .. seealso:: :func:`operator.methodcaller` and :func:`operator.itemgetter` diff --git a/domdf_python_tools/iterative.py b/domdf_python_tools/iterative.py index 5d24cf6..76f6b89 100644 --- a/domdf_python_tools/iterative.py +++ b/domdf_python_tools/iterative.py @@ -306,9 +306,9 @@ def groupfloats( .. code-block:: python >>> list(groupfloats( - ... [170.0, 170.05, 170.1, 170.15, 171.05, 171.1, 171.15, 171.2], - ... step=0.05, - ... )) + ... [170.0, 170.05, 170.1, 170.15, 171.05, 171.1, 171.15, 171.2], + ... step=0.05, + ... )) [(170.0, 170.05, 170.1, 170.15), (171.05, 171.1, 171.15, 171.2)] >>> list(groupfloats([1, 2, 3, 4, 5, 7, 8, 9, 10])) [(1, 2, 3, 4, 5), (7, 8, 9, 10)] diff --git a/domdf_python_tools/pagesizes/units.py b/domdf_python_tools/pagesizes/units.py index 2792f78..58aa29f 100644 --- a/domdf_python_tools/pagesizes/units.py +++ b/domdf_python_tools/pagesizes/units.py @@ -80,7 +80,7 @@ class Unit(float): .. code-block:: python - >>> (3*mm) + (7*mm) + >>> (3 * mm) + (7 * mm) When adding different :class:`~domdf_python_tools.pagesizes.units.Unit` objects, @@ -88,18 +88,18 @@ class Unit(float): .. code-block:: python - >>> (2.54*cm) + inch + >>> (2.54 * cm) + inch - >>> inch + (2.54*cm) + >>> inch + (2.54 * cm) :class:`~domdf_python_tools.pagesizes.units.Unit` objects can also be added to :class:`float` and :class:`int` objects: .. code-block:: python - >>> (3*cm) + 7 + >>> (3 * cm) + 7 - >>> 7 + (3*cm) + >>> 7 + (3 * cm) @@ -109,15 +109,15 @@ class Unit(float): .. code-block:: python - >>> (17*mm) - (7*mm) + >>> (17 * mm) - (7 * mm) - >>> (2.54*cm) - inch + >>> (2.54 * cm) - inch - >>> inch - (2.54*cm) + >>> inch - (2.54 * cm) - >>> (17*cm) - 7 + >>> (17 * cm) - 7 - >>> 17 - (7*cm) + >>> 17 - (7 * cm) @@ -128,11 +128,11 @@ class Unit(float): .. code-block:: python - >>> (3*mm) * 3 + >>> (3 * mm) * 3 - >>> 3 * (3*mm) + >>> 3 * (3 * mm) - >>> 3.5 * (3*mm) + >>> 3.5 * (3 * mm) Multiplication works either way round. @@ -142,7 +142,7 @@ class Unit(float): .. code-block:: python - >>> inch * (7*cm) + >>> inch * (7 * cm) Traceback (most recent call last): NotImplementedError: Multiplying a unit by another unit is not allowed. @@ -153,16 +153,16 @@ class Unit(float): .. code-block:: python - >>> (3*mm) / 3 + >>> (3 * mm) / 3 - >>> (10*mm) / 2.5 + >>> (10 * mm) / 2.5 Dividing by another unit results in a :exc:`NotImplementedError`: .. code-block:: python - >>> inch / (7*cm) + >>> inch / (7 * cm) Traceback (most recent call last): NotImplementedError: Dividing a unit by another unit is not allowed. @@ -171,7 +171,7 @@ class Unit(float): .. code-block:: python - >>> 3 / (3*mm) + >>> 3 / (3 * mm) Traceback (most recent call last): NotImplementedError: Dividing by a unit is not allowed. @@ -188,7 +188,7 @@ class Unit(float): .. code-block:: python - >>> (3*mm) % 2.5 + >>> (3 * mm) % 2.5 Dividing by a unit, or modulo division of two units, is not officially supported. diff --git a/domdf_python_tools/stringlist.py b/domdf_python_tools/stringlist.py index b50a82f..516398d 100644 --- a/domdf_python_tools/stringlist.py +++ b/domdf_python_tools/stringlist.py @@ -405,7 +405,7 @@ def with_indent(self, indent: Union[String, Indent], size: int = 0): >>> sl = StringList() >>> with sl.with_indent(" ", 1): - ... sl.append("Hello World") + ... sl.append("Hello World") :param indent: The :class:`~.Indent` to use within the ``with`` block, or the indent type. :param size: If ``indent`` is an indent type, the indent size to use within the ``with`` block. @@ -428,7 +428,7 @@ def with_indent_size(self, size: int = 0): >>> sl = StringList() >>> with sl.with_indent_size(1): - ... sl.append("Hello World") + ... sl.append("Hello World") :param size: The indent size to use within the ``with`` block. """ @@ -450,7 +450,7 @@ def with_indent_type(self, indent_type: str = '\t'): >>> sl = StringList() >>> with sl.with_indent_type(" "): - ... sl.append("Hello World") + ... sl.append("Hello World") :param indent_type: The type of indent to use within the ``with`` block. """ diff --git a/domdf_python_tools/words.py b/domdf_python_tools/words.py index d7dc7d8..dd74787 100644 --- a/domdf_python_tools/words.py +++ b/domdf_python_tools/words.py @@ -653,9 +653,9 @@ class PluralPhrase(NamedTuple): .. code-block:: python >>> phrase = PluralPhrase( - ... "The proposed {} {} to ...", - ... (Plural("change", "changes"), Plural("is", "are")) - ... ) + ... "The proposed {} {} to ...", + ... (Plural("change", "changes"), Plural("is", "are")), + ... ) >>> phrase(1) 'The proposed change is to ...' >>> phrase(2) diff --git a/justfile b/justfile index 9cd8b78..4307956 100644 --- a/justfile +++ b/justfile @@ -23,3 +23,8 @@ bare-ignore: lint: unused-imports incomplete-defs bare-ignore tox -n qa + +uncomm: + git status -uall --ignored + +# Custom commands can be added below this comment diff --git a/tox.ini b/tox.ini index d80004e..9c4408b 100644 --- a/tox.ini +++ b/tox.ini @@ -67,13 +67,11 @@ setenv = PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:py313] -download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:py312] -download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 From d6b1384c8da900b6e4c18495050f2724d6baa38a Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 27 Jul 2026 14:59:12 +0100 Subject: [PATCH 32/33] Emit warning if passing boolean as first argument to PathPlus.maybe_make --- domdf_python_tools/paths.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/domdf_python_tools/paths.py b/domdf_python_tools/paths.py index 6ca924c..6982b7a 100644 --- a/domdf_python_tools/paths.py +++ b/domdf_python_tools/paths.py @@ -458,6 +458,11 @@ def maybe_make( """ + if isinstance(mode, bool): + warnings.warn( + f"Boolean {mode} passed as mode argument, which was probably unintended. You probably wanted 'parents={mode}'", + ) + try: self.mkdir(mode, parents, exist_ok=True) except FileExistsError: From 2f9a4051d5dbd5501e010eb54b845f0da6de9d3c Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 27 Jul 2026 15:01:09 +0100 Subject: [PATCH 33/33] Remove old (empty) testing extra --- __pkginfo__.py | 2 +- pyproject.toml | 1 - repo_helper.yml | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/__pkginfo__.py b/__pkginfo__.py index c9a9179..3117d66 100644 --- a/__pkginfo__.py +++ b/__pkginfo__.py @@ -2,4 +2,4 @@ __all__ = ["extras_require"] -extras_require = {"dates": ["pytz>=2019.1"], "testing": [], "all": ["pytz>=2019.1"]} +extras_require = {"dates": ["pytz>=2019.1"], "all": ["pytz>=2019.1"]} diff --git a/pyproject.toml b/pyproject.toml index f0a9cd8..b31a47c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,6 @@ Documentation = "https://domdf-python-tools.readthedocs.io/en/latest" [project.optional-dependencies] dates = [ "pytz>=2019.1",] -testing = [] all = [ "pytz>=2019.1",] [tool.sphinx-pyproject] diff --git a/repo_helper.yml b/repo_helper.yml index 2b40716..c298e62 100644 --- a/repo_helper.yml +++ b/repo_helper.yml @@ -38,7 +38,6 @@ classifiers: extras_require: dates: - pytz>=2019.1 - testing: [] conda_extras: - dates