diff --git a/CHANGES/1641.bugfix.rst b/CHANGES/1641.bugfix.rst new file mode 100644 index 00000000..eb4d2332 --- /dev/null +++ b/CHANGES/1641.bugfix.rst @@ -0,0 +1,5 @@ +Resolved incorrect ordering of registered handlers in the :class:`aiogram.fsm.scene.Scene` +object caused by :code:`inspect.getmembers` returning sorted members. +Handlers are now registered in the order of their definition within the class, +ensuring proper execution sequence, especially when handling filters with different +levels of specificity. diff --git a/aiogram/fsm/scene.py b/aiogram/fsm/scene.py index 0cbd0e8b..80bcbb47 100644 --- a/aiogram/fsm/scene.py +++ b/aiogram/fsm/scene.py @@ -323,7 +323,10 @@ class Scene: if callback_query_without_state is None: callback_query_without_state = parent_scene_config.callback_query_without_state - for name, value in inspect.getmembers(cls): + members: dict[str, Any] = {} + for base in reversed(inspect.getmro(cls)): + members.update(base.__dict__) + for name, value in members.items(): if scene_handlers := getattr(value, "__aiogram_handler__", None): handlers.extend(scene_handlers) if isinstance(value, ObserverDecorator): diff --git a/tests/test_fsm/test_scene.py b/tests/test_fsm/test_scene.py index b22ef975..24b36507 100644 --- a/tests/test_fsm/test_scene.py +++ b/tests/test_fsm/test_scene.py @@ -1680,3 +1680,31 @@ class TestSceneInheritance: assert child_2_handler.handler is _empty_handler assert child_3_handler.handler is not _empty_handler + + +def collect_handler_names(scene): + return [handler.handler.__name__ for handler in scene.__scene_config__.handlers] + + +class TestSceneHandlersOrdering: + def test_correct_ordering(self): + class Scene1(Scene): + @on.message() + async def handler1(self, message: Message) -> None: + pass + + @on.message() + async def handler2(self, message: Message) -> None: + pass + + class Scene2(Scene): + @on.message() + async def handler2(self, message: Message) -> None: + pass + + @on.message() + async def handler1(self, message: Message) -> None: + pass + + assert collect_handler_names(Scene1) == ["handler1", "handler2"] + assert collect_handler_names(Scene2) == ["handler2", "handler1"]