Skip to content

Commit daf41af

Browse files
authored
Merge pull request #208 from scrapinghub/modernize
Add Python 3.13, drop Python 3.8, update tool versions
2 parents 4120db7 + 944f367 commit daf41af

15 files changed

+37
-67
lines changed

.github/workflows/publish.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Python
1818
uses: actions/setup-python@v5
1919
with:
20-
python-version: '3.x'
20+
python-version: '3.13'
2121
- name: Install dependencies
2222
run: |
2323
python -m pip install --upgrade pip

.github/workflows/tests-ubuntu.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
18+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1919
env:
2020
- TOXENV: py
2121
include:
22-
- python-version: '3.8'
22+
- python-version: '3.9'
2323
env:
2424
TOXENV: pinned
2525

@@ -48,7 +48,7 @@ jobs:
4848
strategy:
4949
fail-fast: false
5050
matrix:
51-
python-version: ['3.12']
51+
python-version: ['3.13']
5252
tox-job: ["mypy", "docs", "linters", "types", "twinecheck"]
5353

5454
steps:

.github/workflows/tests-windows.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
18+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1919
env:
2020
- TOXENV: py
2121
include:
22-
- python-version: '3.8'
22+
- python-version: '3.9'
2323
env:
2424
TOXENV: pinned
2525

.pre-commit-config.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repos:
44
language_version: python3
55
exclude: ^docs/tutorial-project/
66
repo: https://github.com/ambv/black
7-
rev: 24.2.0
7+
rev: 24.10.0
88
- hooks:
99
- id: isort
1010
language_version: python3
@@ -22,4 +22,4 @@ repos:
2222
- flake8-docstrings
2323
- flake8-string-format
2424
repo: https://github.com/pycqa/flake8
25-
rev: 7.0.0
25+
rev: 7.1.1

.readthedocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ sphinx:
66
build:
77
os: ubuntu-22.04
88
tools:
9-
python: "3.11" # Keep in sync with .github/workflows/tests-ubuntu.yml
9+
python: "3.13" # Keep in sync with .github/workflows/tests-ubuntu.yml
1010

1111
python:
1212
install:

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ web-poet
2828

2929
.. intro starts
3030
31-
``web-poet`` is a Python 3.8+ implementation of the `page object pattern`_ for
31+
``web-poet`` is a Python 3.9+ implementation of the `page object pattern`_ for
3232
web scraping. It enables writing portable, reusable web parsing code.
3333

3434
.. _page object pattern: https://martinfowler.com/bliki/PageObject.html

docs/conf.py

+1-9
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import os
88
import sys
99

10-
import sphinx_rtd_theme
11-
1210
# -- Path setup --------------------------------------------------------------
1311

1412
# If extensions (or modules to document with autodoc) are in another directory,
@@ -54,7 +52,7 @@
5452
# You can specify multiple suffix as a list of string:
5553
#
5654
# source_suffix = ['.rst', '.md']
57-
source_suffix = ".rst"
55+
source_suffix = {".rst": "restructuredtext"}
5856

5957
# The master toctree document.
6058
master_doc = "index"
@@ -82,12 +80,6 @@
8280
#
8381
html_theme = "sphinx_rtd_theme"
8482

85-
# Add any paths that contain custom themes here, relative to this directory.
86-
# Add path to the RTD explicitly to robustify builds (otherwise might
87-
# fail in a clean Debian build env)
88-
89-
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
90-
9183
# Theme options are theme-specific and customize the look and feel of a theme
9284
# further. For a list of options available for each theme, see the
9385
# documentation.

docs/requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Sphinx==5.3.0
2-
sphinx-rtd-theme==1.0.0
1+
Sphinx==8.1.3
2+
sphinx-rtd-theme==3.0.1

setup.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"python-dateutil >= 2.7.0",
3131
"time-machine >= 2.7.1",
3232
"packaging >= 20.0",
33-
"backports.zoneinfo >= 0.2.1; python_version < '3.9' and platform_system != 'Windows'",
3433
],
3534
classifiers=[
3635
"Development Status :: 3 - Alpha",
@@ -39,10 +38,10 @@
3938
"Natural Language :: English",
4039
"Operating System :: OS Independent",
4140
"Programming Language :: Python :: 3",
42-
"Programming Language :: Python :: 3.8",
4341
"Programming Language :: Python :: 3.9",
4442
"Programming Language :: Python :: 3.10",
4543
"Programming Language :: Python :: 3.11",
4644
"Programming Language :: Python :: 3.12",
45+
"Programming Language :: Python :: 3.13",
4746
],
4847
)

tests/test_serialization.py

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import sys
2-
from typing import Type
1+
from typing import Annotated, Type
32

43
import attrs
54
import pytest
@@ -218,12 +217,7 @@ def test_httpclient_empty(tmp_path) -> None:
218217
assert "HttpClient" in read_serialized_deps
219218

220219

221-
@pytest.mark.skipif(
222-
sys.version_info < (3, 9), reason="No Annotated support in Python < 3.9"
223-
)
224220
def test_annotated(book_list_html_response) -> None:
225-
from typing import Annotated
226-
227221
@attrs.define
228222
class MyWebPage(ItemPage):
229223
response: Annotated[HttpResponse, "foo", 42]
@@ -243,9 +237,6 @@ class MyWebPage(ItemPage):
243237
_assert_pages_equal(po, deserialized_po)
244238

245239

246-
@pytest.mark.skipif(
247-
sys.version_info < (3, 9), reason="No Annotated support in Python < 3.9"
248-
)
249240
def test_annotated_duplicate(book_list_html_response) -> None:
250241
url_str = "http://books.toscrape.com/index.html"
251242
url = ResponseUrl(url_str)

tests/test_testing.py

+9-19
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import datetime
22
import json
3-
import sys
43
from collections import deque
54
from pathlib import Path
6-
from typing import Any, Dict, Optional
5+
from typing import Annotated, Any, Dict, Optional
6+
from zoneinfo import ZoneInfo
77

88
import attrs
99
import dateutil.tz
@@ -223,7 +223,8 @@ class MetadataLocalTime(Metadata):
223223

224224
@attrs.define(kw_only=True)
225225
class ProductLocalTime(Product):
226-
metadata: Optional[MetadataLocalTime]
226+
# in newer zyte-common-items this should inherit from ProductMetadata
227+
metadata: Optional[MetadataLocalTime] # type: ignore[assignment]
227228

228229

229230
def _get_product_item(date: datetime.datetime) -> ProductLocalTime:
@@ -282,11 +283,6 @@ def test_pytest_frozen_time_naive(pytester, book_list_html_response) -> None:
282283
@pytest.mark.skipif(not time_machine.HAVE_TZSET, reason="Not supported on Windows")
283284
@pytest.mark.parametrize("offset", [-5, 0, 8])
284285
def test_pytest_frozen_time_tz(pytester, book_list_html_response, offset) -> None:
285-
if sys.version_info >= (3, 9):
286-
from zoneinfo import ZoneInfo
287-
else:
288-
from backports.zoneinfo import ZoneInfo
289-
290286
tzinfo = ZoneInfo(f"Etc/GMT{-offset:+d}")
291287
frozen_time = datetime.datetime(2022, 3, 4, 20, 21, 22, tzinfo=tzinfo)
292288
_assert_frozen_item(frozen_time, pytester, book_list_html_response)
@@ -540,20 +536,14 @@ def test_page_object_exception_none(pytester, book_list_html_response) -> None:
540536
result.assert_outcomes(failed=1)
541537

542538

543-
if sys.version_info >= (3, 9):
544-
from typing import Annotated
545-
546-
@attrs.define(kw_only=True)
547-
class MyAnnotatedItemPage(MyItemPage):
548-
response: Annotated[HttpResponse, "foo", 42]
539+
@attrs.define(kw_only=True)
540+
class MyAnnotatedItemPage(MyItemPage):
541+
response: Annotated[HttpResponse, "foo", 42]
549542

550-
async def to_item(self) -> dict:
551-
return {"foo": "bar"}
543+
async def to_item(self) -> dict:
544+
return {"foo": "bar"}
552545

553546

554-
@pytest.mark.skipif(
555-
sys.version_info < (3, 9), reason="No Annotated support in Python < 3.9"
556-
)
557547
def test_annotated(pytester, book_list_html_response) -> None:
558548
_save_fixture(
559549
pytester,

tox.ini

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py38,py39,py310,py311,py312,mypy,docs,types
2+
envlist = py39,py310,py311,py312,py313,mypy,docs,types
33

44
[pytest]
55
asyncio_mode = strict
@@ -31,9 +31,13 @@ commands =
3131

3232
[testenv:mypy]
3333
deps =
34-
mypy==1.8.0
34+
mypy==1.11.2
35+
aiohttp
36+
py
37+
pytest
3538
types-requests
3639
types-python-dateutil
40+
zyte-common-items
3741

3842
commands = mypy web_poet tests
3943

@@ -58,7 +62,7 @@ deps = -rrequirements-dev.txt
5862
commands = pre-commit run --all-files --show-diff-on-failure
5963

6064
[testenv:pinned]
61-
basepython = python3.8
65+
basepython = python3.9
6266
deps =
6367
{[testenv]deps}
6468
pytest==6.2.0
@@ -78,14 +82,13 @@ deps =
7882
python-dateutil==2.7.0
7983
time-machine==2.7.1
8084
packaging==20.0
81-
backports.zoneinfo==0.2.1 ; platform_system != 'Windows'
8285

8386

8487
[testenv:twinecheck]
8588
basepython = python3
8689
deps =
8790
twine==5.1.1
88-
build==0.10.0
91+
build==1.2.2
8992
commands =
9093
python -m build --sdist
9194
twine check dist/*

web_poet/fields.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import inspect
77
from contextlib import suppress
88
from functools import update_wrapper, wraps
9-
from typing import Callable, Dict, List, Optional, Tuple, Type, TypeVar
9+
from typing import Callable, Dict, List, Optional, Tuple, Type, TypeVar, cast
1010

1111
import attrs
1212
from itemadapter import ItemAdapter
@@ -163,7 +163,7 @@ def processed(page):
163163
if method is not None:
164164
# @field syntax
165165
res = _field(method)
166-
update_wrapper(res, method)
166+
update_wrapper(cast(Callable, res), method)
167167
return res
168168
else:
169169
# @field(...) syntax

web_poet/testing/fixture.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import json
44
import logging
55
import os
6-
import sys
76
from pathlib import Path
87
from typing import Any, Iterable, Optional, Type, TypeVar, Union, cast
8+
from zoneinfo import ZoneInfo
99

1010
import dateutil.parser
1111
import dateutil.tz
@@ -181,11 +181,6 @@ def _parse_frozen_time(meta_value: str) -> datetime.datetime:
181181
)
182182
return parsed_value.astimezone()
183183

184-
if sys.version_info >= (3, 9):
185-
from zoneinfo import ZoneInfo
186-
else:
187-
from backports.zoneinfo import ZoneInfo
188-
189184
if parsed_value.tzinfo == dateutil.tz.UTC:
190185
return parsed_value.replace(tzinfo=ZoneInfo("UTC"))
191186

web_poet/testing/pytest.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,15 @@ def collect(self) -> Iterable[Union[pytest.Item, pytest.Collector]]:
7171
WebPoetItem.from_parent(parent=self, name="item", fixture=self.fixture)
7272
]
7373
else:
74-
overall_tests = [
74+
overall_tests: List[pytest.Item] = [
7575
WebPoetNoToItemException.from_parent(
7676
parent=self, name="TO_ITEM_DOESNT_RAISE", fixture=self.fixture
7777
),
7878
WebPoetNoExtraFieldsItem.from_parent(
7979
parent=self, name="NO_EXTRA_FIELDS", fixture=self.fixture
8080
),
8181
]
82-
field_tests = [
82+
field_tests: List[pytest.Item] = [
8383
WebPoetFieldItem.from_parent(
8484
parent=self, name=field, fixture=self.fixture, field_name=field
8585
)

0 commit comments

Comments
 (0)