Skip to content

Commit b4dfb81

Browse files
committed
feat: drop support for pydantic v1
1 parent 838d68f commit b4dfb81

19 files changed

Lines changed: 196 additions & 430 deletions

‎.github/workflows/test.yml‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ jobs:
3838
strategy:
3939
matrix:
4040
py_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
41-
pydantic_ver: ["<2", ">=2.5,<3"]
4241
os: [ubuntu-latest, windows-latest, macos-latest]
4342
runs-on: "${{ matrix.os }}"
4443
steps:
@@ -57,8 +56,6 @@ jobs:
5756
version: "latest"
5857
- name: Install deps
5958
run: uv sync --all-extras
60-
- name: Setup pydantic version
61-
run: uv pip install "pydantic ${{ matrix.pydantic_ver }}"
6259
- name: Run pytest check
6360
run: uv run pytest -vv -n auto --cov="taskiq" .
6461
- name: Generate report

‎pyproject.toml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ classifiers = [
2727
dependencies = [
2828
"aiohttp>=3",
2929
"anyio>=4",
30-
"packaging>=19",
3130
"pycron>=3.0.0",
32-
"pydantic>=1.0,<=3.0",
31+
"pydantic>=2.13,<3.0",
3332
"taskiq_dependencies>=1.3.1,<2",
3433
"typing-extensions>=3.10.0.0; python_version < '3.11'"
3534
]

‎taskiq/compat.py‎

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,16 @@
11
from collections.abc import Hashable
22
from functools import lru_cache
3-
from importlib.metadata import version
43
from typing import Any, TypeVar
54

65
import pydantic
7-
from packaging.version import Version, parse
86

9-
PYDANTIC_VER = parse(version("pydantic"))
7+
T = TypeVar("T", bound=Hashable)
108

11-
Model = TypeVar("Model", bound="pydantic.BaseModel")
12-
IS_PYDANTIC2 = Version("2.0") <= PYDANTIC_VER
139

14-
if IS_PYDANTIC2:
15-
T = TypeVar("T", bound=Hashable)
10+
@lru_cache
11+
def create_type_adapter(annot: type[T]) -> pydantic.TypeAdapter[T]:
12+
return pydantic.TypeAdapter(annot)
1613

17-
@lru_cache
18-
def create_type_adapter(annot: type[T]) -> pydantic.TypeAdapter[T]:
19-
return pydantic.TypeAdapter(annot)
2014

21-
def parse_obj_as(annot: type[T], obj: Any) -> T:
22-
return create_type_adapter(annot).validate_python(obj)
23-
24-
def model_validate(
25-
model_class: type[Model],
26-
message: dict[str, Any],
27-
) -> Model:
28-
return model_class.model_validate(message)
29-
30-
def model_dump(instance: Model) -> dict[str, Any]:
31-
return instance.model_dump(mode="json")
32-
33-
def model_validate_json(
34-
model_class: type[Model],
35-
message: str | bytes | bytearray,
36-
) -> Model:
37-
return model_class.model_validate_json(message)
38-
39-
def model_dump_json(instance: Model) -> str:
40-
return instance.model_dump_json()
41-
42-
def model_copy(
43-
instance: Model,
44-
update: dict[str, Any] | None = None,
45-
deep: bool = False,
46-
) -> Model:
47-
return instance.model_copy(update=update, deep=deep)
48-
49-
validate_call = pydantic.validate_call
50-
51-
else:
52-
parse_obj_as = pydantic.parse_obj_as # type: ignore
53-
54-
def model_validate(
55-
model_class: type[Model],
56-
message: dict[str, Any],
57-
) -> Model:
58-
return model_class.parse_obj(message)
59-
60-
def model_dump(instance: Model) -> dict[str, Any]:
61-
return instance.dict()
62-
63-
def model_validate_json(
64-
model_class: type[Model],
65-
message: str | bytes | bytearray,
66-
) -> Model:
67-
return model_class.parse_raw(message) # type: ignore[arg-type]
68-
69-
def model_dump_json(instance: Model) -> str:
70-
return instance.json()
71-
72-
def model_copy(
73-
instance: Model,
74-
update: dict[str, Any] | None = None,
75-
deep: bool = False,
76-
) -> Model:
77-
return instance.copy(update=update, deep=deep)
78-
79-
validate_call = pydantic.validate_arguments # type: ignore
15+
def parse_obj_as(annot: type[T], obj: Any) -> T:
16+
return create_type_adapter(annot).validate_python(obj)

‎taskiq/depends/progress_tracker.py‎

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import enum
22
from typing import Generic, TypeVar
33

4+
from pydantic import BaseModel, ConfigDict
45
from taskiq_dependencies import Depends
56

6-
from taskiq.compat import IS_PYDANTIC2
77
from taskiq.context import Context
88

99
_ProgressType = TypeVar("_ProgressType")
@@ -18,18 +18,8 @@ class TaskState(str, enum.Enum):
1818
RETRY = "RETRY"
1919

2020

21-
if IS_PYDANTIC2:
22-
from pydantic import BaseModel, ConfigDict
23-
24-
class _TaskProgressConfig(BaseModel):
25-
model_config = ConfigDict(arbitrary_types_allowed=True)
26-
27-
else:
28-
from pydantic.generics import GenericModel
29-
30-
class _TaskProgressConfig(GenericModel): # type: ignore[no-redef]
31-
class Config:
32-
arbitrary_types_allowed = True
21+
class _TaskProgressConfig(BaseModel):
22+
model_config = ConfigDict(arbitrary_types_allowed=True)
3323

3424

3525
class TaskProgress(_TaskProgressConfig, Generic[_ProgressType]):

‎taskiq/formatters/json_formatter.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from taskiq.abc.formatter import TaskiqFormatter
2-
from taskiq.compat import model_dump_json, model_validate_json
32
from taskiq.message import BrokerMessage, TaskiqMessage
43

54

@@ -16,7 +15,7 @@ def dumps(self, message: TaskiqMessage) -> BrokerMessage:
1615
return BrokerMessage(
1716
task_id=message.task_id,
1817
task_name=message.task_name,
19-
message=model_dump_json(message).encode(),
18+
message=message.model_dump_json().encode(),
2019
labels=message.labels,
2120
)
2221

@@ -27,4 +26,4 @@ def loads(self, message: bytes) -> TaskiqMessage:
2726
:param message: broker's message.
2827
:return: parsed taskiq message.
2928
"""
30-
return model_validate_json(TaskiqMessage, message)
29+
return TaskiqMessage.model_validate_json(message)

‎taskiq/formatters/proxy_formatter.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from typing import TYPE_CHECKING
22

33
from taskiq.abc.formatter import TaskiqFormatter
4-
from taskiq.compat import model_dump, model_validate
54
from taskiq.message import BrokerMessage, TaskiqMessage
65

76
if TYPE_CHECKING:
@@ -24,7 +23,7 @@ def dumps(self, message: TaskiqMessage) -> BrokerMessage:
2423
return BrokerMessage(
2524
task_id=message.task_id,
2625
task_name=message.task_name,
27-
message=self.broker.serializer.dumpb(model_dump(message)),
26+
message=self.broker.serializer.dumpb(message.model_dump(mode="json")),
2827
labels=message.labels,
2928
)
3029

@@ -35,4 +34,4 @@ def loads(self, message: bytes) -> TaskiqMessage:
3534
:param message: broker's message.
3635
:return: parsed taskiq message.
3736
"""
38-
return model_validate(TaskiqMessage, self.broker.serializer.loadb(message))
37+
return TaskiqMessage.model_validate(self.broker.serializer.loadb(message))

‎taskiq/kicker.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from pydantic import BaseModel
1717

1818
from taskiq.abc.middleware import TaskiqMiddleware
19-
from taskiq.compat import model_dump
2019
from taskiq.exceptions import SendTaskError
2120
from taskiq.labels import prepare_label
2221
from taskiq.message import TaskiqMessage
@@ -294,7 +293,7 @@ def _prepare_arg(cls, arg: Any) -> Any:
294293
:return: Formatted argument.
295294
"""
296295
if isinstance(arg, BaseModel):
297-
arg = model_dump(arg)
296+
arg = arg.model_dump(mode="json")
298297
if is_dataclass(arg):
299298
if isinstance(arg, type):
300299
raise ValueError(

‎taskiq/middlewares/opentelemetry_middleware.py‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
from collections.abc import Generator
33
from contextlib import AbstractContextManager
44
from datetime import datetime, timezone
5-
from importlib.metadata import version
65
from typing import Any, TypeVar
76

87
import psutil
9-
from packaging.version import Version, parse
108

119
try:
1210
import opentelemetry # noqa: F401
@@ -34,16 +32,6 @@
3432
# Taskiq Context key
3533
CTX_KEY = "__otel_task_span"
3634

37-
# unlike pydantic v2, v1 includes CTX_KEY by default
38-
# excluding it here
39-
PYDANTIC_VER = parse(version("pydantic"))
40-
IS_PYDANTIC1 = Version("2.0") > PYDANTIC_VER
41-
if IS_PYDANTIC1:
42-
if TaskiqMessage.__exclude_fields__: # type: ignore[attr-defined]
43-
TaskiqMessage.__exclude_fields__.update(CTX_KEY) # type: ignore
44-
else:
45-
TaskiqMessage.__exclude_fields__ = {CTX_KEY} # type: ignore
46-
4735
# Taskiq Context attributes
4836
TASKIQ_CONTEXT_ATTRIBUTES = [
4937
"_retries",
@@ -115,8 +103,8 @@ def attach_context(
115103

116104
if ctx_dict is None:
117105
ctx_dict = {}
118-
# use object.__setattr__ directly
119-
# to skip pydantic v1 setattr
106+
# use object.__setattr__ directly since CTX_KEY is not a declared model field,
107+
# and pydantic forbids setting undeclared attributes
120108
object.__setattr__(message, CTX_KEY, ctx_dict)
121109

122110
ctx_dict[(message.task_id, is_publish)] = (span, activation, token)

‎taskiq/middlewares/taskiq_admin_middleware.py‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import aiohttp
88

99
from taskiq.abc.middleware import TaskiqMiddleware
10-
from taskiq.compat import model_dump
1110
from taskiq.message import TaskiqMessage
1211
from taskiq.result import TaskiqResult
1312

@@ -126,7 +125,7 @@ async def post_send(self, message: TaskiqMessage) -> None:
126125
127126
:param message: kicked message.
128127
"""
129-
dict_message: dict[str, Any] = model_dump(message)
128+
dict_message: dict[str, Any] = message.model_dump(mode="json")
130129
await self._spawn_request(
131130
f"/api/tasks/{message.task_id}/queued",
132131
{
@@ -149,7 +148,7 @@ async def pre_execute(self, message: TaskiqMessage) -> TaskiqMessage:
149148
:param message: incoming parsed taskiq message.
150149
:return: modified message.
151150
"""
152-
dict_message: dict[str, Any] = model_dump(message)
151+
dict_message: dict[str, Any] = message.model_dump(mode="json")
153152
await self._spawn_request(
154153
f"/api/tasks/{message.task_id}/started",
155154
{
@@ -177,7 +176,7 @@ async def post_execute(
177176
:param message: incoming message.
178177
:param result: result of execution for current task.
179178
"""
180-
dict_result: dict[str, Any] = model_dump(result)
179+
dict_result: dict[str, Any] = result.model_dump(mode="json")
181180
await self._spawn_request(
182181
f"/api/tasks/{message.task_id}/executed",
183182
{

0 commit comments

Comments
 (0)