Repository navigation
FG Manager #524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
FG Manager #524
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d10be52
FG Manager
Tishka17 545a0d4
Split 2 notify modes
Tishka17 e32e40d
Fix compatibility on bg ops
Tishka17 07c6a05
Reformat
Tishka17 52e0943
add tests
Tishka17 9daf2b1
optional data in update, add doc
Tishka17 c34da3b
Fix get running loop
Tishka17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| Updating from another chat or background | ||
| ********************************************************************* | ||
|
|
||
| There are several cases when you do not have proper ``dialog_manager`` instance: | ||
|
|
||
| * background tasks | ||
| * http/broker handlers | ||
| * handlers for another user/chat | ||
|
|
||
| In all these cases you should use so called "background manager" which can be obtained from ``BgManagerFactory`` or another ``dialog_manager`` instance using ``.bg()`` method. | ||
| Note, that you cannot use normal ``dialog_manager`` outside of aiogram dialog handler. | ||
|
|
||
| Using BgManager | ||
| ------------------------------------------------ | ||
|
|
||
| Background manager can be used to asynchronously update dialog stack. All these actions are handled in background and you have not access to current state or dialog data. | ||
|
|
||
| .. code-block:: python | ||
| await bg_manager.start(state) | ||
| await bg_manager.done() | ||
| await bg_manager.switch_to(state) | ||
| await bg_manager.update(data) | ||
| Or you can enter a context to use normal dialog manager accessing all necessary data: | ||
|
|
||
| .. code-block:: python | ||
| with bg_manager.fg() as dialog_manager: | ||
| dialog_manager.dialog_data["key"] = value | ||
| dialog_manager.update() | ||
| Obtaining BgManagerFactory | ||
| ------------------------------------------------ | ||
|
|
||
| In aiogram handlers: | ||
|
|
||
| .. code-block:: python | ||
| async def handler(update: Any, dialog_bg_factory: BgManagerFactory) -> None: ... | ||
| In middleware. Works if the middleware is registered after setup_dialogs. In 99% of cases, you won’t need this. | ||
|
|
||
| .. code-block:: python | ||
| async def __call__(self, handler, event, data): | ||
| dialog_bg_factory = data[BG_FACTORY_KEY] | ||
| As a result of the setup_dialogs function: | ||
|
|
||
| .. code-block:: python | ||
| dialog_bg_factory = setup_dialogs(dispatcher) | ||
| In aiogram dialog handlers: | ||
|
|
||
| .. code-block:: python | ||
| async def handler(update: Any, widget: Widget, manager: DialogManager) -> None: | ||
| dialog_bg_factory = manager.middleware_data["dialog_bg_factory"] | ||
| Getting a BgManager | ||
| ------------------------------------------------ | ||
|
|
||
| Using ``DialogManager`` (another background manager will work as well): | ||
|
|
||
| .. code-block:: python | ||
| bg_manager = dialog_manager.bg( | ||
| user_id=other_user_id, | ||
| chat_id=other_chat_id, | ||
| ) | ||
| Using ``BgManagerFactory`` | ||
|
|
||
| .. code-block:: python | ||
| bg_manager = dialog_manager.bg( | ||
| bot, | ||
| user_id=other_user_id, | ||
| chat_id=other_chat_id, | ||
| ) | ||
| Dealing with middlewares | ||
| ------------------------------------------------ | ||
|
|
||
| Background manager sends custom events using aiogram dispatcher, so you have to your middlewares won't be triggered by default. To fix it register you middleware for dialog events: | ||
|
|
||
| .. code-block:: python | ||
| from aiogram_dialog import DIALOG_EVENT_NAME | ||
| dispatcher.observers[DIALOG_EVENT_NAME].middleware(...) | ||
| Classes and signatures | ||
| ------------------------------------------------ | ||
|
|
||
| .. autoclass:: aiogram_dialog.api.protocols.BaseDialogManager | ||
| :members: done, start, switch_to, update, bg, fg | ||
|
|
||
| .. autoclass:: aiogram_dialog.api.protocols.BgManagerFactory | ||
| :members: bg |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| DialogUpdateEvent, | ||
| ShowMode, | ||
| ) | ||
| from aiogram_dialog.api.entities.update_event import DialogFgEvent | ||
| from aiogram_dialog.api.protocols import DialogManager | ||
|
|
||
| logger = getLogger(__name__) | ||
|
|
@@ -27,6 +28,9 @@ async def handle_update( | |
| elif isinstance(event, DialogSwitchEvent): | ||
| await dialog_manager.switch_to(state=event.new_state) | ||
| await dialog_manager.show() | ||
| elif isinstance(event, DialogFgEvent): | ||
| event.entered.set_result(dialog_manager) | ||
| await event.exited | ||
|
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard foreground handshake futures before setting results. At Line 32, 💡 Proposed fix elif isinstance(event, DialogFgEvent):
- event.entered.set_result(dialog_manager)
+ if event.entered.cancelled():
+ return
+ if not event.entered.done():
+ event.entered.set_result(dialog_manager)
await event.exited🤖 Prompt for AI Agents |
||
| elif event.action is DialogAction.UPDATE: | ||
| if not dialog_manager.has_context(): | ||
| logger.warning("No context found") | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 1450
🌐 Web query:
Python asyncio.get_event_loop deprecation asyncio.get_running_loop💡 Result:
asyncio.get_event_loop is deprecated in scenarios where no current event loop is set, with changes escalating across Python versions. In Python 3.10-3.11, it emitted DeprecationWarning when no running loop existed. Since Python 3.12, it warns when no running or current loop is set. In Python 3.14 (current as of 2026-03-21), it raises RuntimeError if no current event loop exists. The asyncio policy system, which supported implicit loop creation, is deprecated since 3.14 and slated for removal in 3.16. The recommended replacement is asyncio.get_running_loop, which returns the running event loop in the current thread but raises RuntimeError if no loop is running. It can only be called from coroutines or callbacks and has simpler, more predictable behavior. Key differences: - get_event_loop: Complex logic; falls back to policy if no running loop; now errors without set loop. - get_running_loop: Strictly returns running loop or errors; preferred in async contexts. For code needing a loop outside running contexts (e.g., synchronous code), use asyncio.new_event_loop explicitly, set it with asyncio.set_event_loop, and manage its lifecycle (run_until_complete, shutdown, close). Prefer high-level asyncio.run for top-level entry points. Example migration in async context: # Old (deprecated) loop = asyncio.get_event_loop # New loop = asyncio.get_running_loop Outside async: # Old (now errors in 3.14) loop = asyncio.get_event_loop # New loop = asyncio.new_event_loop asyncio.set_event_loop(loop) try: loop.run_until_complete(coro) finally: loop.run_until_complete(loop.shutdown_asyncgens) loop.close Or better, use asyncio.run(coro).
Citations:
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 1019
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 1657
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 3365
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 403
🏁 Script executed:
Repository: Tishka17/aiogram_dialog
Length of output: 966
Critical: Future references are lost during event serialization, causing indefinite hang.
When
event.as_(bot)is called on line 273, the event is serialized and reconstructed. TheDialogFgEventobject passed tonotify_taskis no longer the same instance with the liveenteredandexitedFuture objects created on lines 268-269. When the update handler receives this reconstructed event and attemptsevent.entered.set_result(dialog_manager)(update_handler.py:32), it sets the result on a different Future instance than the one being awaited on line 276, causing an indefinite hang.Additionally:
AsyncIterator[DialogManager], notIterator[DialogManager](line 264).asyncio.get_event_loop()is deprecated in Python 3.10+ and raisesRuntimeErrorin Python 3.14+. Useasyncio.get_running_loop()instead (lines 268-269).notify_taskfails or is cancelled beforeevent.enteredis set,await event.enteredwill hang indefinitely with no timeout or cancellation mechanism.🤖 Prompt for AI Agents