Skip to content

Commit cadac8a

Browse files
authored
Migrate to hatchling, add Python 3.14, drop 3.9, bump linters (#11)
1 parent a325112 commit cadac8a

13 files changed

Lines changed: 150 additions & 80 deletions

File tree

.github/workflows/publish.yml

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,49 @@
11
name: publish
2+
3+
permissions:
4+
contents: read
5+
26
on:
37
push:
48
tags:
59
- "[0-9]+.[0-9]+.[0-9]+"
610
jobs:
7-
deploy:
11+
build:
12+
name: Build distribution
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v6
16+
with:
17+
persist-credentials: false
18+
- name: Set up Python
19+
uses: actions/setup-python@v6
20+
with:
21+
python-version: "3.14"
22+
- name: Install pypa/build
23+
run: python3 -m pip install build --user
24+
- name: Build a binary wheel and a source tarball
25+
run: python3 -m build
26+
- name: Store the distribution packages
27+
uses: actions/upload-artifact@v7
28+
with:
29+
name: python-package-distributions
30+
path: dist/
31+
32+
publish:
33+
name: Publish to PyPI
34+
needs:
35+
- build
836
runs-on: ubuntu-latest
937
environment:
1038
name: pypi
1139
url: https://pypi.org/p/form2request
1240
permissions:
1341
id-token: write
1442
steps:
15-
- uses: actions/checkout@v4
16-
- name: Set up Python
17-
uses: actions/setup-python@v5
43+
- name: Download all the dists
44+
uses: actions/download-artifact@v8
1845
with:
19-
python-version: '3.13'
20-
- name: Install dependencies
21-
run: |
22-
python -m pip install --upgrade pip
23-
pip install build
24-
- name: Build package
25-
run: python -m build
26-
- name: Publish package
27-
uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # 1.9.0
46+
name: python-package-distributions
47+
path: dist/
48+
- name: Publish distribution to PyPI
49+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # 1.14.0

.github/workflows/test.yml

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
name: tox
2+
3+
permissions:
4+
contents: read
5+
26
on:
37
push:
48
branches: [ main ]
@@ -11,22 +15,24 @@ jobs:
1115
fail-fast: false
1216
matrix:
1317
include:
14-
- python-version: '3.9'
18+
- python-version: '3.10'
1519
toxenv: min
16-
- python-version: '3.9'
20+
- python-version: '3.10'
1721
toxenv: min-extra
18-
- python-version: '3.9'
1922
- python-version: '3.10'
2023
- python-version: '3.11'
2124
- python-version: '3.12'
2225
- python-version: '3.13'
23-
- python-version: '3.13'
26+
- python-version: '3.14'
27+
- python-version: '3.14'
2428
toxenv: extra
2529

2630
steps:
27-
- uses: actions/checkout@v4
31+
- uses: actions/checkout@v6
32+
with:
33+
persist-credentials: false
2834
- name: Set up Python ${{ matrix.python-version }}
29-
uses: actions/setup-python@v5
35+
uses: actions/setup-python@v6
3036
with:
3137
python-version: ${{ matrix.python-version }}
3238
- name: Install dependencies
@@ -36,20 +42,26 @@ jobs:
3642
- name: tox
3743
run: |
3844
tox -e ${{ matrix.toxenv || 'py' }}
39-
- name: coverage
40-
if: ${{ success() }}
41-
uses: codecov/codecov-action@v5
45+
- name: Upload coverage report
46+
uses: codecov/codecov-action@v6
47+
- name: Upload test results
48+
if: ${{ !cancelled() }}
49+
uses: codecov/codecov-action@v6
50+
with:
51+
report_type: test_results
4252
check:
4353
runs-on: ubuntu-latest
4454
strategy:
4555
fail-fast: false
4656
matrix:
47-
python-version: ["3.13"]
57+
python-version: ["3.14"]
4858
tox-job: ["pre-commit", "mypy", "docs", "doctest", "twinecheck"]
4959
steps:
50-
- uses: actions/checkout@v4
60+
- uses: actions/checkout@v6
61+
with:
62+
persist-credentials: false
5163
- name: Set up Python ${{ matrix.python-version }}
52-
uses: actions/setup-python@v5
64+
uses: actions/setup-python@v6
5365
with:
5466
python-version: ${{ matrix.python-version }}
5567
- name: Install dependencies

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
*.egg-info/
33
/.coverage
44
/coverage.xml
5+
*.junit.xml
56
/build/
67
/dist/
78
/.tox/

.pre-commit-config.yaml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.11.2
3+
rev: v0.15.17
44
hooks:
5-
- id: ruff
5+
- id: ruff-check
66
args: [ --fix ]
77
- id: ruff-format
88
- repo: https://github.com/adamchainz/blacken-docs
9-
rev: 1.19.1
9+
rev: 1.20.0
1010
hooks:
1111
- id: blacken-docs
1212
additional_dependencies:
13-
- black==25.1.0
13+
- black==26.3.1
14+
- repo: https://github.com/pre-commit/pre-commit-hooks
15+
rev: v6.0.0
16+
hooks:
17+
- id: end-of-file-fixer
18+
- id: trailing-whitespace
19+
- repo: https://github.com/sphinx-contrib/sphinx-lint
20+
rev: v1.0.2
21+
hooks:
22+
- id: sphinx-lint
23+
- repo: https://github.com/rhysd/actionlint
24+
rev: v1.7.12
25+
hooks:
26+
- id: actionlint

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ sphinx:
55
build:
66
os: ubuntu-24.04
77
tools:
8-
python: "3.13" # Keep in sync with .github/workflows/test.yml
8+
python: "3.14" # Keep in sync with .github/workflows/test.yml
99
python:
1010
install:
1111
- requirements: docs/requirements.txt

MANIFEST.in

Lines changed: 0 additions & 1 deletion
This file was deleted.

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ form2request
2020

2121
.. description starts
2222
23-
``form2request`` is a Python 3.9+ library to build HTTP requests out of HTML
23+
``form2request`` is a Python library to build HTTP requests out of HTML
2424
forms.
2525

2626
.. description ends

docs/conf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
project = "form2request"
2-
copyright = "Zyte Group Ltd"
2+
project_copyright = "Zyte Group Ltd"
33
release = "0.2.0"
44

55
extensions = [
@@ -30,6 +30,8 @@
3030
"HtmlElement",
3131
"Selector",
3232
"SelectorList",
33+
# documented there as web_poet.page_inputs.http.HttpRequest, may need :canonical:
34+
"web_poet.HttpRequest",
3335
)
3436
),
3537
]

docs/usage.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,13 @@ software.
258258
:class:`~form2request.Request` also provides conversion methods for common use
259259
cases:
260260

261-
- :meth:`~form2request.Request.to_scrapy`, for :doc:`Scrapy 1.1.0+
261+
- :meth:`~form2request.Request.to_scrapy`, for :doc:`Scrapy 1.7.1+
262262
<scrapy:index>`:
263263

264264
>>> request_data.to_scrapy(callback=self.parse) # doctest: +SKIP
265265
<GET https://example.com?foo=bar>
266266

267-
- :meth:`~form2request.Request.to_requests`, for :doc:`requests 1.0.0+
267+
- :meth:`~form2request.Request.to_requests`, for :doc:`requests 2.8.0+
268268
<requests:index>` (see an example :ref:`above <requests-example>`).
269269

270270
- :meth:`~form2request.Request.to_poet`, for :doc:`web-poet 0.2.0+

form2request/_base.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
from __future__ import annotations
22

33
import uuid
4-
from collections.abc import Iterable
4+
from collections.abc import Callable, Iterable
55
from dataclasses import dataclass
6-
from typing import (
7-
TYPE_CHECKING,
8-
Any,
9-
Callable,
10-
Optional,
11-
Union,
12-
cast,
13-
)
6+
from typing import TYPE_CHECKING, Any, TypeAlias, cast
147
from urllib.parse import urlencode, urljoin, urlsplit, urlunsplit
158

169
from parsel import Selector, SelectorList
1710
from w3lib.html import strip_html5_whitespace
1811

1912
if TYPE_CHECKING:
13+
import requests
14+
import scrapy
15+
import web_poet
2016
from lxml.html import FormElement, HtmlElement
2117

2218

@@ -29,9 +25,9 @@ class FileField:
2925
content_type: str = "application/octet-stream"
3026

3127

32-
FormdataVType = Union[str, FileField, Iterable[str]]
33-
FormdataKVType = tuple[str, FormdataVType]
34-
FormdataType = Optional[Union[dict[str, FormdataVType], Iterable[FormdataKVType]]]
28+
FormdataVType: TypeAlias = str | FileField | Iterable[str]
29+
FormdataKVType: TypeAlias = tuple[str, FormdataVType]
30+
FormdataType: TypeAlias = dict[str, FormdataVType] | Iterable[FormdataKVType] | None
3531

3632

3733
def _parsel_to_lxml(
@@ -211,14 +207,14 @@ class Request:
211207
headers: list[tuple[str, str]]
212208
body: bytes
213209

214-
def to_poet(self, **kwargs: Any):
210+
def to_poet(self, **kwargs: Any) -> web_poet.HttpRequest:
215211
"""Convert the request to :class:`web_poet.HttpRequest
216212
<web_poet.page_inputs.http.HttpRequest>`.
217213
218214
All *kwargs* are passed to :class:`web_poet.HttpRequest
219215
<web_poet.page_inputs.http.HttpRequest>` as is.
220216
"""
221-
import web_poet
217+
import web_poet # noqa: PLC0415
222218

223219
return web_poet.HttpRequest(
224220
url=self.url,
@@ -228,12 +224,12 @@ def to_poet(self, **kwargs: Any):
228224
**kwargs,
229225
)
230226

231-
def to_requests(self, **kwargs: Any):
227+
def to_requests(self, **kwargs: Any) -> requests.PreparedRequest:
232228
"""Convert the request to :class:`requests.PreparedRequest`.
233229
234230
All *kwargs* are passed to :class:`requests.Request` as is.
235231
"""
236-
import requests
232+
import requests # noqa: PLC0415
237233

238234
request = requests.Request(
239235
self.method,
@@ -244,12 +240,12 @@ def to_requests(self, **kwargs: Any):
244240
)
245241
return request.prepare()
246242

247-
def to_scrapy(self, callback: Callable, **kwargs: Any):
243+
def to_scrapy(self, callback: Callable, **kwargs: Any) -> scrapy.Request:
248244
"""Convert the request to :class:`scrapy.Request`.
249245
250246
All *kwargs* are passed to :class:`scrapy.Request` as is.
251247
"""
252-
import scrapy
248+
import scrapy # noqa: PLC0415
253249

254250
return scrapy.Request(
255251
self.url,

0 commit comments

Comments
 (0)