Skip to content

Commit 35c1108

Browse files
authored
fix: Add support for Pydantic v2.12 (#1471)
### Description - Add support pydantic v2.12. This uses a workaround to ensure compatibility with `Annotated[..., Field(default_factory=...)]` and with type hints. ### Issues - Closes: #1464
1 parent aa15753 commit 35c1108

File tree

4 files changed

+43
-38
lines changed

4 files changed

+43
-38
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ dependencies = [
4040
"protego>=0.5.0",
4141
"psutil>=6.0.0",
4242
"pydantic-settings>=2.2.0,!=2.7.0,!=2.7.1,!=2.8.0",
43-
"pydantic>=2.11.0,<2.12.0",
43+
"pydantic>=2.11.0",
4444
"pyee>=9.0.0",
4545
"tldextract>=5.1.0",
4646
"typing-extensions>=4.1.0",

src/crawlee/_request.py

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -185,33 +185,44 @@ class Request(BaseModel):
185185
method: HttpMethod = 'GET'
186186
"""HTTP request method."""
187187

188-
headers: Annotated[HttpHeaders, Field(default_factory=HttpHeaders)] = HttpHeaders()
189-
"""HTTP request headers."""
190-
191188
payload: Annotated[
192189
HttpPayload | None,
193190
BeforeValidator(lambda v: v.encode() if isinstance(v, str) else v),
194191
PlainSerializer(lambda v: v.decode() if isinstance(v, bytes) else v),
195192
] = None
196193
"""HTTP request payload."""
197194

198-
user_data: Annotated[
199-
dict[str, JsonSerializable], # Internally, the model contains `UserData`, this is just for convenience
200-
Field(alias='userData', default_factory=lambda: UserData()),
201-
PlainValidator(user_data_adapter.validate_python),
202-
PlainSerializer(
203-
lambda instance: user_data_adapter.dump_python(
204-
instance,
205-
by_alias=True,
206-
exclude_none=True,
207-
exclude_unset=True,
208-
exclude_defaults=True,
209-
)
210-
),
211-
] = {}
212-
"""Custom user data assigned to the request. Use this to save any request related data to the
213-
request's scope, keeping them accessible on retries, failures etc.
214-
"""
195+
# Workaround for pydantic 2.12 and mypy type checking issue for Annotated with default_factory
196+
if TYPE_CHECKING:
197+
headers: HttpHeaders = HttpHeaders()
198+
"""HTTP request headers."""
199+
200+
user_data: dict[str, JsonSerializable] = {}
201+
"""Custom user data assigned to the request. Use this to save any request related data to the
202+
request's scope, keeping them accessible on retries, failures etc.
203+
"""
204+
205+
else:
206+
headers: Annotated[HttpHeaders, Field(default_factory=HttpHeaders)]
207+
"""HTTP request headers."""
208+
209+
user_data: Annotated[
210+
dict[str, JsonSerializable], # Internally, the model contains `UserData`, this is just for convenience
211+
Field(alias='userData', default_factory=lambda: UserData()),
212+
PlainValidator(user_data_adapter.validate_python),
213+
PlainSerializer(
214+
lambda instance: user_data_adapter.dump_python(
215+
instance,
216+
by_alias=True,
217+
exclude_none=True,
218+
exclude_unset=True,
219+
exclude_defaults=True,
220+
)
221+
),
222+
]
223+
"""Custom user data assigned to the request. Use this to save any request related data to the
224+
request's scope, keeping them accessible on retries, failures etc.
225+
"""
215226

216227
retry_count: Annotated[int, Field(alias='retryCount')] = 0
217228
"""Number of times the request has been retried."""

src/crawlee/_types.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,7 @@
33
import dataclasses
44
from collections.abc import Callable, Iterator, Mapping
55
from dataclasses import dataclass
6-
from typing import (
7-
TYPE_CHECKING,
8-
Annotated,
9-
Any,
10-
Literal,
11-
Protocol,
12-
TypedDict,
13-
TypeVar,
14-
cast,
15-
overload,
16-
)
6+
from typing import TYPE_CHECKING, Annotated, Any, Literal, Protocol, TypedDict, TypeVar, cast, overload
177

188
from pydantic import ConfigDict, Field, PlainValidator, RootModel
199

@@ -71,11 +61,15 @@ class HttpHeaders(RootModel, Mapping[str, str]):
7161

7262
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
7363

74-
root: Annotated[
75-
dict[str, str],
76-
PlainValidator(lambda value: _normalize_headers(value)),
77-
Field(default_factory=dict),
78-
] = {}
64+
# Workaround for pydantic 2.12 and mypy type checking issue for Annotated with default_factory
65+
if TYPE_CHECKING:
66+
root: dict[str, str] = {}
67+
else:
68+
root: Annotated[
69+
dict[str, str],
70+
PlainValidator(lambda value: _normalize_headers(value)),
71+
Field(default_factory=dict),
72+
]
7973

8074
def __getitem__(self, key: str) -> str:
8175
return self.root[key.lower()]

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)