Skip to content

Commit e2a28fc

Browse files
authored
Drop Python 3.9, reinstate 3.14/pydantic jobs (#112)
1 parent a358dfe commit e2a28fc

File tree

15 files changed

+114
-115
lines changed

15 files changed

+114
-115
lines changed

.github/workflows/checks.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
include:
17-
- python-version: 3.13
17+
- python-version: "3.14"
1818
env:
1919
TOXENV: typing
20-
- python-version: 3.13
20+
- python-version: "3.14"
2121
env:
2222
TOXENV: docs
23-
- python-version: 3.13
23+
- python-version: "3.14"
2424
env:
2525
TOXENV: twinecheck
26-
- python-version: 3.13
26+
- python-version: "3.14"
2727
env:
2828
TOXENV: pylint
2929

3030
steps:
31-
- uses: actions/checkout@v5
31+
- uses: actions/checkout@v6
3232

3333
- name: Set up Python ${{ matrix.python-version }}
3434
uses: actions/setup-python@v6
@@ -45,5 +45,5 @@ jobs:
4545
pre-commit:
4646
runs-on: ubuntu-latest
4747
steps:
48-
- uses: actions/checkout@v5
48+
- uses: actions/checkout@v6
4949
- uses: pre-commit/action@v3.0.1

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ jobs:
1212
permissions:
1313
id-token: write
1414
steps:
15-
- uses: actions/checkout@v5
15+
- uses: actions/checkout@v6
1616
- uses: actions/setup-python@v6
1717
with:
18-
python-version: 3.13
18+
python-version: "3.14"
1919
- run: |
2020
python -m pip install --upgrade build
2121
python -m build

.github/workflows/tests.yml

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,18 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
include:
18-
- python-version: "3.9"
18+
- python-version: "3.10"
1919
env:
2020
TOXENV: min-attrs
21-
- python-version: "3.9"
21+
- python-version: "3.10"
2222
env:
2323
TOXENV: min-pydantic
24-
- python-version: "3.9"
24+
- python-version: "3.10"
2525
env:
2626
TOXENV: min-scrapy
27-
- python-version: "3.9"
27+
- python-version: "3.10"
2828
env:
2929
TOXENV: min-extra
30-
- python-version: "3.9"
31-
env:
32-
TOXENV: py
3330
- python-version: "3.10"
3431
env:
3532
TOXENV: py
@@ -42,21 +39,19 @@ jobs:
4239
- python-version: "3.13"
4340
env:
4441
TOXENV: py
45-
- python-version: "3.14.0-rc.2"
42+
- python-version: "3.14"
4643
env:
4744
TOXENV: py
48-
- python-version: "3.14.0-rc.2"
45+
- python-version: "3.14"
4946
env:
5047
TOXENV: attrs
51-
# pydantic doesn't yet support 3.14
52-
- python-version: "3.13"
48+
- python-version: "3.14"
5349
env:
5450
TOXENV: pydantic
55-
- python-version: "3.14.0-rc.2"
51+
- python-version: "3.14"
5652
env:
5753
TOXENV: scrapy
58-
# pydantic doesn't yet support 3.14
59-
- python-version: "3.13"
54+
- python-version: "3.14"
6055
env:
6156
TOXENV: extra
6257
- python-version: "pypy3.11"
@@ -73,7 +68,7 @@ jobs:
7368

7469

7570
steps:
76-
- uses: actions/checkout@v5
71+
- uses: actions/checkout@v6
7772

7873
- name: Set up Python ${{ matrix.python-version }}
7974
uses: actions/setup-python@v6
@@ -91,19 +86,19 @@ jobs:
9186
uses: codecov/codecov-action@v5
9287

9388
tests-other-os:
94-
name: "Test: py39, ${{ matrix.os }}"
89+
name: "Test: py310, ${{ matrix.os }}"
9590
runs-on: "${{ matrix.os }}"
9691
strategy:
9792
matrix:
9893
os: [macos-latest, windows-latest]
9994

10095
steps:
101-
- uses: actions/checkout@v5
96+
- uses: actions/checkout@v6
10297

10398
- name: Set up Python
10499
uses: actions/setup-python@v6
105100
with:
106-
python-version: 3.9
101+
python-version: "3.10"
107102

108103
- name: Install tox
109104
run: pip install tox

.pre-commit-config.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.13.0
3+
rev: v0.15.2
44
hooks:
55
- id: ruff-check
66
args: [ --fix ]
@@ -10,3 +10,7 @@ repos:
1010
hooks:
1111
- id: end-of-file-fixer
1212
- id: trailing-whitespace
13+
- repo: https://github.com/rhysd/actionlint
14+
rev: v1.7.11
15+
hooks:
16+
- id: actionlint

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ a pre-defined interface (see [extending `itemadapter`](#extending-itemadapter)).
2424

2525
## Requirements
2626

27-
* Python 3.9+, either the CPython implementation (default) or the PyPy
27+
* Python 3.10+, either the CPython implementation (default) or the PyPy
2828
implementation
2929
* [`scrapy`](https://scrapy.org/) 2.2+: optional, needed to interact with
3030
`scrapy` items

itemadapter/_imports.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import sys
34
from typing import Any
45

56
# attempt the following imports only once,
@@ -33,34 +34,45 @@
3334
except ImportError:
3435
attr = None
3536

36-
pydantic_v1: Any = None
3737
pydantic: Any = None
38+
PydanticUndefined: Any = None
3839

39-
try:
40-
import pydantic
41-
except ImportError: # No pydantic
42-
pass
43-
else:
40+
pydantic_v1: Any = None
41+
PydanticV1Undefined: Any = None
42+
43+
# pydantic v1 doesn't support Python 3.14+, on older Python versions we try to find both
44+
if sys.version_info < (3, 14):
4445
try:
45-
import pydantic.v1 as pydantic_v1
46-
except ImportError: # Pydantic <1.10.17
47-
pydantic_v1 = pydantic
48-
pydantic = None
49-
else: # Pydantic 1.10.17+
50-
if not hasattr(pydantic.BaseModel, "model_fields"): # Pydantic <2
46+
import pydantic
47+
except ImportError: # No pydantic
48+
pass
49+
else:
50+
try:
51+
import pydantic.v1 as pydantic_v1
52+
except ImportError: # Pydantic <1.10.17
5153
pydantic_v1 = pydantic
5254
pydantic = None
55+
else: # Pydantic 1.10.17+
56+
if not hasattr(pydantic.BaseModel, "model_fields"): # Pydantic >=1.10.17,<2
57+
pydantic_v1 = pydantic
58+
pydantic = None
59+
# else Pydantic >=2
5360

54-
try:
55-
from pydantic.v1.fields import Undefined as PydanticV1Undefined
56-
from pydantic_core import PydanticUndefined
57-
except ImportError: # < Pydantic 2.0
5861
try:
59-
from pydantic.fields import ( # type: ignore[attr-defined,no-redef]
60-
Undefined as PydanticUndefined,
61-
)
62-
from pydantic.fields import ( # type: ignore[attr-defined,no-redef]
63-
Undefined as PydanticV1Undefined,
64-
)
65-
except ImportError:
66-
PydanticUndefined = PydanticV1Undefined = None # type: ignore[assignment]
62+
from pydantic.v1.fields import Undefined as PydanticV1Undefined
63+
from pydantic_core import PydanticUndefined
64+
except ImportError: # Pydantic < 2.0
65+
try:
66+
from pydantic.fields import ( # type: ignore[attr-defined,no-redef]
67+
Undefined as PydanticUndefined,
68+
)
69+
70+
PydanticV1Undefined = PydanticUndefined
71+
except ImportError:
72+
pass
73+
else:
74+
try:
75+
import pydantic
76+
from pydantic_core import PydanticUndefined
77+
except ImportError: # No pydantic
78+
pass

itemadapter/_json_schema.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from copy import copy
1010
from enum import Enum
1111
from textwrap import dedent
12+
from types import MappingProxyType, UnionType
1213
from typing import (
1314
TYPE_CHECKING,
1415
Any,
@@ -24,8 +25,6 @@
2425
from .utils import _is_pydantic_model
2526

2627
if TYPE_CHECKING:
27-
from types import MappingProxyType
28-
2928
from .adapter import AdapterInterface, ItemAdapter
3029

3130

@@ -139,20 +138,15 @@ def array_type(type_hint):
139138
unique_args = set(args)
140139
if len(unique_args) == 1:
141140
return next(iter(unique_args))
142-
return Union[tuple(unique_args)]
141+
return Union[tuple(unique_args)] # noqa: UP007
143142

144143

145144
def update_prop_from_pattern(prop: dict[str, Any], pattern: str) -> None:
146145
if is_valid_pattern(pattern):
147146
prop.setdefault("pattern", pattern)
148147

149148

150-
try:
151-
from types import UnionType
152-
except ImportError: # Python < 3.10
153-
UNION_TYPES: set[Any] = {Union}
154-
else:
155-
UNION_TYPES = {Union, UnionType}
149+
UNION_TYPES = {Union, UnionType}
156150

157151

158152
def update_prop_from_origin(
@@ -204,7 +198,7 @@ def update_prop_from_type(prop: dict[str, Any], prop_type: Any, state: _JsonSche
204198
if issubclass(prop_type, Enum):
205199
values = [item.value for item in prop_type]
206200
value_types = tuple({type(v) for v in values})
207-
prop_type = value_types[0] if len(value_types) == 1 else Union[value_types]
201+
prop_type = value_types[0] if len(value_types) == 1 else Union[value_types] # noqa: UP007
208202
update_prop_from_type(prop, prop_type, state)
209203
prop.setdefault("enum", values)
210204
return

pyproject.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ authors = [
1111
readme = "README.md"
1212
license = "BSD-3-Clause"
1313
license-files = ["LICENSE"]
14-
requires-python = ">=3.9"
14+
requires-python = ">=3.10"
1515
classifiers = [
1616
"Development Status :: 3 - Alpha",
1717
"Intended Audience :: Developers",
1818
"Programming Language :: Python",
1919
"Programming Language :: Python :: 3",
20-
"Programming Language :: Python :: 3.9",
2120
"Programming Language :: Python :: 3.10",
2221
"Programming Language :: Python :: 3.11",
2322
"Programming Language :: Python :: 3.12",
2423
"Programming Language :: Python :: 3.13",
24+
"Programming Language :: Python :: 3.14",
2525
"Programming Language :: Python :: Implementation :: CPython",
2626
"Programming Language :: Python :: Implementation :: PyPy",
2727
"Operating System :: OS Independent",
@@ -74,6 +74,9 @@ regex = true
7474
[[tool.bumpversion.files]]
7575
filename = "itemadapter/__init__.py"
7676

77+
[tool.coverage.run]
78+
branch = true
79+
7780
[tool.pylint.MASTER]
7881
persistent = "no"
7982
load-plugins=[
@@ -257,7 +260,3 @@ split-on-trailing-comma = false
257260

258261
[tool.ruff.lint.pydocstyle]
259262
convention = "pep257"
260-
261-
[tool.ruff.lint.pyupgrade]
262-
# for Pydantic annotations while we support Python 3.9
263-
keep-runtime-typing = true

0 commit comments

Comments
 (0)