Py3.14 support

Bump .pre-commit-config.yaml
Bump `mongo` feature deps
Bump `proxy` feature dep
Bump `test` feature deps
Bump `dev` feature deps

Set `aiohttp` max version `<3.14`

Fix `test_isolation.py` tests
Fix `test_storages.py` tests

Add Py version limit `<3.15` (breaking changes possible)
Add new `uvloop` starter to `Dispatcher.run_polling`

Remove old `uvloop` `set_event_loop_policy`
Remove `pytest-lazy-fixture`
This commit is contained in:
andrew000 2025-10-09 21:26:13 +03:00
parent 0c6a705310
commit 7c6d78cf30
7 changed files with 58 additions and 51 deletions

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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",
]

View file

@ -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":

View file

@ -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(

View file

@ -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):