From 6d6bcc6256501b6c1b3d83be9866d0d6aab2e81c Mon Sep 17 00:00:00 2001 From: Anfimov Dima Date: Thu, 30 Oct 2025 11:41:36 +0100 Subject: [PATCH 1/4] fix: TaskiqAdminMiddleware compatibility with python <3.11 --- taskiq/middlewares/taskiq_admin_middleware.py | 4 +- .../test_taskiq_admin_middleware.py | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/middlewares/test_taskiq_admin_middleware.py diff --git a/taskiq/middlewares/taskiq_admin_middleware.py b/taskiq/middlewares/taskiq_admin_middleware.py index eae614b3..19b51dc9 100644 --- a/taskiq/middlewares/taskiq_admin_middleware.py +++ b/taskiq/middlewares/taskiq_admin_middleware.py @@ -1,5 +1,5 @@ import asyncio -from datetime import UTC, datetime +from datetime import datetime, timezone from logging import getLogger from typing import Any from urllib.parse import urljoin @@ -48,7 +48,7 @@ def __init__( @staticmethod def _now_iso() -> str: - return datetime.now(UTC).replace(tzinfo=None).isoformat() + return datetime.now(timezone.utc).replace(tzinfo=None).isoformat() def _get_client(self) -> aiohttp.ClientSession: """Create and cache session.""" diff --git a/tests/middlewares/test_taskiq_admin_middleware.py b/tests/middlewares/test_taskiq_admin_middleware.py new file mode 100644 index 00000000..343a5eea --- /dev/null +++ b/tests/middlewares/test_taskiq_admin_middleware.py @@ -0,0 +1,88 @@ +import asyncio +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from taskiq import TaskiqMessage +from taskiq.middlewares.taskiq_admin_middleware import TaskiqAdminMiddleware + + +@pytest.fixture +async def middleware() -> TaskiqAdminMiddleware: + middleware = TaskiqAdminMiddleware( + url="http://localhost:8000", + api_token="test-token", # noqa: S106 + timeout=5, + taskiq_broker_name="test-broker", + ) + await middleware.startup() + yield middleware + await middleware.shutdown() + + +@pytest.fixture +def message() -> TaskiqMessage: + return TaskiqMessage( + task_id="task-123", + task_name="test_task", + labels={}, + args=[1, 2, 3], + kwargs={"key": "value"}, + ) + + +def _make_mock_response() -> AsyncMock: + """Create a properly configured mock response object.""" + mock_response = AsyncMock() + mock_response.__aenter__.return_value = mock_response + mock_response.__aexit__.return_value = None + mock_response.ok = True + mock_response.raise_for_status = Mock() + return mock_response + + +class TestTaskiqAdminMiddlewarePostSend: + @pytest.mark.anyio + async def test_when_post_send_is_called__then_queued_endpoint_is_called( + self, + middleware: TaskiqAdminMiddleware, + message: TaskiqMessage, + ) -> None: + # Given + with patch("aiohttp.ClientSession.post") as mock_post: + mock_response = _make_mock_response() + mock_post.return_value = mock_response + + # When + await middleware.post_send(message) + await asyncio.sleep(0.1) + + # Then + mock_post.assert_called() + assert mock_post.call_args is not None + assert "/api/tasks/task-123/queued" in mock_post.call_args[0][0] + + @pytest.mark.anyio + async def test_when_post_send_is_called__then_payload_includes_task_info( + self, + middleware: TaskiqAdminMiddleware, + message: TaskiqMessage, + ) -> None: + # Given + with patch("aiohttp.ClientSession.post") as mock_post: + mock_response = _make_mock_response() + mock_post.return_value = mock_response + + # When + await middleware.post_send(message) + await asyncio.sleep(0.1) + + # Then + call_args = mock_post.call_args + assert call_args is not None + payload = call_args[1]["json"] + assert payload["args"] == [1, 2, 3] + assert payload["kwargs"] == {"key": "value"} + assert payload["taskName"] == "test_task" + assert payload["worker"] == "test-broker" + assert "queuedAt" in payload From 5183c9f39258269eaa50664a22d388dfae5c7931 Mon Sep 17 00:00:00 2001 From: Anfimov Dima Date: Thu, 30 Oct 2025 11:45:28 +0100 Subject: [PATCH 2/4] chore: enable anyio_mode=auto --- poetry.lock | 16 +++++++--------- pyproject.toml | 4 +++- tests/api/test_receiver_task.py | 2 -- tests/api/test_scheduler.py | 4 ---- tests/brokers/test_inmemory.py | 7 ------- tests/cli/scheduler/test_updater.py | 4 ---- tests/depends/test_progress_tracker.py | 3 --- tests/formatters/test_json_formatter.py | 4 ---- tests/formatters/test_proxy_formatter.py | 4 ---- tests/middlewares/test_simple_retry.py | 3 --- tests/middlewares/test_task_retry.py | 7 ------- .../middlewares/test_taskiq_admin_middleware.py | 5 ++--- tests/receiver/test_receiver.py | 17 ----------------- tests/scheduler/test_label_based_sched.py | 3 --- tests/scheduler/test_scheduler.py | 3 --- tests/serializers/test_json_serializer.py | 4 ---- tests/test_funcs.py | 3 --- tests/test_requeue.py | 4 ---- tests/test_retry_task.py | 1 - tests/test_task.py | 1 - tests/test_utils.py | 4 ---- 21 files changed, 12 insertions(+), 91 deletions(-) diff --git a/poetry.lock b/poetry.lock index 803af00d..f89e29df 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -151,14 +151,14 @@ files = [ [[package]] name = "anyio" -version = "4.9.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -168,9 +168,7 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "async-timeout" @@ -2370,4 +2368,4 @@ zmq = ["pyzmq"] [metadata] lock-version = "2.1" python-versions = "^3.9" -content-hash = "8ede2b0845e691ebbdc2740c7b17d44aa489635c934ea77adbf047c02341a04e" +content-hash = "2b3feaf434e86a3923bb75a66daf60dff0d43d64a1e27d6d2f269a891621af01" diff --git a/pyproject.toml b/pyproject.toml index 6a900425..8b0d592e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ pydantic = ">=1.0,<=3.0" importlib-metadata = "*" pycron = "^3.0.0" taskiq_dependencies = ">=1.3.1,<2" -anyio = ">=3" +anyio = ">=4" packaging = ">=19" # For prometheus metrics prometheus_client = { version = "^0", optional = true } @@ -104,6 +104,7 @@ multi_line_output = 3 [tool.pytest.ini_options] log_level = 'INFO' +anyio_mode = "auto" [tool.coverage.run] omit = [ @@ -174,6 +175,7 @@ line-length = 88 "SLF001", # Private member accessed "S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes "D101", # Missing docstring in public class + "D102", # Missing docstring in public method ] [tool.ruff.lint.pydocstyle] diff --git a/tests/api/test_receiver_task.py b/tests/api/test_receiver_task.py index 975ccb8f..bae1df8d 100644 --- a/tests/api/test_receiver_task.py +++ b/tests/api/test_receiver_task.py @@ -7,7 +7,6 @@ from tests.utils import AsyncQueueBroker -@pytest.mark.anyio async def test_successful() -> None: broker = AsyncQueueBroker() kicked = 0 @@ -28,7 +27,6 @@ def test_func() -> None: assert kicked == desired_kicked -@pytest.mark.anyio async def test_cancelation() -> None: broker = AsyncQueueBroker() kicked = 0 diff --git a/tests/api/test_scheduler.py b/tests/api/test_scheduler.py index 96546d38..153de3ca 100644 --- a/tests/api/test_scheduler.py +++ b/tests/api/test_scheduler.py @@ -2,15 +2,12 @@ import contextlib from datetime import datetime, timedelta, timezone -import pytest - from taskiq import TaskiqScheduler from taskiq.api import run_scheduler_task from taskiq.schedule_sources import LabelScheduleSource from tests.utils import AsyncQueueBroker -@pytest.mark.anyio async def test_successful() -> None: broker = AsyncQueueBroker() scheduler = TaskiqScheduler(broker, sources=[LabelScheduleSource(broker)]) @@ -26,7 +23,6 @@ def _() -> None: scheduler_task.cancel() -@pytest.mark.anyio async def test_cancelation() -> None: broker = AsyncQueueBroker() scheduler = TaskiqScheduler(broker, sources=[LabelScheduleSource(broker)]) diff --git a/tests/brokers/test_inmemory.py b/tests/brokers/test_inmemory.py index 422ecc18..9dc85019 100644 --- a/tests/brokers/test_inmemory.py +++ b/tests/brokers/test_inmemory.py @@ -8,7 +8,6 @@ from taskiq.state import TaskiqState -@pytest.mark.anyio async def test_inmemory_success() -> None: broker = InMemoryBroker() test_val = uuid.uuid4().hex @@ -23,7 +22,6 @@ async def task() -> str: assert not broker._running_tasks -@pytest.mark.anyio async def test_cannot_listen() -> None: broker = InMemoryBroker() @@ -32,7 +30,6 @@ async def test_cannot_listen() -> None: pass -@pytest.mark.anyio async def test_startup() -> None: broker = InMemoryBroker() test_value = uuid.uuid4().hex @@ -51,7 +48,6 @@ async def _c_startup(state: TaskiqState) -> None: assert broker.state.from_client == test_value -@pytest.mark.anyio async def test_shutdown() -> None: broker = InMemoryBroker() test_value = uuid.uuid4().hex @@ -70,7 +66,6 @@ async def _c_startup(state: TaskiqState) -> None: assert broker.state.from_client == test_value -@pytest.mark.anyio async def test_execution() -> None: broker = InMemoryBroker() test_value = uuid.uuid4().hex @@ -87,7 +82,6 @@ async def test_task() -> str: assert result.return_value == test_value -@pytest.mark.anyio async def test_inline_awaits() -> None: broker = InMemoryBroker(await_inplace=True) slept = False @@ -104,7 +98,6 @@ async def test_task() -> None: assert not broker._running_tasks -@pytest.mark.anyio async def test_wait_all() -> None: broker = InMemoryBroker() slept = False diff --git a/tests/cli/scheduler/test_updater.py b/tests/cli/scheduler/test_updater.py index 2ac9ef8d..3ca62e82 100644 --- a/tests/cli/scheduler/test_updater.py +++ b/tests/cli/scheduler/test_updater.py @@ -1,8 +1,6 @@ from datetime import datetime from typing import List, Union -import pytest - from taskiq import InMemoryBroker, ScheduleSource from taskiq.cli.scheduler.run import get_all_schedules from taskiq.scheduler.scheduled_task import ScheduledTask @@ -20,7 +18,6 @@ async def get_schedules(self) -> List[ScheduledTask]: return self.schedules -@pytest.mark.anyio async def test_get_schedules_success() -> None: """Tests that schedules are returned correctly.""" schedules1 = [ @@ -62,7 +59,6 @@ async def test_get_schedules_success() -> None: ] -@pytest.mark.anyio async def test_get_schedules_error() -> None: """Tests that if source returned an error, empty list will be returned.""" source1 = DummySource( diff --git a/tests/depends/test_progress_tracker.py b/tests/depends/test_progress_tracker.py index 040381b0..57be6e93 100644 --- a/tests/depends/test_progress_tracker.py +++ b/tests/depends/test_progress_tracker.py @@ -56,7 +56,6 @@ def get_message( ) -@pytest.mark.anyio @pytest.mark.parametrize( "state,meta", [ @@ -82,7 +81,6 @@ async def test_func(tes_val: ProgressTracker[Any] = TaskiqDepends()) -> None: assert progress.state == state -@pytest.mark.anyio async def test_progress_tracker_ctx_none() -> None: broker = InMemoryBroker() @@ -98,7 +96,6 @@ async def test_func() -> None: assert progress is None -@pytest.mark.anyio @pytest.mark.parametrize( "state,meta", [ diff --git a/tests/formatters/test_json_formatter.py b/tests/formatters/test_json_formatter.py index 17a37185..f95b496d 100644 --- a/tests/formatters/test_json_formatter.py +++ b/tests/formatters/test_json_formatter.py @@ -1,12 +1,9 @@ import json -import pytest - from taskiq.formatters.json_formatter import JSONFormatter from taskiq.message import BrokerMessage, TaskiqMessage -@pytest.mark.anyio async def test_json_dumps() -> None: fmt = JSONFormatter() msg = TaskiqMessage( @@ -34,7 +31,6 @@ async def test_json_dumps() -> None: assert json.loads(dumped.message) == json.loads(expected.message) -@pytest.mark.anyio async def test_json_loads() -> None: fmt = JSONFormatter() msg = ( diff --git a/tests/formatters/test_proxy_formatter.py b/tests/formatters/test_proxy_formatter.py index 8d583f16..53095c66 100644 --- a/tests/formatters/test_proxy_formatter.py +++ b/tests/formatters/test_proxy_formatter.py @@ -1,10 +1,7 @@ -import pytest - from taskiq.brokers.inmemory_broker import InMemoryBroker from taskiq.message import BrokerMessage, TaskiqMessage -@pytest.mark.anyio async def test_proxy_dumps() -> None: # uses json serializer by default broker = InMemoryBroker() @@ -29,7 +26,6 @@ async def test_proxy_dumps() -> None: assert broker.formatter.dumps(msg) == expected -@pytest.mark.anyio async def test_proxy_loads() -> None: # uses json serializer by default broker = InMemoryBroker() diff --git a/tests/middlewares/test_simple_retry.py b/tests/middlewares/test_simple_retry.py index 7a0c12d3..8cd6c227 100644 --- a/tests/middlewares/test_simple_retry.py +++ b/tests/middlewares/test_simple_retry.py @@ -17,7 +17,6 @@ def broker() -> AsyncMock: return mocked_broker -@pytest.mark.anyio async def test_successful_retry(broker: AsyncMock) -> None: middleware = SimpleRetryMiddleware() middleware.set_broker(broker) @@ -39,7 +38,6 @@ async def test_successful_retry(broker: AsyncMock) -> None: assert resend.labels["_retries"] == "1" -@pytest.mark.anyio async def test_no_retry(broker: AsyncMock) -> None: middleware = SimpleRetryMiddleware() middleware.set_broker(broker) @@ -57,7 +55,6 @@ async def test_no_retry(broker: AsyncMock) -> None: broker.kick.assert_not_called() -@pytest.mark.anyio async def test_max_retries(broker: AsyncMock) -> None: middleware = SimpleRetryMiddleware(default_retry_count=3) middleware.set_broker(broker) diff --git a/tests/middlewares/test_task_retry.py b/tests/middlewares/test_task_retry.py index 8d5dfca9..59798633 100644 --- a/tests/middlewares/test_task_retry.py +++ b/tests/middlewares/test_task_retry.py @@ -6,7 +6,6 @@ from taskiq.exceptions import NoResultError -@pytest.mark.anyio async def test_wait_result() -> None: """Tests wait_result.""" broker = InMemoryBroker().with_middlewares( @@ -31,7 +30,6 @@ async def run_task() -> str: assert resp.return_value == "hello world!" -@pytest.mark.anyio async def test_wait_result_error() -> None: """Tests wait_result.""" broker = InMemoryBroker().with_middlewares( @@ -64,7 +62,6 @@ async def run_task() -> str: assert resp.return_value == "hello world!" -@pytest.mark.anyio async def test_wait_result_no_result() -> None: """Tests wait_result.""" broker = InMemoryBroker().with_middlewares( @@ -100,7 +97,6 @@ async def run_task() -> str: await broker.result_backend.get_result(task.task_id) -@pytest.mark.anyio async def test_max_retries() -> None: """Tests wait_result.""" broker = InMemoryBroker().with_middlewares( @@ -127,7 +123,6 @@ def run_task() -> str: assert str(resp.error) == str(runs) -@pytest.mark.anyio async def test_no_retry() -> None: broker = InMemoryBroker().with_middlewares( SimpleRetryMiddleware( @@ -153,7 +148,6 @@ def run_task() -> str: assert str(resp.error) == str(runs) -@pytest.mark.anyio async def test_retry_of_custom_exc_types_of_simple_middleware() -> None: # test that the passed error will be handled broker = InMemoryBroker().with_middlewares( @@ -206,7 +200,6 @@ def run_task2() -> None: assert runs == 1 -@pytest.mark.anyio async def test_retry_of_custom_exc_types_of_smart_middleware() -> None: # test that the passed error will be handled broker = InMemoryBroker().with_middlewares( diff --git a/tests/middlewares/test_taskiq_admin_middleware.py b/tests/middlewares/test_taskiq_admin_middleware.py index 343a5eea..bc7c0e3a 100644 --- a/tests/middlewares/test_taskiq_admin_middleware.py +++ b/tests/middlewares/test_taskiq_admin_middleware.py @@ -1,4 +1,5 @@ import asyncio +from typing import AsyncGenerator from unittest.mock import AsyncMock, Mock, patch import pytest @@ -8,7 +9,7 @@ @pytest.fixture -async def middleware() -> TaskiqAdminMiddleware: +async def middleware() -> AsyncGenerator[TaskiqAdminMiddleware, None]: middleware = TaskiqAdminMiddleware( url="http://localhost:8000", api_token="test-token", # noqa: S106 @@ -42,7 +43,6 @@ def _make_mock_response() -> AsyncMock: class TestTaskiqAdminMiddlewarePostSend: - @pytest.mark.anyio async def test_when_post_send_is_called__then_queued_endpoint_is_called( self, middleware: TaskiqAdminMiddleware, @@ -62,7 +62,6 @@ async def test_when_post_send_is_called__then_queued_endpoint_is_called( assert mock_post.call_args is not None assert "/api/tasks/task-123/queued" in mock_post.call_args[0][0] - @pytest.mark.anyio async def test_when_post_send_is_called__then_payload_includes_task_info( self, middleware: TaskiqAdminMiddleware, diff --git a/tests/receiver/test_receiver.py b/tests/receiver/test_receiver.py index 2fd3f83f..11ac8b68 100644 --- a/tests/receiver/test_receiver.py +++ b/tests/receiver/test_receiver.py @@ -41,7 +41,6 @@ def get_receiver( ) -@pytest.mark.anyio async def test_run_task_successful_async() -> None: """Tests that run_task can run async tasks.""" @@ -64,7 +63,6 @@ async def test_func(param: int) -> int: assert result.return_value == 1 -@pytest.mark.anyio async def test_run_task_successful_sync() -> None: """Tests that run_task can run sync tasks.""" @@ -86,7 +84,6 @@ def test_func(param: int) -> int: assert result.return_value == 1 -@pytest.mark.anyio async def test_run_task_exception() -> None: """Tests that run_task can run sync tasks.""" @@ -109,7 +106,6 @@ def test_func() -> None: assert result.is_err -@pytest.mark.anyio async def test_run_timeouts() -> None: async def test_func() -> None: await asyncio.sleep(2) @@ -131,7 +127,6 @@ async def test_func() -> None: assert result.is_err -@pytest.mark.anyio async def test_run_timeouts_sync() -> None: def test_func() -> None: time.sleep(2) @@ -153,7 +148,6 @@ def test_func() -> None: assert result.is_err -@pytest.mark.anyio async def test_run_task_exception_middlewares() -> None: """Tests that run_task can run sync tasks.""" @@ -190,7 +184,6 @@ def test_func() -> None: assert _TestMiddleware.found_exceptions[0].__class__ is ValueError -@pytest.mark.anyio async def test_callback_success() -> None: """Test that callback function works well.""" broker = InMemoryBroker() @@ -218,7 +211,6 @@ async def my_task() -> int: assert called_times == 1 -@pytest.mark.anyio async def test_callback_no_dep_info() -> None: """Test that callback function works well.""" broker = InMemoryBroker() @@ -253,7 +245,6 @@ async def my_task(dep: int = Depends(dependency)) -> None: assert ret_val == expected -@pytest.mark.anyio async def test_callback_success_ackable() -> None: """Test that acking works.""" broker = InMemoryBroker() @@ -292,7 +283,6 @@ def ack_callback() -> None: assert acked -@pytest.mark.anyio async def test_callback_success_ackable_async() -> None: """Test that acks work with async functions.""" broker = InMemoryBroker() @@ -331,7 +321,6 @@ async def ack_callback() -> None: assert acked -@pytest.mark.anyio async def test_callback_wrong_format() -> None: """Test that wrong format of a message won't throw an error.""" receiver = get_receiver() @@ -341,7 +330,6 @@ async def test_callback_wrong_format() -> None: ) -@pytest.mark.anyio async def test_callback_unknown_task() -> None: """Tests that running an unknown task won't throw an error.""" broker = InMemoryBroker() @@ -360,7 +348,6 @@ async def test_callback_unknown_task() -> None: await receiver.callback(broker_message.message) -@pytest.mark.anyio async def test_custom_ctx() -> None: """Tests that run_task can run sync tasks.""" @@ -400,7 +387,6 @@ def test_func(tes_val: MyTestClass = Depends()) -> int: assert not result.is_err -@pytest.mark.anyio async def test_callback_semaphore() -> None: """Test that callback function semaphore works well.""" max_async_tasks = 3 @@ -426,7 +412,6 @@ async def task_sem() -> int: listen_task.cancel() -@pytest.mark.anyio async def test_no_result_error() -> None: broker = InMemoryBroker() executed = asyncio.Event() @@ -444,7 +429,6 @@ async def task_no_result() -> int: assert not broker._running_tasks -@pytest.mark.anyio async def test_result() -> None: broker = InMemoryBroker() @@ -459,7 +443,6 @@ async def task_no_result() -> str: assert not broker._running_tasks -@pytest.mark.anyio async def test_error_result() -> None: broker = InMemoryBroker() diff --git a/tests/scheduler/test_label_based_sched.py b/tests/scheduler/test_label_based_sched.py index 6eddc94b..0fab6f86 100644 --- a/tests/scheduler/test_label_based_sched.py +++ b/tests/scheduler/test_label_based_sched.py @@ -14,7 +14,6 @@ from taskiq.scheduler.scheduler import TaskiqScheduler -@pytest.mark.anyio @pytest.mark.parametrize( "schedule_label", [ @@ -60,7 +59,6 @@ def task() -> None: assert task_from_broker.labels == {"schedule": schedule_label} -@pytest.mark.anyio async def test_label_discovery_no_cron() -> None: broker = InMemoryBroker() @@ -77,7 +75,6 @@ def task() -> None: assert schedules == [] -@pytest.mark.anyio async def test_task_scheduled_at_time_runs_only_once(mock_sleep: None) -> None: event = asyncio.Event() broker = InMemoryBroker() diff --git a/tests/scheduler/test_scheduler.py b/tests/scheduler/test_scheduler.py index 905c840a..18c7f2ac 100644 --- a/tests/scheduler/test_scheduler.py +++ b/tests/scheduler/test_scheduler.py @@ -1,7 +1,5 @@ from typing import List -import pytest - from taskiq.abc.schedule_source import ScheduleSource from taskiq.brokers.inmemory_broker import InMemoryBroker from taskiq.exceptions import ScheduledTaskCancelledError @@ -22,7 +20,6 @@ def pre_send( raise ScheduledTaskCancelledError -@pytest.mark.anyio async def test_scheduled_task_cancelled() -> None: broker = InMemoryBroker() source = CancellingScheduleSource() diff --git a/tests/serializers/test_json_serializer.py b/tests/serializers/test_json_serializer.py index 7685185d..f7d4e9b1 100644 --- a/tests/serializers/test_json_serializer.py +++ b/tests/serializers/test_json_serializer.py @@ -1,9 +1,6 @@ -import pytest - from taskiq.serializers.json_serializer import JSONSerializer -@pytest.mark.anyio async def test_json_dumpb() -> None: serizalizer = JSONSerializer() assert serizalizer.dumpb(None) == b"null" # noqa: PLR2004 @@ -13,7 +10,6 @@ async def test_json_dumpb() -> None: assert serizalizer.dumpb({"a": "b"}) == b'{"a": "b"}' # noqa: PLR2004 -@pytest.mark.anyio async def test_json_loadb() -> None: serizalizer = JSONSerializer() assert serizalizer.loadb(b"null") is None diff --git a/tests/test_funcs.py b/tests/test_funcs.py index b014b765..75a2bdaa 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -8,7 +8,6 @@ from taskiq.task import AsyncTaskiqTask -@pytest.mark.anyio async def test_gather() -> None: """Test successful task gathering.""" rb_mock = AsyncMock() @@ -21,7 +20,6 @@ async def test_gather() -> None: assert await gather(task1, task2) == (1, 1) # type: ignore -@pytest.mark.anyio async def test_gather_timeout() -> None: """Tests how gather works if timeout is reached.""" rb_mock = AsyncMock() @@ -34,7 +32,6 @@ async def test_gather_timeout() -> None: await gather(task1, task2, timeout=0.4) -@pytest.mark.anyio async def test_gather_result_backend_error() -> None: """Test how gather works if result backend doesn't work.""" rb_mock = AsyncMock() diff --git a/tests/test_requeue.py b/tests/test_requeue.py index 5f836368..c451b6df 100644 --- a/tests/test_requeue.py +++ b/tests/test_requeue.py @@ -1,9 +1,6 @@ -import pytest - from taskiq import Context, InMemoryBroker, TaskiqDepends -@pytest.mark.anyio async def test_requeue() -> None: broker = InMemoryBroker() @@ -26,7 +23,6 @@ async def task(context: Context = TaskiqDepends()) -> None: assert runs_count == 2 -@pytest.mark.anyio async def test_requeue_from_dependency() -> None: broker = InMemoryBroker() diff --git a/tests/test_retry_task.py b/tests/test_retry_task.py index 95eaf4dd..a58748d4 100644 --- a/tests/test_retry_task.py +++ b/tests/test_retry_task.py @@ -14,7 +14,6 @@ "retry_count", range(5), ) -@pytest.mark.anyio async def test_save_task_id_for_retry(retry_count: int) -> None: broker = InMemoryBroker().with_middlewares( SmartRetryMiddleware( diff --git a/tests/test_task.py b/tests/test_task.py index da57dbb2..e6200ff8 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -50,7 +50,6 @@ async def get_result( serializers.JSONSerializer(), ], ) -@pytest.mark.anyio async def test_res_parsing_success(serializer: TaskiqSerializer) -> None: class MyResult(BaseModel): name: str diff --git a/tests/test_utils.py b/tests/test_utils.py index bd27f09c..485ab319 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,9 +1,6 @@ -import pytest - from taskiq.utils import maybe_awaitable -@pytest.mark.anyio async def test_maybe_awaitable_coroutine() -> None: async def meme() -> int: return 1 @@ -12,7 +9,6 @@ async def meme() -> int: assert val == 1 -@pytest.mark.anyio async def test_maybe_awaitable_sync() -> None: def meme() -> int: return 1 From 8c772f45bc93cec37b9f4c6a801cc8ef17b912f6 Mon Sep 17 00:00:00 2001 From: Anfimov Dima Date: Thu, 30 Oct 2025 11:59:03 +0100 Subject: [PATCH 3/4] fix: type hint in TaskiqAdminMiddleware --- taskiq/middlewares/taskiq_admin_middleware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taskiq/middlewares/taskiq_admin_middleware.py b/taskiq/middlewares/taskiq_admin_middleware.py index 19b51dc9..659e78c7 100644 --- a/taskiq/middlewares/taskiq_admin_middleware.py +++ b/taskiq/middlewares/taskiq_admin_middleware.py @@ -1,7 +1,7 @@ import asyncio from datetime import datetime, timezone from logging import getLogger -from typing import Any +from typing import Any, Optional from urllib.parse import urljoin import aiohttp @@ -36,7 +36,7 @@ def __init__( url: str, api_token: str, timeout: int = 5, - taskiq_broker_name: str | None = None, + taskiq_broker_name: Optional[str] = None, ) -> None: super().__init__() self.url = url From a6894fd7215ee60d7ed646ecbd1d32b282726884 Mon Sep 17 00:00:00 2001 From: Anfimov Dima Date: Thu, 30 Oct 2025 12:57:30 +0100 Subject: [PATCH 4/4] chore: update ruff and add rule to detect incorrect annotations --- poetry.lock | 39 +++++++++++---------- pyproject.toml | 2 ++ taskiq/middlewares/prometheus_middleware.py | 4 +-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index f89e29df..5d34d474 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1876,30 +1876,31 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "ruff" -version = "0.11.2" +version = "0.14.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.11.2-py3-none-linux_armv6l.whl", hash = "sha256:c69e20ea49e973f3afec2c06376eb56045709f0212615c1adb0eda35e8a4e477"}, - {file = "ruff-0.11.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2c5424cc1c4eb1d8ecabe6d4f1b70470b4f24a0c0171356290b1953ad8f0e272"}, - {file = "ruff-0.11.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ecf20854cc73f42171eedb66f006a43d0a21bfb98a2523a809931cda569552d9"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c543bf65d5d27240321604cee0633a70c6c25c9a2f2492efa9f6d4b8e4199bb"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20967168cc21195db5830b9224be0e964cc9c8ecf3b5a9e3ce19876e8d3a96e3"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:955a9ce63483999d9f0b8f0b4a3ad669e53484232853054cc8b9d51ab4c5de74"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:86b3a27c38b8fce73bcd262b0de32e9a6801b76d52cdb3ae4c914515f0cef608"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3b66a03b248c9fcd9d64d445bafdf1589326bee6fc5c8e92d7562e58883e30f"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0397c2672db015be5aa3d4dac54c69aa012429097ff219392c018e21f5085147"}, - {file = "ruff-0.11.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869bcf3f9abf6457fbe39b5a37333aa4eecc52a3b99c98827ccc371a8e5b6f1b"}, - {file = "ruff-0.11.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2a2b50ca35457ba785cd8c93ebbe529467594087b527a08d487cf0ee7b3087e9"}, - {file = "ruff-0.11.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7c69c74bf53ddcfbc22e6eb2f31211df7f65054bfc1f72288fc71e5f82db3eab"}, - {file = "ruff-0.11.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6e8fb75e14560f7cf53b15bbc55baf5ecbe373dd5f3aab96ff7aa7777edd7630"}, - {file = "ruff-0.11.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:842a472d7b4d6f5924e9297aa38149e5dcb1e628773b70e6387ae2c97a63c58f"}, - {file = "ruff-0.11.2-py3-none-win32.whl", hash = "sha256:aca01ccd0eb5eb7156b324cfaa088586f06a86d9e5314b0eb330cb48415097cc"}, - {file = "ruff-0.11.2-py3-none-win_amd64.whl", hash = "sha256:3170150172a8f994136c0c66f494edf199a0bbea7a409f649e4bc8f4d7084080"}, - {file = "ruff-0.11.2-py3-none-win_arm64.whl", hash = "sha256:52933095158ff328f4c77af3d74f0379e34fd52f175144cefc1b192e7ccd32b4"}, - {file = "ruff-0.11.2.tar.gz", hash = "sha256:ec47591497d5a1050175bdf4e1a4e6272cddff7da88a2ad595e1e326041d8d94"}, + {file = "ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1"}, + {file = "ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11"}, + {file = "ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096"}, + {file = "ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df"}, + {file = "ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05"}, + {file = "ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5"}, + {file = "ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e"}, + {file = "ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770"}, + {file = "ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9"}, + {file = "ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af"}, + {file = "ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a"}, + {file = "ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 8b0d592e..c151745a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,6 +123,7 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] +target-version="py39" # List of enabled rulsets. # See https://docs.astral.sh/ruff/rules/ for more information. lint.select = [ @@ -151,6 +152,7 @@ lint.select = [ "ERA", # Checks for commented out code "PL", # PyLint checks "RUF", # Specific to Ruff checks + "FA102", # Future annotations ] lint.ignore = [ "D105", # Missing docstring in magic method diff --git a/taskiq/middlewares/prometheus_middleware.py b/taskiq/middlewares/prometheus_middleware.py index 7bbf6991..8c9ed02d 100644 --- a/taskiq/middlewares/prometheus_middleware.py +++ b/taskiq/middlewares/prometheus_middleware.py @@ -44,7 +44,7 @@ def __init__( logger.debug("Initializing metrics") try: - from prometheus_client import Counter, Histogram + from prometheus_client import Counter, Histogram # noqa: PLC0415 except ImportError as exc: raise ImportError( "Cannot initialize metrics. Please install 'taskiq[metrics]'.", @@ -85,7 +85,7 @@ def startup(self) -> None: This function starts prometheus server. It starts it only in case if it's a worker process. """ - from prometheus_client import start_http_server + from prometheus_client import start_http_server # noqa: PLC0415 if self.broker.is_worker_process: try: