EOL of Py3.9 (#1726)
Some checks failed
Tests / tests (macos-latest, 3.10) (push) Has been cancelled
Tests / tests (macos-latest, 3.11) (push) Has been cancelled
Tests / tests (macos-latest, 3.12) (push) Has been cancelled
Tests / tests (macos-latest, 3.13) (push) Has been cancelled
Tests / tests (ubuntu-latest, 3.10) (push) Has been cancelled
Tests / tests (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / tests (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / tests (ubuntu-latest, 3.13) (push) Has been cancelled
Tests / tests (windows-latest, 3.10) (push) Has been cancelled
Tests / tests (windows-latest, 3.11) (push) Has been cancelled
Tests / tests (windows-latest, 3.12) (push) Has been cancelled
Tests / tests (windows-latest, 3.13) (push) Has been cancelled
Tests / pypy-tests (macos-latest, pypy3.10) (push) Has been cancelled
Tests / pypy-tests (macos-latest, pypy3.11) (push) Has been cancelled
Tests / pypy-tests (ubuntu-latest, pypy3.10) (push) Has been cancelled
Tests / pypy-tests (ubuntu-latest, pypy3.11) (push) Has been cancelled

* Drop py3.9 and pypy3.9

Add pypy3.11 (testing) into `tests.yml`

Remove py3.9 from matrix in `tests.yml`

Refactor not auto-gen code to be compatible with py3.10+, droping ugly 3.9 annotation.

Replace some `from typing` imports to `from collections.abc`, due to deprecation

Add `from __future__ import annotations` and `if TYPE_CHECKING:` where possible

Add some `noqa` to calm down Ruff in some places, if Ruff will be used as default linting+formatting tool in future

Replace some relative imports to absolute

Sort `__all__` tuples in `__init__.py` and some other `.py` files

Sort `__slots__` tuples in classes

Split raises into `msg` and `raise` (`EM101`, `EM102`) to not duplicate error message in the traceback

Add `Self` from `typing_extenstion` where possible

Resolve typing problem in `aiogram/filters/command.py:18`

Concatenate nested `if` statements

Convert `HandlerContainer` into a dataclass in `aiogram/fsm/scene.py`

Bump tests docker-compose.yml `redis:6-alpine` -> `redis:8-alpine`

Bump tests docker-compose.yml `mongo:7.0.6` -> `mongo:8.0.14`

Bump pre-commit-config `black==24.4.2` -> `black==25.9.0`

Bump pre-commit-config `ruff==0.5.1` -> `ruff==0.13.3`

Update Makefile lint for ruff to show fixes

Add `make outdated` into Makefile

Use `pathlib` instead of `os.path`

Bump `redis[hiredis]>=5.0.1,<5.3.0` -> `redis[hiredis]>=6.2.0,<7`

Bump `cryptography>=43.0.0` -> `cryptography>=46.0.0` due to security reasons

Bump `pytz~=2023.3` -> `pytz~=2025.2`

Bump `pycryptodomex~=3.19.0` -> `pycryptodomex~=3.23.0` due to security reasons

Bump linting and formatting tools

* Add `1726.removal.rst`

* Update aiogram/utils/dataclass.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update aiogram/filters/callback_data.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update 1726.removal.rst

* Remove `outdated` from Makefile

* Add `__slots__` to `HandlerContainer`

* Remove unused imports

* Add `@dataclass` with `slots=True` to `HandlerContainer`

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Andrew 2025-10-06 19:19:23 +03:00 committed by GitHub
parent ab32296d07
commit df7b16d5b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
94 changed files with 1383 additions and 1215 deletions

View file

@ -34,11 +34,11 @@ class CancellableScene(Scene):
"""
@on.message(F.text.casefold() == BUTTON_CANCEL.text.casefold(), after=After.exit())
async def handle_cancel(self, message: Message):
async def handle_cancel(self, message: Message) -> None:
await message.answer("Cancelled.", reply_markup=ReplyKeyboardRemove())
@on.message(F.text.casefold() == BUTTON_BACK.text.casefold(), after=After.back())
async def handle_back(self, message: Message):
async def handle_back(self, message: Message) -> None:
await message.answer("Back.")
@ -48,7 +48,7 @@ class LanguageScene(CancellableScene, state="language"):
"""
@on.message.enter()
async def on_enter(self, message: Message):
async def on_enter(self, message: Message) -> None:
await message.answer(
"What language do you prefer?",
reply_markup=ReplyKeyboardMarkup(
@ -58,14 +58,14 @@ class LanguageScene(CancellableScene, state="language"):
)
@on.message(F.text.casefold() == "python", after=After.exit())
async def process_python(self, message: Message):
async def process_python(self, message: Message) -> None:
await message.answer(
"Python, you say? That's the language that makes my circuits light up! 😉"
"Python, you say? That's the language that makes my circuits light up! 😉",
)
await self.input_language(message)
@on.message(after=After.exit())
async def input_language(self, message: Message):
async def input_language(self, message: Message) -> None:
data: FSMData = await self.wizard.get_data()
await self.show_results(message, language=message.text, **data)
@ -83,7 +83,7 @@ class LikeBotsScene(CancellableScene, state="like_bots"):
"""
@on.message.enter()
async def on_enter(self, message: Message):
async def on_enter(self, message: Message) -> None:
await message.answer(
"Did you like to write bots?",
reply_markup=ReplyKeyboardMarkup(
@ -96,18 +96,18 @@ class LikeBotsScene(CancellableScene, state="like_bots"):
)
@on.message(F.text.casefold() == "yes", after=After.goto(LanguageScene))
async def process_like_write_bots(self, message: Message):
async def process_like_write_bots(self, message: Message) -> None:
await message.reply("Cool! I'm too!")
@on.message(F.text.casefold() == "no", after=After.exit())
async def process_dont_like_write_bots(self, message: Message):
async def process_dont_like_write_bots(self, message: Message) -> None:
await message.answer(
"Not bad not terrible.\nSee you soon.",
reply_markup=ReplyKeyboardRemove(),
)
@on.message()
async def input_like_bots(self, message: Message):
async def input_like_bots(self, message: Message) -> None:
await message.answer("I don't understand you :(")
@ -117,25 +117,25 @@ class NameScene(CancellableScene, state="name"):
"""
@on.message.enter() # Marker for handler that should be called when a user enters the scene.
async def on_enter(self, message: Message):
async def on_enter(self, message: Message) -> None:
await message.answer(
"Hi there! What's your name?",
reply_markup=ReplyKeyboardMarkup(keyboard=[[BUTTON_CANCEL]], resize_keyboard=True),
)
@on.callback_query.enter() # different types of updates that start the scene also supported.
async def on_enter_callback(self, callback_query: CallbackQuery):
async def on_enter_callback(self, callback_query: CallbackQuery) -> None:
await callback_query.answer()
await self.on_enter(callback_query.message)
@on.message.leave() # Marker for handler that should be called when a user leaves the scene.
async def on_leave(self, message: Message):
async def on_leave(self, message: Message) -> None:
data: FSMData = await self.wizard.get_data()
name = data.get("name", "Anonymous")
await message.answer(f"Nice to meet you, {html.quote(name)}!")
@on.message(after=After.goto(LikeBotsScene))
async def input_name(self, message: Message):
async def input_name(self, message: Message) -> None:
await self.wizard.update_data(name=message.text)
@ -154,22 +154,22 @@ class DefaultScene(
start_demo = on.message(F.text.casefold() == "demo", after=After.goto(NameScene))
@on.message(Command("demo"))
async def demo(self, message: Message):
async def demo(self, message: Message) -> None:
await message.answer(
"Demo started",
reply_markup=InlineKeyboardMarkup(
inline_keyboard=[[InlineKeyboardButton(text="Go to form", callback_data="start")]]
inline_keyboard=[[InlineKeyboardButton(text="Go to form", callback_data="start")]],
),
)
@on.callback_query(F.data == "start", after=After.goto(NameScene))
async def demo_callback(self, callback_query: CallbackQuery):
async def demo_callback(self, callback_query: CallbackQuery) -> None:
await callback_query.answer(cache_time=0)
await callback_query.message.delete_reply_markup()
@on.message.enter() # Mark that this handler should be called when a user enters the scene.
@on.message()
async def default_handler(self, message: Message):
async def default_handler(self, message: Message) -> None:
await message.answer(
"Start demo?\nYou can also start demo via command /demo",
reply_markup=ReplyKeyboardMarkup(