diff --git a/pyproject.toml b/pyproject.toml index 64a0a3c..05c694a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,13 +19,13 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: System :: Networking", "Typing :: Typed", ] dependencies = [ "pydantic>=2,<3", "taskiq>=0.11.12,<1", - "typing-extensions>=4.3.0,<5", ] dynamic = ["version"] @@ -96,6 +96,7 @@ lint.select = [ "ERA", # Checks for commented out code "PL", # PyLint checks "RUF", # Specific to Ruff checks + "UP", # Pyupgrade ] lint.ignore = [ "D105", # Missing docstring in magic method @@ -131,3 +132,6 @@ allow-magic-value-types = ["int", "str", "float"] [tool.ruff.lint.flake8-bugbear] extend-immutable-calls = ["taskiq_dependencies.Depends", "taskiq.TaskiqDepends"] + +[tool.pytest.ini_options] +anyio_mode = "auto" diff --git a/taskiq_pipelines/abc.py b/taskiq_pipelines/abc.py index 2259806..b1295e9 100644 --- a/taskiq_pipelines/abc.py +++ b/taskiq_pipelines/abc.py @@ -1,15 +1,14 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, Type +from typing import Any, ClassVar from taskiq import AsyncBroker, TaskiqResult -from typing_extensions import ClassVar class AbstractStep(ABC): """Abstract pipeline step.""" _step_name: str - _known_steps: ClassVar[Dict[str, Type["AbstractStep"]]] = {} + _known_steps: ClassVar[dict[str, type["AbstractStep"]]] = {} def __init_subclass__(cls, step_name: str, **kwargs: Any) -> None: super().__init_subclass__(**kwargs) diff --git a/taskiq_pipelines/exceptions.py b/taskiq_pipelines/exceptions.py index 91d3244..3eca365 100644 --- a/taskiq_pipelines/exceptions.py +++ b/taskiq_pipelines/exceptions.py @@ -1,4 +1,4 @@ -from typing import ClassVar, Union +from typing import ClassVar from taskiq import TaskiqError @@ -16,7 +16,7 @@ class StepError(PipelineError): _STEP_NAME: ClassVar[str] task_id: str - error: Union[BaseException, None] + error: BaseException | None class MappingError(StepError): diff --git a/taskiq_pipelines/middleware.py b/taskiq_pipelines/middleware.py index fd8767d..b4da54f 100644 --- a/taskiq_pipelines/middleware.py +++ b/taskiq_pipelines/middleware.py @@ -1,5 +1,5 @@ from logging import getLogger -from typing import Any, List, Optional +from typing import Any import pydantic from taskiq import TaskiqMessage, TaskiqMiddleware, TaskiqResult @@ -43,7 +43,7 @@ async def post_save( # noqa: PLR0911 pipeline_data = message.labels[PIPELINE_DATA] parsed_data = self.broker.serializer.loadb(pipeline_data) try: - steps_data = pydantic.TypeAdapter(List[DumpedStep]).validate_python( + steps_data = pydantic.TypeAdapter(list[DumpedStep]).validate_python( parsed_data, ) except ValueError as err: @@ -103,7 +103,7 @@ async def on_error( return pipe_data = message.labels[PIPELINE_DATA] try: - steps = pydantic.TypeAdapter(List[DumpedStep]).validate_json(pipe_data) + steps = pydantic.TypeAdapter(list[DumpedStep]).validate_json(pipe_data) except ValueError: return if current_step_num == len(steps) - 1: @@ -113,7 +113,7 @@ async def on_error( async def fail_pipeline( self, last_task_id: str, - abort: Optional[BaseException] = None, + abort: BaseException | None = None, ) -> None: """ This function aborts pipeline. diff --git a/taskiq_pipelines/pipeliner.py b/taskiq_pipelines/pipeliner.py index bd4d8db..813f104 100644 --- a/taskiq_pipelines/pipeliner.py +++ b/taskiq_pipelines/pipeliner.py @@ -2,23 +2,12 @@ from collections.abc import Coroutine from types import CoroutineType -from typing import ( - Any, - Dict, - Generic, - List, - Literal, - Optional, - TypeVar, - Union, - overload, -) +from typing import Any, Generic, Literal, ParamSpec, TypeVar, overload import pydantic from taskiq import AsyncBroker, AsyncTaskiqTask from taskiq.decor import AsyncTaskiqDecoratedTask from taskiq.kicker import AsyncKicker -from typing_extensions import ParamSpec from taskiq_pipelines.constants import CURRENT_STEP, EMPTY_PARAM_NAME, PIPELINE_DATA from taskiq_pipelines.steps import FilterStep, MapperStep, SequentialStep, parse_step @@ -33,11 +22,11 @@ class DumpedStep(pydantic.BaseModel): """Dumped state model.""" step_type: str - step_data: Dict[str, Any] + step_data: dict[str, Any] task_id: str -DumpedSteps = pydantic.RootModel[List[DumpedStep]] +DumpedSteps = pydantic.RootModel[list[DumpedStep]] class Pipeline(Generic[_FuncParams, _ReturnType]): @@ -57,80 +46,66 @@ class Pipeline(Generic[_FuncParams, _ReturnType]): def __init__( self, broker: AsyncBroker, - task: Optional[ - Union[ - AsyncKicker[_FuncParams, Coroutine[Any, Any, _ReturnType]], - AsyncKicker[_FuncParams, CoroutineType[Any, Any, _ReturnType]], - AsyncTaskiqDecoratedTask[ - _FuncParams, - Coroutine[Any, Any, _ReturnType], - ], - AsyncTaskiqDecoratedTask[ - _FuncParams, - CoroutineType[Any, Any, _ReturnType], - ], + task: ( + AsyncKicker[_FuncParams, Coroutine[Any, Any, _ReturnType]] + | AsyncKicker[_FuncParams, CoroutineType[Any, Any, _ReturnType]] + | AsyncTaskiqDecoratedTask[_FuncParams, Coroutine[Any, Any, _ReturnType]] + | AsyncTaskiqDecoratedTask[ + _FuncParams, + CoroutineType[Any, Any, _ReturnType], ] - ] = None, + | None + ) = None, ) -> None: ... @overload def __init__( self, broker: AsyncBroker, - task: Optional[ - Union[ - AsyncKicker[_FuncParams, _ReturnType], - AsyncTaskiqDecoratedTask[_FuncParams, _ReturnType], - ] - ] = None, + task: ( + AsyncKicker[_FuncParams, _ReturnType] + | AsyncTaskiqDecoratedTask[_FuncParams, _ReturnType] + | None + ) = None, ) -> None: ... def __init__( self, broker: AsyncBroker, - task: Optional[ - Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ] - ] = None, + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any] | None = None, ) -> None: self.broker = broker - self.steps: "List[DumpedStep]" = [] + self.steps: list[DumpedStep] = [] if task: self.call_next(task) @overload def call_next( - self: "Pipeline[_FuncParams, _ReturnType]", - task: Union[ - AsyncKicker[[_ReturnType], Coroutine[Any, Any, _T]], - AsyncKicker[[_ReturnType], CoroutineType[Any, Any, _T]], - AsyncTaskiqDecoratedTask[[_ReturnType], Coroutine[Any, Any, _T]], - AsyncTaskiqDecoratedTask[[_ReturnType], CoroutineType[Any, Any, _T]], - ], - param_name: Union[Optional[str], Literal[-1]] = None, + self: Pipeline[_FuncParams, _ReturnType], + task: ( + AsyncKicker[[_ReturnType], Coroutine[Any, Any, _T]] + | AsyncKicker[[_ReturnType], CoroutineType[Any, Any, _T]] + | AsyncTaskiqDecoratedTask[[_ReturnType], Coroutine[Any, Any, _T]] + | AsyncTaskiqDecoratedTask[[_ReturnType], CoroutineType[Any, Any, _T]] + ), + param_name: str | Literal[-1] | None = None, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, _T]": ... + ) -> Pipeline[_FuncParams, _T]: ... @overload def call_next( - self: "Pipeline[_FuncParams, _ReturnType]", - task: Union[ - AsyncKicker[[_ReturnType], _T], - AsyncTaskiqDecoratedTask[[_ReturnType], _T], - ], - param_name: Union[Optional[str], Literal[-1]] = None, + self: Pipeline[_FuncParams, _ReturnType], + task: ( + AsyncKicker[[_ReturnType], _T] | AsyncTaskiqDecoratedTask[[_ReturnType], _T] + ), + param_name: str | Literal[-1] | None = None, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, _T]": ... + ) -> Pipeline[_FuncParams, _T]: ... def call_next( self, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Union[Optional[str], Literal[-1]] = None, + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | None | Literal[-1] = None, **additional_kwargs: Any, ) -> Any: """ @@ -163,32 +138,26 @@ def call_next( @overload def call_after( - self: "Pipeline[_FuncParams, _ReturnType]", - task: Union[ - AsyncKicker[[], Coroutine[Any, Any, _T]], - AsyncKicker[[], CoroutineType[Any, Any, _T]], - AsyncTaskiqDecoratedTask[[], Coroutine[Any, Any, _T]], - AsyncTaskiqDecoratedTask[[], CoroutineType[Any, Any, _T]], - ], + self: Pipeline[_FuncParams, _ReturnType], + task: ( + AsyncKicker[[], Coroutine[Any, Any, _T]] + | AsyncKicker[[], CoroutineType[Any, Any, _T]] + | AsyncTaskiqDecoratedTask[[], Coroutine[Any, Any, _T]] + | AsyncTaskiqDecoratedTask[[], CoroutineType[Any, Any, _T]] + ), **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, _T]": ... + ) -> Pipeline[_FuncParams, _T]: ... @overload def call_after( - self: "Pipeline[_FuncParams, _ReturnType]", - task: Union[ - AsyncKicker[[], _T], - AsyncTaskiqDecoratedTask[[], _T], - ], + self: Pipeline[_FuncParams, _ReturnType], + task: AsyncKicker[[], _T] | AsyncTaskiqDecoratedTask[[], _T], **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, _T]": ... + ) -> Pipeline[_FuncParams, _T]: ... def call_after( self, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], **additional_kwargs: Any, ) -> Any: """ @@ -219,39 +188,33 @@ def call_after( @overload def map( - self: "Pipeline[_FuncParams, list[_T]]", - task: Union[ - AsyncKicker[Any, Coroutine[Any, Any, _T2]], - AsyncKicker[Any, CoroutineType[Any, Any, _T2]], - AsyncTaskiqDecoratedTask[Any, Coroutine[Any, Any, _T2]], - AsyncTaskiqDecoratedTask[Any, CoroutineType[Any, Any, _T2]], - ], - param_name: Optional[str] = None, + self: Pipeline[_FuncParams, list[_T]], + task: ( + AsyncKicker[Any, Coroutine[Any, Any, _T2]] + | AsyncKicker[Any, CoroutineType[Any, Any, _T2]] + | AsyncTaskiqDecoratedTask[Any, Coroutine[Any, Any, _T2]] + | AsyncTaskiqDecoratedTask[Any, CoroutineType[Any, Any, _T2]] + ), + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, list[_T2]]": ... + ) -> Pipeline[_FuncParams, list[_T2]]: ... @overload def map( - self: "Pipeline[_FuncParams, list[_T]]", - task: Union[ - AsyncKicker[Any, _T2], - AsyncTaskiqDecoratedTask[Any, _T2], - ], - param_name: Optional[str] = None, + self: Pipeline[_FuncParams, list[_T]], + task: AsyncKicker[Any, _T2] | AsyncTaskiqDecoratedTask[Any, _T2], + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, list[_T2]]": ... + ) -> Pipeline[_FuncParams, list[_T2]]: ... def map( - self: "Pipeline[_FuncParams, list[Any]]", - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Optional[str] = None, + self: Pipeline[_FuncParams, list[Any]], + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, @@ -290,39 +253,33 @@ def map( @overload def filter( - self: "Pipeline[_FuncParams, List[_T]]", - task: Union[ - AsyncKicker[[_T], Coroutine[Any, Any, bool]], - AsyncKicker[[_T], CoroutineType[Any, Any, bool]], - AsyncTaskiqDecoratedTask[[_T], Coroutine[Any, Any, bool]], - AsyncTaskiqDecoratedTask[[_T], CoroutineType[Any, Any, bool]], - ], - param_name: Optional[str] = None, + self: Pipeline[_FuncParams, list[_T]], + task: ( + AsyncKicker[[_T], Coroutine[Any, Any, bool]] + | AsyncKicker[[_T], CoroutineType[Any, Any, bool]] + | AsyncTaskiqDecoratedTask[[_T], Coroutine[Any, Any, bool]] + | AsyncTaskiqDecoratedTask[[_T], CoroutineType[Any, Any, bool]] + ), + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, List[_T]]": ... + ) -> Pipeline[_FuncParams, list[_T]]: ... @overload def filter( - self: "Pipeline[_FuncParams, List[_T]]", - task: Union[ - AsyncKicker[[_T], bool], - AsyncTaskiqDecoratedTask[[_T], bool], - ], - param_name: Optional[str] = None, + self: Pipeline[_FuncParams, list[_T]], + task: AsyncKicker[[_T], bool] | AsyncTaskiqDecoratedTask[[_T], bool], + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, - ) -> "Pipeline[_FuncParams, List[_T]]": ... + ) -> Pipeline[_FuncParams, list[_T]]: ... def filter( self, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Optional[str] = None, + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | None = None, skip_errors: bool = False, check_interval: float = 0.5, **additional_kwargs: Any, @@ -371,7 +328,7 @@ def dumpb(self) -> bytes: ) @classmethod - def loadb(cls, broker: AsyncBroker, pipe_data: bytes) -> "Pipeline[Any, Any]": + def loadb(cls, broker: AsyncBroker, pipe_data: bytes) -> Pipeline[Any, Any]: """ Parses serialized pipeline. @@ -382,7 +339,7 @@ def loadb(cls, broker: AsyncBroker, pipe_data: bytes) -> "Pipeline[Any, Any]": :param pipe_data: serialized pipeline data. :return: new """ - pipe: "Pipeline[Any, Any]" = Pipeline(broker) + pipe: Pipeline[Any, Any] = Pipeline(broker) data = broker.serializer.loadb(pipe_data) pipe.steps = DumpedSteps.model_validate(data) # type: ignore[assignment] return pipe diff --git a/taskiq_pipelines/steps/__init__.py b/taskiq_pipelines/steps/__init__.py index 2b331a3..495cbb7 100644 --- a/taskiq_pipelines/steps/__init__.py +++ b/taskiq_pipelines/steps/__init__.py @@ -1,7 +1,7 @@ """Package with default pipeline steps.""" from logging import getLogger -from typing import Any, Dict +from typing import Any from taskiq_pipelines.abc import AbstractStep from taskiq_pipelines.steps.filter import FilterStep @@ -11,7 +11,7 @@ logger = getLogger(__name__) -def parse_step(step_type: str, step_data: Dict[str, Any]) -> AbstractStep: +def parse_step(step_type: str, step_data: dict[str, Any]) -> AbstractStep: step_cls = AbstractStep._known_steps.get(step_type) if step_cls is None: logger.warning(f"Unknown step type: {step_type}") diff --git a/taskiq_pipelines/steps/filter.py b/taskiq_pipelines/steps/filter.py index c4e5305..44a52bb 100644 --- a/taskiq_pipelines/steps/filter.py +++ b/taskiq_pipelines/steps/filter.py @@ -1,5 +1,6 @@ import asyncio -from typing import Any, Dict, Iterable, List, Optional, Union +from collections.abc import Iterable +from typing import Any import pydantic from taskiq import AsyncBroker, Context, TaskiqDepends, TaskiqResult @@ -14,12 +15,12 @@ @async_shared_broker.task(task_name="taskiq_pipelines.shared.filter_tasks") async def filter_tasks( # noqa: C901 - task_ids: List[str], + task_ids: list[str], parent_task_id: str, check_interval: float, skip_errors: bool = False, context: Context = TaskiqDepends(), -) -> List[Any]: +) -> list[Any]: """ Filter resulted tasks. @@ -77,9 +78,9 @@ class FilterStep(pydantic.BaseModel, AbstractStep, step_name="filter"): """Task to filter results.""" task_name: str - labels: Dict[str, str] - param_name: Optional[str] - additional_kwargs: Dict[str, Any] + labels: dict[str, str] + param_name: str | None + additional_kwargs: dict[str, Any] skip_errors: bool check_interval: float @@ -111,7 +112,7 @@ async def act( raise AbortPipeline(reason="Result of the previous task is not iterable.") sub_task_ids = [] for item in result.return_value: - kicker: "AsyncKicker[Any, Any]" = AsyncKicker( + kicker: AsyncKicker[Any, Any] = AsyncKicker( task_name=self.task_name, broker=broker, labels=self.labels, @@ -142,11 +143,8 @@ async def act( @classmethod def from_task( cls, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Optional[str], + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | None, skip_errors: bool, check_interval: float, **additional_kwargs: Any, diff --git a/taskiq_pipelines/steps/mapper.py b/taskiq_pipelines/steps/mapper.py index 06c4248..df6ef44 100644 --- a/taskiq_pipelines/steps/mapper.py +++ b/taskiq_pipelines/steps/mapper.py @@ -1,5 +1,6 @@ import asyncio -from typing import Any, Dict, Iterable, List, Optional, Union +from collections.abc import Iterable +from typing import Any import pydantic from taskiq import ( @@ -19,11 +20,11 @@ @async_shared_broker.task(task_name="taskiq_pipelines.shared.wait_tasks") async def wait_tasks( - task_ids: List[str], + task_ids: list[str], check_interval: float, skip_errors: bool = True, context: Context = TaskiqDepends(), -) -> List[Any]: +) -> list[Any]: """ Waits for subtasks to complete. @@ -72,9 +73,9 @@ class MapperStep(pydantic.BaseModel, AbstractStep, step_name="mapper"): """Step that maps iterables.""" task_name: str - labels: Dict[str, str] - param_name: Optional[str] - additional_kwargs: Dict[str, Any] + labels: dict[str, str] + param_name: str | None + additional_kwargs: dict[str, Any] skip_errors: bool check_interval: float @@ -106,13 +107,13 @@ async def act( :raises AbortPipeline: if the result of the previous task is not iterable. """ - sub_task_ids: List[str] = [] + sub_task_ids: list[str] = [] return_value = result.return_value if not isinstance(return_value, Iterable): raise AbortPipeline(reason="Result of the previous task is not iterable.") for item in return_value: - kicker: "AsyncKicker[Any, Any]" = AsyncKicker( + kicker: AsyncKicker[Any, Any] = AsyncKicker( task_name=self.task_name, broker=broker, labels=self.labels, @@ -143,11 +144,8 @@ async def act( @classmethod def from_task( cls, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Optional[str], + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | None, skip_errors: bool, check_interval: float, **additional_kwargs: Any, diff --git a/taskiq_pipelines/steps/sequential.py b/taskiq_pipelines/steps/sequential.py index 7b10fb0..5e19c47 100644 --- a/taskiq_pipelines/steps/sequential.py +++ b/taskiq_pipelines/steps/sequential.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Union +from typing import Any import pydantic from taskiq import AsyncBroker, AsyncTaskiqDecoratedTask, TaskiqResult @@ -18,11 +18,11 @@ class SequentialStep(pydantic.BaseModel, AbstractStep, step_name="sequential"): """ task_name: str - labels: Dict[str, str] + labels: dict[str, str] # order is important here, otherwise pydantic will always choose str. # we use int instead of Literal[-1] because pydantic thinks that -1 is always str. - param_name: Union[Optional[int], str] - additional_kwargs: Dict[str, Any] + param_name: int | str | None + additional_kwargs: dict[str, Any] async def act( self, @@ -51,7 +51,7 @@ async def act( :param pipe_data: serialized pipeline. :param result: result of the previous task. """ - kicker: "AsyncKicker[Any, Any]" = ( + kicker: AsyncKicker[Any, Any] = ( AsyncKicker( task_name=self.task_name, broker=broker, @@ -73,11 +73,8 @@ async def act( @classmethod def from_task( cls, - task: Union[ - AsyncKicker[Any, Any], - AsyncTaskiqDecoratedTask[Any, Any], - ], - param_name: Union[Optional[str], int], + task: AsyncKicker[Any, Any] | AsyncTaskiqDecoratedTask[Any, Any], + param_name: str | int | None, **additional_kwargs: Any, ) -> "SequentialStep": """ diff --git a/tests/test_steps.py b/tests/test_steps.py index 3cfd1c9..ce19ba8 100644 --- a/tests/test_steps.py +++ b/tests/test_steps.py @@ -1,15 +1,12 @@ # mypy: ignore-errors # Temporary ignore, because of some mypy issues. -from typing import List -import pytest from taskiq import InMemoryBroker from taskiq_pipelines import AbortPipeline, Pipeline, PipelineMiddleware -@pytest.mark.anyio async def test_success() -> None: """Tests that sequential step works as expected.""" broker = InMemoryBroker().with_middlewares(PipelineMiddleware()) @@ -28,13 +25,12 @@ def double(i: int) -> int: assert res.return_value == 4 -@pytest.mark.anyio async def test_mapping_success() -> None: """Test that map step works as expected.""" broker = InMemoryBroker().with_middlewares(PipelineMiddleware()) @broker.task - def ranger(i: int) -> List[int]: + def ranger(i: int) -> list[int]: return list(range(i)) @broker.task @@ -47,7 +43,6 @@ def double(i: int) -> int: assert res.return_value == list(map(double, ranger(4))) -@pytest.mark.anyio async def test_abort_pipeline() -> None: """Test AbortPipeline.""" broker = InMemoryBroker().with_middlewares(PipelineMiddleware()) diff --git a/uv.lock b/uv.lock index 4e67f67..2c419e9 100644 --- a/uv.lock +++ b/uv.lock @@ -1433,7 +1433,6 @@ source = { editable = "." } dependencies = [ { name = "pydantic" }, { name = "taskiq" }, - { name = "typing-extensions" }, ] [package.dev-dependencies] @@ -1452,7 +1451,6 @@ dev = [ requires-dist = [ { name = "pydantic", specifier = ">=2,<3" }, { name = "taskiq", specifier = ">=0.11.12,<1" }, - { name = "typing-extensions", specifier = ">=4.3.0,<5" }, ] [package.metadata.requires-dev]