diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 4d6b5a9..114dc57 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -21,24 +21,24 @@ jobs: - python-version: '3.13' env: TOXENV: docs - - python-version: '3.13' + - python-version: '3.14' env: TOXENV: pre-commit - - python-version: '3.13' + - python-version: '3.14' env: TOXENV: pylint - - python-version: '3.13' + - python-version: '3.14' env: TOXENV: typing - - python-version: '3.13' + - python-version: '3.14' env: TOXENV: twinecheck steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5cb1ffb..f8d885b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v6 with: - python-version: '3.13' + python-version: '3.14' - name: Check Tag id: check-release-tag diff --git a/.github/workflows/tests-macos.yml b/.github/workflows/tests-macos.yml index 6682858..7713e3e 100644 --- a/.github/workflows/tests-macos.yml +++ b/.github/workflows/tests-macos.yml @@ -17,20 +17,18 @@ jobs: fail-fast: false matrix: python-version: - - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' - - 3.14.0-rc.1 - - pypy3.10 + - '3.14' - pypy3.11 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/tests-ubuntu.yml b/.github/workflows/tests-ubuntu.yml index 7f05f94..d289ce4 100644 --- a/.github/workflows/tests-ubuntu.yml +++ b/.github/workflows/tests-ubuntu.yml @@ -17,20 +17,18 @@ jobs: fail-fast: false matrix: python-version: - - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' - - 3.14.0-rc.1 - - pypy3.10 + - '3.14' - pypy3.11 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/tests-windows.yml b/.github/workflows/tests-windows.yml index c846e54..67591dd 100644 --- a/.github/workflows/tests-windows.yml +++ b/.github/workflows/tests-windows.yml @@ -17,20 +17,18 @@ jobs: fail-fast: false matrix: python-version: - - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' - - 3.14.0-rc.1 - - pypy3.10 + - '3.14' - pypy3.11 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c59a32..e4b3909 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,19 @@ --- repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.5 + rev: v0.14.4 hooks: - id: ruff-check args: [--fix] - id: ruff-format - repo: https://github.com/adamchainz/blacken-docs - rev: 1.19.1 + rev: 1.20.0 hooks: - id: blacken-docs additional_dependencies: - - black==25.1.0 + - black==25.9.0 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace diff --git a/README.rst b/README.rst index bf95701..ce226f0 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ This is a Python library of web-related functions, such as: Requirements ============ -Python 3.9+ +Python 3.10+ Install ======= diff --git a/docs/index.rst b/docs/index.rst index c89d2c0..bff851d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,7 +28,7 @@ Modules Requirements ============ -Python 3.9+ +Python 3.10+ Install ======= diff --git a/pyproject.toml b/pyproject.toml index 105017d..4087950 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,7 +27,7 @@ classifiers = [ "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dynamic = ["version"] [project.urls] @@ -66,18 +65,8 @@ filename = "docs/conf.py" [tool.coverage.run] branch = true -[tool.coverage.report] -exclude_also = [ - "if TYPE_CHECKING:", -] - [tool.mypy] -check_untyped_defs = true - -[[tool.mypy.overrides]] -# All non-tests functions must be typed. -module = "w3lib.*" -allow_untyped_defs = false +strict = true [[tool.mypy.overrides]] # Allow test functions to be untyped diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 3502ed7..cfd2ece 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -137,7 +137,7 @@ class TestHtmlConversion: def test_unicode_body(self): unicode_string = "\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0442\u0435\u043a\u0441\u0442" original_string = unicode_string.encode("cp1251") - encoding, body_unicode = html_to_unicode(ct("cp1251"), original_string) + _, body_unicode = html_to_unicode(ct("cp1251"), original_string) # check body_as_unicode assert isinstance(body_unicode, str) assert body_unicode == unicode_string @@ -207,7 +207,7 @@ def test_utf8_unexpected_end_of_data_with_valid_utf8_BOM(self): def test_replace_wrong_encoding(self): """Test invalid chars are replaced properly""" - encoding, body_unicode = html_to_unicode(ct("utf-8"), b"PREFIX\xe3\xabSUFFIX") + _, body_unicode = html_to_unicode(ct("utf-8"), b"PREFIX\xe3\xabSUFFIX") # XXX: Policy for replacing invalid chars may suffer minor variations # but it should always contain the unicode replacement char ('\ufffd') assert "\ufffd" in body_unicode, repr(body_unicode) @@ -215,7 +215,7 @@ def test_replace_wrong_encoding(self): assert "SUFFIX" in body_unicode, repr(body_unicode) # Do not destroy html tags due to encoding bugs - encoding, body_unicode = html_to_unicode(ct("utf-8"), b"\xf0value") + _, body_unicode = html_to_unicode(ct("utf-8"), b"\xf0value") assert "value" in body_unicode, repr(body_unicode) def _assert_encoding_detected( diff --git a/tests/test_url.py b/tests/test_url.py index c48d400..2b45282 100644 --- a/tests/test_url.py +++ b/tests/test_url.py @@ -4,7 +4,7 @@ import sys from inspect import isclass from pathlib import Path -from typing import Callable +from typing import TYPE_CHECKING from urllib.parse import urlparse import pytest @@ -32,6 +32,9 @@ url_query_parameter, ) +if TYPE_CHECKING: + from collections.abc import Callable + # Test cases for URL-to-safe-URL conversions with a URL and an encoding as # input parameters. # diff --git a/tox.ini b/tox.ini index 9a2de58..a729cc7 100644 --- a/tox.ini +++ b/tox.ini @@ -4,13 +4,12 @@ # and then run "tox" from this directory. [tox] -envlist = py39, py310, py311, py312, py313, py314, pypy3.10, pypy3.11, docs, pylint, typing, pre-commit, twinecheck +envlist = py310, py311, py312, py313, py314, pypy3.11, docs, pylint, typing, pre-commit, twinecheck [testenv] deps = - coverage >= 7.2.0 pytest !=3.1.1, !=3.1.2 - pytest-cov + pytest-cov >= 7.0.0 commands = python -m pytest \ --doctest-modules \ @@ -21,14 +20,14 @@ commands = basepython = python3 deps = pytest - mypy==1.17.0 + mypy==1.18.2 commands = - mypy --strict {posargs: w3lib tests} + mypy {posargs: w3lib tests} [testenv:pylint] deps = {[testenv]deps} - pylint==3.3.7 + pylint==4.0.2 commands = pylint docs tests w3lib @@ -46,8 +45,8 @@ skip_install = true [testenv:twinecheck] basepython = python3 deps = - twine==6.1.0 - build==1.2.2.post1 + twine==6.2.0 + build==1.3.0 commands = python -m build --sdist twine check dist/* diff --git a/w3lib/_types.py b/w3lib/_types.py index 93d20e9..a69c2e7 100644 --- a/w3lib/_types.py +++ b/w3lib/_types.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Union +from typing import TypeAlias # the base class UnicodeError doesn't have attributes like start / end -AnyUnicodeError = Union[UnicodeEncodeError, UnicodeDecodeError] +AnyUnicodeError: TypeAlias = UnicodeEncodeError | UnicodeDecodeError diff --git a/w3lib/encoding.py b/w3lib/encoding.py index 90802b2..0bbd39d 100644 --- a/w3lib/encoding.py +++ b/w3lib/encoding.py @@ -8,11 +8,13 @@ import encodings import re from re import Match -from typing import TYPE_CHECKING, Callable, cast +from typing import TYPE_CHECKING, cast import w3lib.util if TYPE_CHECKING: + from collections.abc import Callable + from w3lib._types import AnyUnicodeError _HEADER_ENCODING_RE = re.compile(r"charset=([\w-]+)", re.IGNORECASE) diff --git a/w3lib/http.py b/w3lib/http.py index fe47748..15bd785 100644 --- a/w3lib/http.py +++ b/w3lib/http.py @@ -3,12 +3,12 @@ from base64 import b64encode from collections.abc import Mapping, MutableMapping, Sequence from io import BytesIO -from typing import Any, Union, overload +from typing import Any, TypeAlias, overload from w3lib.util import to_bytes, to_unicode -HeadersDictInput = Mapping[bytes, Union[Any, Sequence[bytes]]] -HeadersDictOutput = MutableMapping[bytes, list[bytes]] +HeadersDictInput: TypeAlias = Mapping[bytes, Any | Sequence[bytes]] +HeadersDictOutput: TypeAlias = MutableMapping[bytes, list[bytes]] @overload diff --git a/w3lib/url.py b/w3lib/url.py index 566d227..bbefbbd 100644 --- a/w3lib/url.py +++ b/w3lib/url.py @@ -12,7 +12,7 @@ import re import string from pathlib import Path -from typing import TYPE_CHECKING, Callable, NamedTuple, cast, overload +from typing import TYPE_CHECKING, NamedTuple, cast, overload from urllib.parse import ( # type: ignore[attr-defined] ParseResult, _coerce_args, @@ -35,7 +35,7 @@ from .util import to_unicode if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from ._types import AnyUnicodeError