diff --git a/.github/workflows/docs_test_action.yml b/.github/workflows/docs_test_action.yml index 8f60ba5a..761b41fc 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 0a8c0c3e..5e67c5c8 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 ๐Ÿ›Ž๏ธ @@ -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 10c6f300..4c22a524 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-2022'] fail-fast: false steps: @@ -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/.github/workflows/octocheese.yml b/.github/workflows/octocheese.yml index fd77cd01..a5b3f19d 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 4505aef6..a1e08282 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -18,28 +18,27 @@ 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.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,3.14,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: "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-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: @@ -81,3 +80,4 @@ jobs: with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage + include-hidden-files: true diff --git a/.github/workflows/python_ci_alt_linux.yml b/.github/workflows/python_ci_alt_linux.yml index 6aa867fd..8401ea3d 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 }} diff --git a/.github/workflows/python_ci_linux.yml b/.github/workflows/python_ci_linux.yml index de8bd506..4cc812eb 100644 --- a/.github/workflows/python_ci_linux.yml +++ b/.github/workflows/python_ci_linux.yml @@ -19,28 +19,27 @@ 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.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,3.14,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: "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,build", experimental: True} + - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} - {python-version: "pypy-3.10", testenvs: "pypy310,build", experimental: True} steps: @@ -83,11 +82,12 @@ jobs: with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage + include-hidden-files: true Coverage: needs: tests - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" steps: - name: Checkout ๐Ÿ›Ž๏ธ uses: "actions/checkout@v4" @@ -125,6 +125,7 @@ jobs: with: name: "combined-coverage" path: .coverage + include-hidden-files: true - name: "Upload Combined Coverage to Coveralls" if: ${{ steps.show.outcome != 'failure' }} @@ -136,7 +137,7 @@ jobs: Deploy: needs: tests - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" steps: - name: Checkout ๐Ÿ›Ž๏ธ uses: "actions/checkout@v4" @@ -162,7 +163,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 }} @@ -210,6 +211,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 d008b4ea..d52b858b 100644 --- a/.github/workflows/python_ci_macos.yml +++ b/.github/workflows/python_ci_macos.yml @@ -18,28 +18,28 @@ 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.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,3.14,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.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: "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: "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} + - {python-version: "pypy-3.10", os-ver: "14", testenvs: "pypy310,build", experimental: True} steps: - name: Checkout ๐Ÿ›Ž๏ธ @@ -80,3 +80,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 b4912f4d..973fdef8 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 @@ -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$ @@ -62,11 +62,11 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/asottile/pyupgrade - rev: v2.12.0 + rev: v3.3.0 hooks: - id: pyupgrade args: - - --py36-plus + - --py37-plus - --keep-runtime-typing - repo: https://github.com/Lucas-C/pre-commit-hooks diff --git a/.readthedocs.yml b/.readthedocs.yml index 35b5c858..e73aa4ab 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: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4c3b5a2c..35c62f8f 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 f155af05..d3e8a02b 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: diff --git a/doc-source/requirements.txt b/doc-source/requirements.txt index 254a6964..faad183f 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/domdf_python_tools/bases.py b/domdf_python_tools/bases.py index d25b380e..4aa4d727 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: @@ -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``. @@ -538,7 +544,7 @@ def __float__(self) -> float: def __abs__(self: _F) -> _F: return self.__class__(float(self).__abs__()) - def __hash__(self) -> int: + def __hash__(self) -> int: # type: ignore[override] return float(self).__hash__() def __repr__(self) -> str: @@ -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 22dc0833..e8253f13 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 3244bc56..7ce12df6 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 b3cf970e..5d24cf62 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 f7c6b7b3..f248c0db 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 d205744e..a67bef08 100644 --- a/domdf_python_tools/pretty_print.py +++ b/domdf_python_tools/pretty_print.py @@ -39,20 +39,20 @@ # 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 # 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) @@ -106,8 +106,8 @@ def __init__( _dispatch: MutableMapping[Callable, Callable] _indent_per_level: int - _format_items: Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None] - _dispatch = dict(PrettyPrinter._dispatch) # type: ignore + _format_items: ClassVar[Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None]] + _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 4b0c173a..920bb2c1 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 a8a09f8d..27179388 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 efa533af..a87878eb 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 8207d809..54589a52 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 @@ -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/versions.py b/domdf_python_tools/versions.py index 2ebc152f..fe5cc7fd 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/domdf_python_tools/words.py b/domdf_python_tools/words.py index d8bc36cf..ad2e5fe9 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]): @@ -218,7 +217,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 +594,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) @@ -679,7 +680,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/pyproject.toml b/pyproject.toml index 335c68b1..b1bdb9a6 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", @@ -16,13 +16,14 @@ 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 :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", @@ -142,14 +143,14 @@ 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", "3.14",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] 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 4c9b41d1..dafed9a5 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$" @@ -20,15 +18,14 @@ 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": + "3.14": pypy37: pypy38: pypy39: diff --git a/tests/discover_demo_module/__init__.py b/tests/discover_demo_module/__init__.py index abee116f..6a52ec9f 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/list_tests.py b/tests/list_tests.py index 2c6f7050..49c5d781 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/mypy_test.py b/tests/mypy_test.py index a72f85b2..deea8d97 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/seq_tests.py b/tests/seq_tests.py index 07754900..cad4c304 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_bases.py b/tests/test_bases.py index 1125ffb1..e0f22d62 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_dates.py b/tests/test_dates.py index df5d7764..19214179 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_delegators.py b/tests/test_delegators.py index 8afbe8dc..82826489 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/tests/test_doctools.py b/tests/test_doctools.py index 55358b41..1eb0af17 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 dd433626..4f4df9e5 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 71fcc984..998aa489 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 81bdac2d..edb398cb 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.py b/tests/test_paths.py index 22e411be..75679411 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_paths_stdlib.py b/tests/test_paths_stdlib.py index 82d0a85c..f1571403 100644 --- a/tests/test_paths_stdlib.py +++ b/tests/test_paths_stdlib.py @@ -30,7 +30,7 @@ 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 @@ -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: @@ -159,7 +162,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 530cfcb2..11813ce1 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 42e95b3e..f129c249 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 8809883a..2c2d0893 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 77bf9ea1..337ba0f6 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_utils.py b/tests/test_utils.py index 768bd836..08809e24 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/tests/test_versions.py b/tests/test_versions.py index deaa405c..d0b0b4ca 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( diff --git a/tox.ini b/tox.ini index 8e920f9c..3422d705 100644 --- a/tox.ini +++ b/tox.ini @@ -22,15 +22,14 @@ [tox] envlist = - py36 py37 py38 py39 py310 py311 py312 - py313-dev - pypy36 + py313 + py314 pypy37 pypy38 pypy39 @@ -47,33 +46,31 @@ requires = [envlists] test = - py36 py37 py38 py39 py310 py311 py312 - py313-dev - pypy36 + py313 + py314 pypy37 pypy38 pypy39 pypy310 qa = mypy, lint -cov = py38, coverage +cov = py39, coverage [testenv:.package] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 -[testenv:py313-dev] +[testenv:py313] download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 - UNSAFE_PYO3_SKIP_VERSION_CHECK=1 [testenv:py312] download = True @@ -84,7 +81,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 @@ -109,7 +106,7 @@ commands = check-wheel-contents dist/ [testenv:lint] -basepython = python3.8 +basepython = python3.9 changedir = {toxinidir} ignore_errors = True skip_install = False @@ -129,17 +126,18 @@ 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} [testenv:perflint] -basepython = python3.8 +basepython = python3.9 changedir = {toxinidir} ignore_errors = True skip_install = True @@ -147,19 +145,19 @@ 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 deps = - mypy==0.971 + mypy==1.17.1 -r{toxinidir}/tests/requirements.txt -r{toxinidir}/stubs.txt pprint36 commands = mypy domdf_python_tools tests {posargs} [testenv:pyup] -basepython = python3.8 +basepython = python3.9 skip_install = True ignore_errors = True changedir = {toxinidir} @@ -168,7 +166,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 @@ -216,7 +214,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