diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6b91d3d1..97742762 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: "trailing-whitespace" - id: "check-case-conflict" @@ -20,6 +20,6 @@ repos: files: &files '^(aiogram|tests|examples)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.13.3' + rev: 'v0.14.0' hooks: - id: ruff diff --git a/aiogram/__init__.py b/aiogram/__init__.py index b243ea2f..e38c03b1 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -1,6 +1,3 @@ -import asyncio as _asyncio -from contextlib import suppress - from aiogram.dispatcher.flags import FlagGenerator from . import enums, methods, types @@ -14,12 +11,6 @@ from .utils.magic_filter import MagicFilter from .utils.text_decorations import html_decoration as html from .utils.text_decorations import markdown_decoration as md -with suppress(ImportError): - import uvloop as _uvloop - - _asyncio.set_event_loop_policy(_uvloop.EventLoopPolicy()) - - F = MagicFilter() flags = FlagGenerator() diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index a40355ab..77f6458b 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio import contextvars import signal +import sys import warnings from asyncio import CancelledError, Event, Future, Lock from collections.abc import AsyncGenerator, Awaitable @@ -656,16 +657,29 @@ class Dispatcher(Router): :return: """ with suppress(KeyboardInterrupt): - return asyncio.run( - self.start_polling( - *bots, - **kwargs, - polling_timeout=polling_timeout, - handle_as_tasks=handle_as_tasks, - backoff_config=backoff_config, - allowed_updates=allowed_updates, - handle_signals=handle_signals, - close_bot_session=close_bot_session, - tasks_concurrency_limit=tasks_concurrency_limit, - ), + coro = self.start_polling( + *bots, + **kwargs, + polling_timeout=polling_timeout, + handle_as_tasks=handle_as_tasks, + backoff_config=backoff_config, + allowed_updates=allowed_updates, + handle_signals=handle_signals, + close_bot_session=close_bot_session, + tasks_concurrency_limit=tasks_concurrency_limit, ) + + try: + import uvloop + + except ImportError: + return asyncio.run(coro) + + else: + if sys.version_info >= (3, 11): + with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner: + return runner.run(coro) + else: + uvloop.install() + return asyncio.run(coro) + return None diff --git a/pyproject.toml b/pyproject.toml index f4bfe873..ff376e2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" name = "aiogram" description = 'Modern and fully asynchronous framework for Telegram Bot API' readme = "README.rst" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.15" license = "MIT" authors = [ { name = "Alex Root Junior", email = "jroot.junior@gmail.com" }, @@ -34,6 +34,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Python Modules", @@ -41,7 +42,7 @@ classifiers = [ ] dependencies = [ "magic-filter>=1.0.12,<1.1", - "aiohttp>=3.9.0,<3.13", + "aiohttp>=3.9.0,<3.14", "pydantic>=2.4.1,<2.13", "aiofiles>=23.2.1,<24.2", "certifi>=2023.7.22", @@ -62,11 +63,11 @@ redis = [ "redis[hiredis]>=6.2.0,<7", ] mongo = [ - "motor>=3.3.2,<3.7.0", - "pymongo>4.5,<4.11", + "motor>=3.3.2,<3.8", + "pymongo>4.5,<4.16", ] proxy = [ - "aiohttp-socks~=0.8.3", + "aiohttp-socks~=0.10.1", ] i18n = [ "Babel>=2.13.0,<3", @@ -78,15 +79,13 @@ signature = [ "cryptography>=46.0.0", ] test = [ - "pytest~=7.4.2", - "pytest-html~=4.0.2", - "pytest-asyncio~=0.21.1", - "pytest-lazy-fixture~=0.6.3", - "pytest-mock~=3.12.0", - "pytest-mypy~=0.10.3", - "pytest-cov~=4.1.0", - "pytest-aiohttp~=1.0.5", - "aresponses~=2.1.6", + "pytest==8.4.2", + "pytest-html==4.1.1", + "pytest-mock==3.15.1", + "pytest-mypy==1.0.1", + "pytest-cov==7.0.0", + "pytest-aiohttp~=1.1.0", + "aresponses~=3.0.0", "pytz~=2025.2", "pycryptodomex~=3.23.0", ] @@ -106,11 +105,11 @@ docs = [ dev = [ "black~=25.9.0", "isort~=6.1.0", - "ruff~=0.13.3", + "ruff~=0.14.0", "mypy~=1.10.1", "toml~=0.10.2", "pre-commit~=4.3.0", - "packaging~=24.1", + "packaging~=25.0", "motor-types~=1.0.0b4", ] diff --git a/tests/conftest.py b/tests/conftest.py index 6a0c37f4..5034cde5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -39,7 +39,7 @@ def pytest_configure(config): config.addinivalue_line("markers", "redis: marked tests require redis connection to run") config.addinivalue_line("markers", "mongo: marked tests require mongo connection to run") - if sys.platform == "win32": + if sys.platform == "win32" and sys.version_info < (3, 14): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) else: asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) @@ -186,6 +186,16 @@ async def dispatcher(): await dp.emit_shutdown() +@pytest.fixture() +def storage(request): + return request.getfixturevalue(request.param) + + +@pytest.fixture() +def isolation(request): + return request.getfixturevalue(request.param) + + # @pytest.fixture(scope="session") # def event_loop_policy(request): # if sys.platform == "win32": diff --git a/tests/test_fsm/storage/test_isolation.py b/tests/test_fsm/storage/test_isolation.py index 7257123d..2624ad26 100644 --- a/tests/test_fsm/storage/test_isolation.py +++ b/tests/test_fsm/storage/test_isolation.py @@ -8,11 +8,8 @@ from aiogram.fsm.storage.redis import RedisEventIsolation, RedisStorage @pytest.mark.parametrize( "isolation", - [ - pytest.lazy_fixture("redis_isolation"), - pytest.lazy_fixture("lock_isolation"), - pytest.lazy_fixture("disabled_isolation"), - ], + ["redis_isolation", "lock_isolation", "disabled_isolation"], + indirect=True, ) class TestIsolations: async def test_lock( diff --git a/tests/test_fsm/storage/test_storages.py b/tests/test_fsm/storage/test_storages.py index 884f6874..e374448b 100644 --- a/tests/test_fsm/storage/test_storages.py +++ b/tests/test_fsm/storage/test_storages.py @@ -8,12 +8,8 @@ from aiogram.fsm.storage.base import BaseStorage, StorageKey @pytest.mark.parametrize( "storage", - [ - pytest.lazy_fixture("redis_storage"), - pytest.lazy_fixture("mongo_storage"), - pytest.lazy_fixture("pymongo_storage"), - pytest.lazy_fixture("memory_storage"), - ], + ["memory_storage", "redis_storage", "mongo_storage", "pymongo_storage"], + indirect=True, ) class TestStorages: async def test_set_state(self, storage: BaseStorage, storage_key: StorageKey):