diff --git a/poetry.lock b/poetry.lock index 5d34d474..02e88120 100644 --- a/poetry.lock +++ b/poetry.lock @@ -740,30 +740,6 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] -[[package]] -name = "importlib-metadata" -version = "8.6.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - [[package]] name = "iniconfig" version = "2.1.0" @@ -791,23 +767,6 @@ files = [ [package.extras] compatibility = ["typing-extensions (>=4.5.0)"] -[[package]] -name = "mock" -version = "4.0.3" -description = "Rolling backport of unittest.mock for all Pythons" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, - {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, -] - -[package.extras] -build = ["blurb", "twine", "wheel"] -docs = ["sphinx"] -test = ["pytest (<5.4)", "pytest-cov"] - [[package]] name = "msgpack" version = "1.1.0" @@ -1636,24 +1595,6 @@ files = [ py = "*" pytest = ">=3.10" -[[package]] -name = "pytest-mock" -version = "3.14.0" -description = "Thin-wrapper around the mock package for easier use with pytest" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, -] - -[package.dependencies] -pytest = ">=6.2.5" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - [[package]] name = "pytest-xdist" version = "2.5.0" @@ -1692,18 +1633,6 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "pytz" -version = "2025.2" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, - {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -2013,45 +1942,6 @@ virtualenv = ">=20.29.1" [package.extras] test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"] -[[package]] -name = "types-mock" -version = "4.0.15.2" -description = "Typing stubs for mock" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "types-mock-4.0.15.2.tar.gz", hash = "sha256:83fe479741adb92210c3c92f006fe058297d5051e93c2cec36f1a9e0bae16e9e"}, - {file = "types_mock-4.0.15.2-py3-none-any.whl", hash = "sha256:39d489b6d9361b75448677680a3087701c0cfab61260363cfc0f646d2bf0a8b2"}, -] - -[[package]] -name = "types-pytz" -version = "2023.4.0.20240130" -description = "Typing stubs for pytz" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "types-pytz-2023.4.0.20240130.tar.gz", hash = "sha256:33676a90bf04b19f92c33eec8581136bea2f35ddd12759e579a624a006fd387a"}, - {file = "types_pytz-2023.4.0.20240130-py3-none-any.whl", hash = "sha256:6ce76a9f8fd22bd39b01a59c35bfa2db39b60d11a2f77145e97b730de7e64fe0"}, -] - -[[package]] -name = "types-tzlocal" -version = "5.1.0.1" -description = "Typing stubs for tzlocal" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "types-tzlocal-5.1.0.1.tar.gz", hash = "sha256:b84a115c0c68f0d0fa9af1c57f0645eeef0e539147806faf1f95ac3ac01ce47b"}, - {file = "types_tzlocal-5.1.0.1-py3-none-any.whl", hash = "sha256:0302e8067c86936de8f7e0aaedc2cfbf240080802c603df0f80312fbd4efb926"}, -] - -[package.dependencies] -types-pytz = "*" - [[package]] name = "typing-extensions" version = "4.12.2" @@ -2071,30 +1961,12 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["dev"] -markers = "platform_system == \"Windows\"" +markers = "sys_platform == \"win32\"" files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] -[[package]] -name = "tzlocal" -version = "5.3.1" -description = "tzinfo object for the local timezone" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d"}, - {file = "tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd"}, -] - -[package.dependencies] -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] - [[package]] name = "uvloop" version = "0.21.0" @@ -2337,26 +2209,6 @@ idna = ">=2.0" multidict = ">=4.0" propcache = ">=0.2.1" -[[package]] -name = "zipp" -version = "3.21.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - [extras] cbor = ["cbor2"] metrics = ["prometheus_client"] @@ -2368,5 +2220,5 @@ zmq = ["pyzmq"] [metadata] lock-version = "2.1" -python-versions = "^3.9" -content-hash = "2b3feaf434e86a3923bb75a66daf60dff0d43d64a1e27d6d2f269a891621af01" +python-versions = ">=3.9,<4" +content-hash = "8c14c0d5091c434c50e2e04b3536df554e72d9422dde9fc1c7777a32ae1d372b" diff --git a/pyproject.toml b/pyproject.toml index c151745a..8feccd20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,24 @@ -[tool.poetry] +[project] name = "taskiq" version = "0.0.0" description = "Distributed task queue with full async support" -authors = ["Pavel Kirilin "] -maintainers = ["Pavel Kirilin "] +authors = [ + {name = "Pavel Kirilin", email = ""} +] +maintainers = [ + {name = "Pavel Kirilin", email = ""} +] readme = "README.md" repository = "https://github.com/taskiq-python/taskiq" homepage = "https://taskiq-python.github.io/" documentation = "https://taskiq-python.github.io/" -license = "LICENSE" +license = "MIT" +license-files = ["LICENSE"] classifiers = [ "Typing :: Typed", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -26,63 +30,71 @@ classifiers = [ "Development Status :: 3 - Alpha", ] keywords = ["taskiq", "tasks", "distributed", "async"] +requires-python = ">=3.9,<4" +dependencies = [ + "typing-extensions>=3.10.0.0; python_version < '3.11'", + "pydantic>=1.0,<=3.0", + "pycron>=3.0.0", + "taskiq_dependencies>=1.3.1,<2", + "anyio>=4", + "packaging>=19", + "izulu==0.50.0", + "aiohttp>=3", +] + +[project.optional-dependencies] +zmq = [ + "pyzmq>=26", +] +uv = [ + "uvloop>=0.16.0,<1; sys_platform != 'win32'", +] +metrics = [ + "prometheus_client>=0", +] +reload = [ + "watchdog>=4", + "gitignore-parser>=0", +] +orjson = [ + "orjson>=3", +] +msgpack = [ + "msgpack>=1.0.7", +] +cbor = [ + "cbor2>=5", +] + +[dependency-groups] +dev = [ + "pre-commit>=4.3.0", + # lint + "ruff>=0", + "black>=22.6.0", + # type check + "mypy>=1", + # test + "pytest>=7.1.2", + "pytest-cov>=3.0.0", + "coverage>=6.4.2", + "pytest-xdist[psutil]>=2.5.0", + "tox>=4.6.4", + "freezegun>=1.2.2", + "tzdata>=2025.2; sys_platform == 'win32'", +] + +[project.urls] +Homepage = "https://taskiq-python.github.io/" +Documentation = "https://taskiq-python.github.io/" +Repository = "https://github.com/taskiq-python/taskiq" +"Bug Tracker" = "https://github.com/taskiq-python/taskiq/issues" +Changelog = "https://github.com/taskiq-python/taskiq/releases" -[tool.poetry.dependencies] -python = "^3.9" -typing-extensions = ">=3.10.0.0" -pydantic = ">=1.0,<=3.0" -importlib-metadata = "*" -pycron = "^3.0.0" -taskiq_dependencies = ">=1.3.1,<2" -anyio = ">=4" -packaging = ">=19" -# For prometheus metrics -prometheus_client = { version = "^0", optional = true } -# For ZMQBroker -pyzmq = { version = "^26", optional = true } -# For speed -uvloop = { version = ">=0.16.0,<1", optional = true, markers = "sys_platform != 'win32'" } -# For hot-reload. -watchdog = { version = "^4", optional = true } -gitignore-parser = { version = "^0", optional = true } -pytz = "*" -orjson = { version = "^3", optional = true } -msgpack = { version = "^1.0.7", optional = true } -cbor2 = { version = "^5", optional = true } -izulu = "0.50.0" -aiohttp = "^3" - -[tool.poetry.group.dev.dependencies] -pytest = "^7.1.2" -ruff = "^0" -black = { version = "^22.6.0", allow-prereleases = true } -mypy = "^1" -pre-commit = "^4.3.0" -coverage = "^6.4.2" -pytest-cov = "^3.0.0" -mock = "^4.0.3" -pytest-xdist = { version = "^2.5.0", extras = ["psutil"] } -types-mock = "^4.0.15" -tox = "^4.6.4" -freezegun = "^1.2.2" -pytest-mock = "^3.11.1" -tzlocal = "^5.0.1" -types-tzlocal = "^5.0.1.1" -types-pytz = "^2023.3.1.1" - -[tool.poetry.extras] -zmq = ["pyzmq"] -uv = ["uvloop"] -metrics = ["prometheus_client"] -reload = ["watchdog", "gitignore-parser"] -orjson = ["orjson"] -msgpack = ["msgpack"] -cbor = ["cbor2"] - -[tool.poetry.scripts] +[project.scripts] taskiq = "taskiq.__main__:main" -[tool.poetry.plugins.taskiq_cli] +[project.entry-points.taskiq_cli] worker = "taskiq.cli.worker.cmd:WorkerCMD" scheduler = "taskiq.cli.scheduler.cmd:SchedulerCMD" diff --git a/taskiq/__main__.py b/taskiq/__main__.py index cad25c5f..06b3f6d7 100644 --- a/taskiq/__main__.py +++ b/taskiq/__main__.py @@ -1,9 +1,8 @@ import argparse import sys +from importlib.metadata import entry_points from typing import Dict -from importlib_metadata import entry_points - from taskiq import __version__ from taskiq.abc.cmd import TaskiqCMD diff --git a/taskiq/abc/broker.py b/taskiq/abc/broker.py index f3c693a2..2265a3c0 100644 --- a/taskiq/abc/broker.py +++ b/taskiq/abc/broker.py @@ -23,8 +23,6 @@ ) from uuid import uuid4 -from typing_extensions import ParamSpec, Self, TypeAlias - from taskiq.abc.middleware import TaskiqMiddleware from taskiq.abc.serializer import TaskiqSerializer from taskiq.acks import AckableMessage @@ -39,6 +37,16 @@ from taskiq.utils import maybe_awaitable, remove_suffix from taskiq.warnings import TaskiqDeprecationWarning +if sys.version_info >= (3, 11): + from typing import ParamSpec, Self, TypeAlias +elif sys.version_info >= (3, 10): + from typing import ParamSpec, TypeAlias + + from typing_extensions import Self +else: + from typing_extensions import ParamSpec, Self, TypeAlias + + if TYPE_CHECKING: # pragma: no cover from taskiq.abc.formatter import TaskiqFormatter from taskiq.abc.result_backend import AsyncResultBackend diff --git a/taskiq/brokers/shared_broker.py b/taskiq/brokers/shared_broker.py index def2c797..2a18ec42 100644 --- a/taskiq/brokers/shared_broker.py +++ b/taskiq/brokers/shared_broker.py @@ -1,13 +1,18 @@ +import sys from typing import Any, AsyncGenerator, Optional, TypeVar -from typing_extensions import ParamSpec - from taskiq.abc.broker import AsyncBroker from taskiq.decor import AsyncTaskiqDecoratedTask from taskiq.exceptions import SharedBrokerListenError, SharedBrokerSendTaskError from taskiq.kicker import AsyncKicker from taskiq.message import BrokerMessage +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + + _ReturnType = TypeVar("_ReturnType") _Params = ParamSpec("_Params") diff --git a/taskiq/cli/scheduler/run.py b/taskiq/cli/scheduler/run.py index 79b85855..0349486f 100644 --- a/taskiq/cli/scheduler/run.py +++ b/taskiq/cli/scheduler/run.py @@ -4,8 +4,8 @@ from datetime import datetime, timedelta, timezone from logging import basicConfig, getLogger from typing import Any, Dict, List, Optional, Set, Tuple +from zoneinfo import ZoneInfo -import pytz from pycron import is_now from taskiq.abc.schedule_source import ScheduleSource @@ -29,7 +29,7 @@ def to_tz_aware(time: datetime) -> datetime: :return: timezone aware time. """ if time.tzinfo is None: - return time.replace(tzinfo=pytz.UTC) + return time.replace(tzinfo=timezone.utc) return time @@ -81,7 +81,7 @@ def get_task_delay(task: ScheduledTask) -> Optional[int]: :param task: task to check. :return: True if task must be sent. """ - now = datetime.now(tz=pytz.UTC) + now = datetime.now(tz=timezone.utc) if task.cron is not None: # If user specified cron offset we apply it. # If it's timedelta, we simply add the delta to current time. @@ -90,7 +90,7 @@ def get_task_delay(task: ScheduledTask) -> Optional[int]: # If timezone was specified as string we convert it timezone # offset and then apply. elif task.cron_offset and isinstance(task.cron_offset, str): - now = now.astimezone(pytz.timezone(task.cron_offset)) + now = now.astimezone(ZoneInfo(task.cron_offset)) if is_now(task.cron, now): return 0 return None @@ -159,9 +159,9 @@ async def run_scheduler_loop( # noqa: C901 loop = asyncio.get_event_loop() running_schedules: Dict[str, asyncio.Task[Any]] = {} ran_cron_jobs: Set[str] = set() - current_minute = datetime.now(tz=pytz.UTC).minute + current_minute = datetime.now(tz=timezone.utc).minute while True: - now = datetime.now(tz=pytz.UTC) + now = datetime.now(tz=timezone.utc) # If minute changed, we need to clear # ran_cron_jobs set and update current minute. if now.minute != current_minute: @@ -220,7 +220,7 @@ async def run_scheduler_loop( # noqa: C901 task_future.get_name().removeprefix("schedule_"), ), ) - delay = next_run - datetime.now(tz=pytz.UTC) + delay = next_run - datetime.now(tz=timezone.utc) logger.debug( "Sleeping for %.2f seconds before getting schedules.", delay.total_seconds(), diff --git a/taskiq/cli/watcher.py b/taskiq/cli/watcher.py index 8c6cb29c..0eee0c68 100644 --- a/taskiq/cli/watcher.py +++ b/taskiq/cli/watcher.py @@ -45,7 +45,7 @@ def dispatch(self, event: FileSystemEvent) -> None: return except Exception as exc: logger.info( - f"Cannot check path `{event.src_path}` in gitignore. Cause: {exc}", + f"Cannot check path `{event.src_path!r}` in gitignore. Cause: {exc}", ) return diff --git a/taskiq/compat.py b/taskiq/compat.py index ce54bb9a..f221f67d 100644 --- a/taskiq/compat.py +++ b/taskiq/compat.py @@ -1,9 +1,9 @@ # flake8: noqa from functools import lru_cache +from importlib.metadata import version from typing import Any, Dict, Hashable, Optional, Type, TypeVar, Union import pydantic -from importlib_metadata import version from packaging.version import Version, parse PYDANTIC_VER = parse(version("pydantic")) @@ -64,7 +64,7 @@ def model_validate_json( model_class: Type[Model], message: Union[str, bytes, bytearray], ) -> Model: - return model_class.parse_raw(message) + return model_class.parse_raw(message) # type: ignore[arg-type] def model_dump_json(instance: Model) -> str: return instance.json() diff --git a/taskiq/decor.py b/taskiq/decor.py index 6222ac77..b94ba5ac 100644 --- a/taskiq/decor.py +++ b/taskiq/decor.py @@ -15,12 +15,15 @@ overload, ) -from typing_extensions import ParamSpec - from taskiq.kicker import AsyncKicker from taskiq.scheduler.created_schedule import CreatedSchedule from taskiq.task import AsyncTaskiqTask +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + if TYPE_CHECKING: # pragma: no cover from taskiq.abc.broker import AsyncBroker from taskiq.abc.schedule_source import ScheduleSource diff --git a/taskiq/depends/progress_tracker.py b/taskiq/depends/progress_tracker.py index fca71bb1..4d9b3dea 100644 --- a/taskiq/depends/progress_tracker.py +++ b/taskiq/depends/progress_tracker.py @@ -1,8 +1,7 @@ import enum -from typing import Generic, Optional, Union +from typing import Generic, Optional, TypeVar, Union from taskiq_dependencies import Depends -from typing_extensions import TypeVar from taskiq.compat import IS_PYDANTIC2 from taskiq.context import Context diff --git a/taskiq/kicker.py b/taskiq/kicker.py index e6b93a83..b1a91766 100644 --- a/taskiq/kicker.py +++ b/taskiq/kicker.py @@ -1,3 +1,4 @@ +import sys from collections.abc import Coroutine from dataclasses import asdict, is_dataclass from datetime import datetime @@ -16,7 +17,6 @@ ) from pydantic import BaseModel -from typing_extensions import ParamSpec from taskiq.abc.middleware import TaskiqMiddleware from taskiq.compat import model_dump @@ -28,6 +28,11 @@ from taskiq.task import AsyncTaskiqTask from taskiq.utils import maybe_awaitable +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + if TYPE_CHECKING: # pragma: no cover from taskiq.abc.broker import AsyncBroker from taskiq.abc.schedule_source import ScheduleSource diff --git a/taskiq/result/result.py b/taskiq/result/result.py index 42120cfa..eb060258 100644 --- a/taskiq/result/result.py +++ b/taskiq/result/result.py @@ -1,14 +1,19 @@ import json import pickle +import sys from functools import partial from typing import Any, Callable, Dict, Generic, Optional, TypeVar from pydantic import Field -from typing_extensions import Self from taskiq.compat import IS_PYDANTIC2 from taskiq.serialization import exception_to_python, prepare_exception +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + _ReturnType = TypeVar("_ReturnType") diff --git a/taskiq/scheduler/scheduled_task/v2.py b/taskiq/scheduler/scheduled_task/v2.py index ce28c123..1797e2f2 100644 --- a/taskiq/scheduler/scheduled_task/v2.py +++ b/taskiq/scheduler/scheduled_task/v2.py @@ -1,9 +1,14 @@ +import sys import uuid from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, Field, model_validator -from typing_extensions import Self + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self class ScheduledTask(BaseModel): diff --git a/taskiq/serialization.py b/taskiq/serialization.py index c8fd821d..588c8b29 100644 --- a/taskiq/serialization.py +++ b/taskiq/serialization.py @@ -8,14 +8,16 @@ Iterable, List, Optional, + Protocol, Set, Tuple, Type, + TypeVar, Union, + runtime_checkable, ) import pydantic -from typing_extensions import Protocol, TypeVar, runtime_checkable import taskiq.exceptions from taskiq.compat import IS_PYDANTIC2, validate_call diff --git a/taskiq/task.py b/taskiq/task.py index c4c17bc9..c413f211 100644 --- a/taskiq/task.py +++ b/taskiq/task.py @@ -1,9 +1,7 @@ import asyncio from logging import getLogger from time import time -from typing import TYPE_CHECKING, Any, Generic, Optional, Type - -from typing_extensions import TypeVar +from typing import TYPE_CHECKING, Any, Generic, Optional, Type, TypeVar from taskiq.compat import parse_obj_as from taskiq.exceptions import ( diff --git a/tests/cli/scheduler/test_task_delays.py b/tests/cli/scheduler/test_task_delays.py index 1087fe52..1e776f0c 100644 --- a/tests/cli/scheduler/test_task_delays.py +++ b/tests/cli/scheduler/test_task_delays.py @@ -1,8 +1,7 @@ import datetime +from zoneinfo import ZoneInfo -import pytz from freezegun import freeze_time -from tzlocal import get_localzone from taskiq.cli.scheduler.run import get_task_delay from taskiq.scheduler.scheduled_task import ScheduledTask @@ -23,8 +22,8 @@ def test_should_run_success() -> None: def test_should_run_cron_str_offset() -> None: - hour = datetime.datetime.now().hour - zone = get_localzone() + timezone = ZoneInfo("Europe/Paris") + hour = datetime.datetime.now(tz=timezone).hour delay = get_task_delay( ScheduledTask( task_name="", @@ -32,7 +31,7 @@ def test_should_run_cron_str_offset() -> None: args=[], kwargs={}, cron=f"* {hour} * * *", - cron_offset=str(zone), + cron_offset=str(timezone), ), ) assert delay is not None and delay >= 0 @@ -69,7 +68,7 @@ def test_time_utc_without_zone() -> None: def test_time_utc_with_zone() -> None: - time = datetime.datetime.now(tz=pytz.UTC) + time = datetime.datetime.now(tz=datetime.timezone.utc) delay = get_task_delay( ScheduledTask( task_name="", @@ -83,7 +82,7 @@ def test_time_utc_with_zone() -> None: def test_time_utc_with_local_zone() -> None: - localtz = get_localzone() + localtz = ZoneInfo("Europe/Paris") time = datetime.datetime.now(tz=localtz) delay = get_task_delay( ScheduledTask( @@ -99,7 +98,9 @@ def test_time_utc_with_local_zone() -> None: @freeze_time("2023-01-14 12:00:00") def test_time_localtime_without_zone() -> None: - time = datetime.datetime.now(tz=pytz.FixedOffset(240)).replace(tzinfo=None) + time = datetime.datetime.now( + tz=datetime.timezone(datetime.timedelta(minutes=240)), + ).replace(tzinfo=None) time_to_run = time - datetime.timedelta(seconds=1) delay = get_task_delay( @@ -112,8 +113,10 @@ def test_time_localtime_without_zone() -> None: ), ) - expected_delay = time_to_run.replace(tzinfo=pytz.UTC) - datetime.datetime.now( - pytz.UTC, + expected_delay = time_to_run.replace( + tzinfo=datetime.timezone.utc, + ) - datetime.datetime.now( + datetime.timezone.utc, ) assert delay == int(expected_delay.total_seconds()) @@ -121,7 +124,9 @@ def test_time_localtime_without_zone() -> None: @freeze_time("2023-01-14 12:00:00") def test_time_delay() -> None: - time = datetime.datetime.now(tz=pytz.UTC) + datetime.timedelta(seconds=15) + time = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta( + seconds=15, + ) delay = get_task_delay( ScheduledTask( task_name="", @@ -136,7 +141,7 @@ def test_time_delay() -> None: @freeze_time("2023-01-14 12:00:00.05") def test_time_delay_with_milliseconds() -> None: - time = datetime.datetime.now(tz=pytz.UTC) + datetime.timedelta( + time = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta( seconds=15, milliseconds=150, ) diff --git a/tests/conftest.py b/tests/conftest.py index 15649f7a..8a652db8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,8 @@ import asyncio from typing import Generator +from unittest.mock import patch import pytest -from pytest_mock import MockerFixture from taskiq.abc.broker import AsyncBroker @@ -33,9 +33,10 @@ def reset_broker() -> Generator[None, None, None]: @pytest.fixture -def mock_sleep(mocker: MockerFixture) -> None: +def mock_sleep() -> Generator[None, None, None]: async def _fast_sleep(delay: float) -> None: await asyncio_sleep(delay / 10000) asyncio_sleep = asyncio.sleep - mocker.patch("asyncio.sleep", _fast_sleep) + with patch("asyncio.sleep", _fast_sleep): + yield diff --git a/tests/middlewares/test_simple_retry.py b/tests/middlewares/test_simple_retry.py index 8cd6c227..783d98b7 100644 --- a/tests/middlewares/test_simple_retry.py +++ b/tests/middlewares/test_simple_retry.py @@ -1,7 +1,7 @@ import uuid +from unittest.mock import AsyncMock import pytest -from mock import AsyncMock from taskiq.formatters.json_formatter import JSONFormatter from taskiq.message import TaskiqMessage diff --git a/tests/scheduler/test_label_based_sched.py b/tests/scheduler/test_label_based_sched.py index 0fab6f86..88a3bfb7 100644 --- a/tests/scheduler/test_label_based_sched.py +++ b/tests/scheduler/test_label_based_sched.py @@ -1,9 +1,8 @@ import asyncio -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Any, Dict, List import pytest -import pytz from freezegun import freeze_time from taskiq.brokers.inmemory_broker import InMemoryBroker @@ -18,9 +17,9 @@ "schedule_label", [ pytest.param([{"cron": "* * * * *"}], id="cron"), - pytest.param([{"time": datetime.now(pytz.UTC)}], id="time"), + pytest.param([{"time": datetime.now(timezone.utc)}], id="time"), pytest.param( - [{"time": datetime.now(pytz.UTC), "labels": {"foo": "bar"}}], + [{"time": datetime.now(timezone.utc), "labels": {"foo": "bar"}}], id="labels_inside_schedule", ), pytest.param( @@ -92,8 +91,8 @@ async def test_task_scheduled_at_time_runs_only_once(mock_sleep: None) -> None: @broker.task( task_name="test_task", schedule=[ - {"time": datetime.now(pytz.UTC), "args": [1]}, - {"time": datetime.now(pytz.UTC) + timedelta(days=1), "args": [2]}, + {"time": datetime.now(timezone.utc), "args": [1]}, + {"time": datetime.now(timezone.utc) + timedelta(days=1), "args": [2]}, {"cron": "1 * * * *", "args": [3]}, ], ) diff --git a/tests/serializers/test_serializers.py b/tests/serializers/test_serializers.py index f2c754c8..b3ad6149 100644 --- a/tests/serializers/test_serializers.py +++ b/tests/serializers/test_serializers.py @@ -3,7 +3,6 @@ from typing import Any import pytest -import pytz from taskiq.abc.serializer import TaskiqSerializer from taskiq.serializers import ( @@ -62,5 +61,5 @@ def test_uuid_serialization(serializer: TaskiqSerializer) -> None: ], ) def test_datetime_serialization(serializer: TaskiqSerializer) -> None: - now = datetime.datetime.now(tz=pytz.UTC) + now = datetime.datetime.now(tz=datetime.timezone.utc) assert serializer.loadb(serializer.dumpb(now)) == now diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 75a2bdaa..e00646d7 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -1,7 +1,7 @@ from typing import Any +from unittest.mock import AsyncMock import pytest -from mock import AsyncMock from taskiq.exceptions import ResultIsReadyError, TaskiqResultTimeoutError from taskiq.funcs import gather