2020-05-26 00:23:35 +03:00
|
|
|
import asyncio
|
2019-11-23 19:54:04 +02:00
|
|
|
import inspect
|
2026-01-01 23:42:40 +02:00
|
|
|
import sys
|
2022-10-02 00:04:31 +03:00
|
|
|
import warnings
|
2025-10-06 19:19:23 +03:00
|
|
|
from collections.abc import Callable
|
2019-11-23 19:54:04 +02:00
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
from functools import partial
|
2025-10-06 19:19:23 +03:00
|
|
|
from typing import Any
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2022-10-02 00:04:31 +03:00
|
|
|
from magic_filter.magic import MagicFilter as OriginalMagicFilter
|
2021-05-11 23:04:32 +03:00
|
|
|
|
2022-08-14 01:07:52 +03:00
|
|
|
from aiogram.dispatcher.flags import extract_flags_from_object
|
2022-10-02 00:04:31 +03:00
|
|
|
from aiogram.filters.base import Filter
|
2022-08-14 01:07:52 +03:00
|
|
|
from aiogram.handlers import BaseHandler
|
2022-10-02 00:04:31 +03:00
|
|
|
from aiogram.utils.magic_filter import MagicFilter
|
|
|
|
|
from aiogram.utils.warnings import Recommendation
|
2019-11-24 00:16:25 +02:00
|
|
|
|
2022-04-24 04:19:19 +03:00
|
|
|
CallbackType = Callable[..., Any]
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2026-01-01 23:42:40 +02:00
|
|
|
_ACCEPTED_PARAM_KINDS = {
|
|
|
|
|
inspect.Parameter.POSITIONAL_ONLY,
|
|
|
|
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
|
|
|
inspect.Parameter.KEYWORD_ONLY,
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-23 19:54:04 +02:00
|
|
|
|
|
|
|
|
@dataclass
|
PoC Scenes (#1280)
* Base implementation
* Small refactoring + added possibility to specify post-action on handlers
* Move scene properties to config object
* Revise aiogram/scenes with wizard-based design pattern
Modified files in aiogram/scenes to incorporate the Wizard design pattern. Files affected are _marker.py, _registry.py, _wizard.py and __init__.py. The changes introduced a SceneWizard Class and ScenesManager, both of which aid in controlling navigation between different scenes or states. This helps clarifying the codebase, streamline scene transitions and offer more control over the app flow.
* Added example
* Small optimizations
* Replace ValueError with SceneException in scenes. Added error safety in scene resolver.
* str
* Added possibility to reset context on scene entered and to handle callback query in any state
* Remove inline markup in example
* Small changes
* Docs + example
* Small refactoring
* Remove scene inclusion methods from router
The methods for including scenes as sub-routers have been removed from the router.py file. Instead, the SceneRegistry class is now set to register scenes by default upon initializing. This streamlines the scene management process by removing redundant routers and making registration automatic.
* Init tests
* Small fix in tests
* Add support for State instance in the scene
The aiogram FSM scene now allows the use of State instance as an argument, enabling more customization. Modified the 'as_handler' method to receive **kwargs arguments, allowing passing of attributes to the handler. An additional type check has been also added to ensure the 'scene' is either a subclass of Scene or a string.
* Fixed test
* Expand test coverage for test_fsm module
The commit enhances tests for the test_fsm module to improve code reliability. It includes additional unit tests for the ObserverDecorator and ActionContainer classes and introduces new tests for the SceneHandlerWrapper class. This ensures the correct functionality of the decorator methods, the action container execution, and the handler wrapper.
* Reformat code
* Fixed long line in the example
* Skip some tests on PyPy
* Change mock return_value
* Compatibility...
* Compatibility...
* Compatibility...
* Added base changes description
* Scenes Tests (#1369)
* ADD tests for `SceneRegistry`
* ADD tests for `ScenesManager`
* ADD Changelog
* Revert "ADD Changelog"
This reverts commit 6dd93012524f92d187443e3af8e90264a4499b5b.
* Remove `@pytest.mark.asyncio`, Reformat code
* Scenes Tests. Part 2 (#1371)
* ADD tests for `SceneWizard`
* ADD tests for `Scene`
* Refactor ObserverDecorator to use on.message syntax in test_scene.py
Cover `Scene::__init_subclass__::if isinstance(value, ObserverDecorator):`
* Refactor `HistoryManager` in `aiogram/fsm/scene.py`
Removed condition that checked if 'history' is empty before calling 'update_data' in 'Scene'.
* ADD tests for `HistoryManager`
* Small changes in the documentation
* Small changes in the documentation
* Small changes in the documentation
---------
Co-authored-by: Andrew <11490628+andrew000@users.noreply.github.com>
2023-11-23 00:41:21 +02:00
|
|
|
class CallableObject:
|
2022-04-24 04:19:19 +03:00
|
|
|
callback: CallbackType
|
2019-11-23 19:54:04 +02:00
|
|
|
awaitable: bool = field(init=False)
|
2025-10-06 19:19:23 +03:00
|
|
|
params: set[str] = field(init=False)
|
2023-11-13 21:04:58 +02:00
|
|
|
varkw: bool = field(init=False)
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2020-03-18 17:04:11 +03:00
|
|
|
def __post_init__(self) -> None:
|
2020-04-28 12:07:01 +03:00
|
|
|
callback = inspect.unwrap(self.callback)
|
2019-11-23 19:54:04 +02:00
|
|
|
self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction(callback)
|
2026-01-01 23:42:40 +02:00
|
|
|
|
|
|
|
|
kwargs: dict[str, Any] = {}
|
|
|
|
|
if sys.version_info >= (3, 14):
|
|
|
|
|
import annotationlib
|
|
|
|
|
|
|
|
|
|
kwargs["annotation_format"] = annotationlib.Format.FORWARDREF
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
signature = inspect.signature(callback, **kwargs)
|
|
|
|
|
except (ValueError, TypeError): # pragma: no cover
|
|
|
|
|
self.params = set()
|
|
|
|
|
self.varkw = False
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
params: set[str] = set()
|
|
|
|
|
varkw: bool = False
|
|
|
|
|
|
|
|
|
|
for p in signature.parameters.values():
|
|
|
|
|
if p.kind in _ACCEPTED_PARAM_KINDS:
|
|
|
|
|
params.add(p.name)
|
|
|
|
|
elif p.kind == inspect.Parameter.VAR_KEYWORD:
|
|
|
|
|
varkw = True
|
|
|
|
|
self.params = params
|
|
|
|
|
self.varkw = varkw
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2025-10-06 19:19:23 +03:00
|
|
|
def _prepare_kwargs(self, kwargs: dict[str, Any]) -> dict[str, Any]:
|
2023-11-13 21:04:58 +02:00
|
|
|
if self.varkw:
|
2019-11-23 19:54:04 +02:00
|
|
|
return kwargs
|
|
|
|
|
|
2023-11-13 21:04:58 +02:00
|
|
|
return {k: kwargs[k] for k in self.params if k in kwargs}
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2020-03-18 17:04:11 +03:00
|
|
|
async def call(self, *args: Any, **kwargs: Any) -> Any:
|
2020-03-25 15:35:32 +03:00
|
|
|
wrapped = partial(self.callback, *args, **self._prepare_kwargs(kwargs))
|
2019-11-23 19:54:04 +02:00
|
|
|
if self.awaitable:
|
|
|
|
|
return await wrapped()
|
2025-04-02 23:26:43 +03:00
|
|
|
return await asyncio.to_thread(wrapped)
|
2019-11-23 19:54:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
PoC Scenes (#1280)
* Base implementation
* Small refactoring + added possibility to specify post-action on handlers
* Move scene properties to config object
* Revise aiogram/scenes with wizard-based design pattern
Modified files in aiogram/scenes to incorporate the Wizard design pattern. Files affected are _marker.py, _registry.py, _wizard.py and __init__.py. The changes introduced a SceneWizard Class and ScenesManager, both of which aid in controlling navigation between different scenes or states. This helps clarifying the codebase, streamline scene transitions and offer more control over the app flow.
* Added example
* Small optimizations
* Replace ValueError with SceneException in scenes. Added error safety in scene resolver.
* str
* Added possibility to reset context on scene entered and to handle callback query in any state
* Remove inline markup in example
* Small changes
* Docs + example
* Small refactoring
* Remove scene inclusion methods from router
The methods for including scenes as sub-routers have been removed from the router.py file. Instead, the SceneRegistry class is now set to register scenes by default upon initializing. This streamlines the scene management process by removing redundant routers and making registration automatic.
* Init tests
* Small fix in tests
* Add support for State instance in the scene
The aiogram FSM scene now allows the use of State instance as an argument, enabling more customization. Modified the 'as_handler' method to receive **kwargs arguments, allowing passing of attributes to the handler. An additional type check has been also added to ensure the 'scene' is either a subclass of Scene or a string.
* Fixed test
* Expand test coverage for test_fsm module
The commit enhances tests for the test_fsm module to improve code reliability. It includes additional unit tests for the ObserverDecorator and ActionContainer classes and introduces new tests for the SceneHandlerWrapper class. This ensures the correct functionality of the decorator methods, the action container execution, and the handler wrapper.
* Reformat code
* Fixed long line in the example
* Skip some tests on PyPy
* Change mock return_value
* Compatibility...
* Compatibility...
* Compatibility...
* Added base changes description
* Scenes Tests (#1369)
* ADD tests for `SceneRegistry`
* ADD tests for `ScenesManager`
* ADD Changelog
* Revert "ADD Changelog"
This reverts commit 6dd93012524f92d187443e3af8e90264a4499b5b.
* Remove `@pytest.mark.asyncio`, Reformat code
* Scenes Tests. Part 2 (#1371)
* ADD tests for `SceneWizard`
* ADD tests for `Scene`
* Refactor ObserverDecorator to use on.message syntax in test_scene.py
Cover `Scene::__init_subclass__::if isinstance(value, ObserverDecorator):`
* Refactor `HistoryManager` in `aiogram/fsm/scene.py`
Removed condition that checked if 'history' is empty before calling 'update_data' in 'Scene'.
* ADD tests for `HistoryManager`
* Small changes in the documentation
* Small changes in the documentation
* Small changes in the documentation
---------
Co-authored-by: Andrew <11490628+andrew000@users.noreply.github.com>
2023-11-23 00:41:21 +02:00
|
|
|
class FilterObject(CallableObject):
|
2025-10-06 19:19:23 +03:00
|
|
|
magic: MagicFilter | None = None
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2021-05-11 23:04:32 +03:00
|
|
|
def __post_init__(self) -> None:
|
2022-10-02 00:04:31 +03:00
|
|
|
if isinstance(self.callback, OriginalMagicFilter):
|
|
|
|
|
# MagicFilter instance is callable but generates
|
|
|
|
|
# only "CallOperation" instead of applying the filter
|
|
|
|
|
self.magic = self.callback
|
2021-05-11 23:04:32 +03:00
|
|
|
self.callback = self.callback.resolve
|
2022-10-02 00:04:31 +03:00
|
|
|
if not isinstance(self.magic, MagicFilter):
|
|
|
|
|
# Issue: https://github.com/aiogram/aiogram/issues/990
|
|
|
|
|
warnings.warn(
|
|
|
|
|
category=Recommendation,
|
|
|
|
|
message="You are using F provided by magic_filter package directly, "
|
|
|
|
|
"but it lacks `.as_()` extension."
|
|
|
|
|
"\n Please change the import statement: from `from magic_filter import F` "
|
|
|
|
|
"to `from aiogram import F` to silence this warning.",
|
|
|
|
|
stacklevel=6,
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-06 19:19:23 +03:00
|
|
|
super().__post_init__()
|
2022-10-02 00:04:31 +03:00
|
|
|
|
|
|
|
|
if isinstance(self.callback, Filter):
|
|
|
|
|
self.awaitable = True
|
2021-05-11 23:04:32 +03:00
|
|
|
|
2019-11-23 19:54:04 +02:00
|
|
|
|
|
|
|
|
@dataclass
|
PoC Scenes (#1280)
* Base implementation
* Small refactoring + added possibility to specify post-action on handlers
* Move scene properties to config object
* Revise aiogram/scenes with wizard-based design pattern
Modified files in aiogram/scenes to incorporate the Wizard design pattern. Files affected are _marker.py, _registry.py, _wizard.py and __init__.py. The changes introduced a SceneWizard Class and ScenesManager, both of which aid in controlling navigation between different scenes or states. This helps clarifying the codebase, streamline scene transitions and offer more control over the app flow.
* Added example
* Small optimizations
* Replace ValueError with SceneException in scenes. Added error safety in scene resolver.
* str
* Added possibility to reset context on scene entered and to handle callback query in any state
* Remove inline markup in example
* Small changes
* Docs + example
* Small refactoring
* Remove scene inclusion methods from router
The methods for including scenes as sub-routers have been removed from the router.py file. Instead, the SceneRegistry class is now set to register scenes by default upon initializing. This streamlines the scene management process by removing redundant routers and making registration automatic.
* Init tests
* Small fix in tests
* Add support for State instance in the scene
The aiogram FSM scene now allows the use of State instance as an argument, enabling more customization. Modified the 'as_handler' method to receive **kwargs arguments, allowing passing of attributes to the handler. An additional type check has been also added to ensure the 'scene' is either a subclass of Scene or a string.
* Fixed test
* Expand test coverage for test_fsm module
The commit enhances tests for the test_fsm module to improve code reliability. It includes additional unit tests for the ObserverDecorator and ActionContainer classes and introduces new tests for the SceneHandlerWrapper class. This ensures the correct functionality of the decorator methods, the action container execution, and the handler wrapper.
* Reformat code
* Fixed long line in the example
* Skip some tests on PyPy
* Change mock return_value
* Compatibility...
* Compatibility...
* Compatibility...
* Added base changes description
* Scenes Tests (#1369)
* ADD tests for `SceneRegistry`
* ADD tests for `ScenesManager`
* ADD Changelog
* Revert "ADD Changelog"
This reverts commit 6dd93012524f92d187443e3af8e90264a4499b5b.
* Remove `@pytest.mark.asyncio`, Reformat code
* Scenes Tests. Part 2 (#1371)
* ADD tests for `SceneWizard`
* ADD tests for `Scene`
* Refactor ObserverDecorator to use on.message syntax in test_scene.py
Cover `Scene::__init_subclass__::if isinstance(value, ObserverDecorator):`
* Refactor `HistoryManager` in `aiogram/fsm/scene.py`
Removed condition that checked if 'history' is empty before calling 'update_data' in 'Scene'.
* ADD tests for `HistoryManager`
* Small changes in the documentation
* Small changes in the documentation
* Small changes in the documentation
---------
Co-authored-by: Andrew <11490628+andrew000@users.noreply.github.com>
2023-11-23 00:41:21 +02:00
|
|
|
class HandlerObject(CallableObject):
|
2025-10-06 19:19:23 +03:00
|
|
|
filters: list[FilterObject] | None = None
|
|
|
|
|
flags: dict[str, Any] = field(default_factory=dict)
|
2019-11-23 19:54:04 +02:00
|
|
|
|
2020-03-18 17:04:11 +03:00
|
|
|
def __post_init__(self) -> None:
|
2025-10-06 19:19:23 +03:00
|
|
|
super().__post_init__()
|
2020-10-04 20:56:34 +05:30
|
|
|
callback = inspect.unwrap(self.callback)
|
|
|
|
|
if inspect.isclass(callback) and issubclass(callback, BaseHandler):
|
2019-12-03 00:03:15 +02:00
|
|
|
self.awaitable = True
|
2022-02-19 01:45:59 +02:00
|
|
|
self.flags.update(extract_flags_from_object(callback))
|
2019-12-03 00:03:15 +02:00
|
|
|
|
2025-10-06 19:19:23 +03:00
|
|
|
async def check(self, *args: Any, **kwargs: Any) -> tuple[bool, dict[str, Any]]:
|
2020-01-13 21:17:28 +02:00
|
|
|
if not self.filters:
|
2020-05-26 00:23:35 +03:00
|
|
|
return True, kwargs
|
2019-11-23 19:54:04 +02:00
|
|
|
for event_filter in self.filters:
|
|
|
|
|
check = await event_filter.call(*args, **kwargs)
|
|
|
|
|
if not check:
|
2020-05-26 00:23:35 +03:00
|
|
|
return False, kwargs
|
2019-11-23 19:54:04 +02:00
|
|
|
if isinstance(check, dict):
|
|
|
|
|
kwargs.update(check)
|
|
|
|
|
return True, kwargs
|