Spaces:
Build error
Build error
Validify-testbot-1
/
botbuilder-python
/libraries
/botbuilder-dialogs
/botbuilder
/dialogs
/component_dialog.py
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. | |
from botbuilder.core import TurnContext | |
from .dialog import Dialog | |
from .dialog_context import DialogContext | |
from .dialog_turn_result import DialogTurnResult | |
from .dialog_state import DialogState | |
from .dialog_turn_status import DialogTurnStatus | |
from .dialog_reason import DialogReason | |
from .dialog_set import DialogSet | |
from .dialog_instance import DialogInstance | |
class ComponentDialog(Dialog): | |
""" | |
A :class:`botbuilder.dialogs.Dialog` that is composed of other dialogs | |
:var persisted_dialog state: | |
:vartype persisted_dialog_state: str | |
""" | |
persisted_dialog_state = "dialogs" | |
def __init__(self, dialog_id: str): | |
""" | |
Initializes a new instance of the :class:`ComponentDialog` | |
:param dialog_id: The ID to assign to the new dialog within the parent dialog set. | |
:type dialog_id: str | |
""" | |
super(ComponentDialog, self).__init__(dialog_id) | |
if dialog_id is None: | |
raise TypeError("ComponentDialog(): dialog_id cannot be None.") | |
self._dialogs = DialogSet() | |
self.initial_dialog_id = None | |
# TODO: Add TelemetryClient | |
async def begin_dialog( | |
self, dialog_context: DialogContext, options: object = None | |
) -> DialogTurnResult: | |
""" | |
Called when the dialog is started and pushed onto the parent's dialog stack. | |
If the task is successful, the result indicates whether the dialog is still | |
active after the turn has been processed by the dialog. | |
:param dialog_context: The :class:`botbuilder.dialogs.DialogContext` for the current turn of the conversation. | |
:type dialog_context: :class:`botbuilder.dialogs.DialogContext` | |
:param options: Optional, initial information to pass to the dialog. | |
:type options: object | |
:return: Signals the end of the turn | |
:rtype: :class:`botbuilder.dialogs.Dialog.end_of_turn` | |
""" | |
if dialog_context is None: | |
raise TypeError("ComponentDialog.begin_dialog(): outer_dc cannot be None.") | |
# Start the inner dialog. | |
dialog_state = DialogState() | |
dialog_context.active_dialog.state[self.persisted_dialog_state] = dialog_state | |
inner_dc = DialogContext(self._dialogs, dialog_context.context, dialog_state) | |
inner_dc.parent = dialog_context | |
turn_result = await self.on_begin_dialog(inner_dc, options) | |
# Check for end of inner dialog | |
if turn_result.status != DialogTurnStatus.Waiting: | |
# Return result to calling dialog | |
return await self.end_component(dialog_context, turn_result.result) | |
# Just signal waiting | |
return Dialog.end_of_turn | |
async def continue_dialog(self, dialog_context: DialogContext) -> DialogTurnResult: | |
""" | |
Called when the dialog is continued, where it is the active dialog and the | |
user replies with a new activity. | |
.. remarks:: | |
If the task is successful, the result indicates whether the dialog is still | |
active after the turn has been processed by the dialog. The result may also | |
contain a return value. | |
If this method is *not* overriden the component dialog calls the | |
:meth:`botbuilder.dialogs.DialogContext.continue_dialog` method on it's inner dialog | |
context. If the inner dialog stack is empty, the component dialog ends, | |
and if a :class:`botbuilder.dialogs.DialogTurnResult.result` is available, the component dialog | |
uses that as it's return value. | |
:param dialog_context: The parent dialog context for the current turn of the conversation. | |
:type dialog_context: :class:`botbuilder.dialogs.DialogContext` | |
:return: Signals the end of the turn | |
:rtype: :class:`botbuilder.dialogs.Dialog.end_of_turn` | |
""" | |
if dialog_context is None: | |
raise TypeError("ComponentDialog.begin_dialog(): outer_dc cannot be None.") | |
# Continue execution of inner dialog. | |
dialog_state = dialog_context.active_dialog.state[self.persisted_dialog_state] | |
inner_dc = DialogContext(self._dialogs, dialog_context.context, dialog_state) | |
inner_dc.parent = dialog_context | |
turn_result = await self.on_continue_dialog(inner_dc) | |
if turn_result.status != DialogTurnStatus.Waiting: | |
return await self.end_component(dialog_context, turn_result.result) | |
return Dialog.end_of_turn | |
async def resume_dialog( | |
self, dialog_context: DialogContext, reason: DialogReason, result: object = None | |
) -> DialogTurnResult: | |
""" | |
Called when a child dialog on the parent's dialog stack completed this turn, returning | |
control to this dialog component. | |
.. remarks:: | |
Containers are typically leaf nodes on the stack but the dev is free to push other dialogs | |
on top of the stack which will result in the container receiving an unexpected call to | |
:meth:`ComponentDialog.resume_dialog()` when the pushed on dialog ends. | |
To avoid the container prematurely ending we need to implement this method and simply | |
ask our inner dialog stack to re-prompt. | |
:param dialog_context: The dialog context for the current turn of the conversation. | |
:type dialog_context: :class:`botbuilder.dialogs.DialogContext` | |
:param reason: Reason why the dialog resumed. | |
:type reason: :class:`botbuilder.dialogs.DialogReason` | |
:param result: Optional, value returned from the dialog that was called. | |
:type result: object | |
:return: Signals the end of the turn | |
:rtype: :class:`botbuilder.dialogs.Dialog.end_of_turn` | |
""" | |
await self.reprompt_dialog(dialog_context.context, dialog_context.active_dialog) | |
return Dialog.end_of_turn | |
async def reprompt_dialog( | |
self, context: TurnContext, instance: DialogInstance | |
) -> None: | |
""" | |
Called when the dialog should re-prompt the user for input. | |
:param context: The context object for this turn. | |
:type context: :class:`botbuilder.core.TurnContext` | |
:param instance: State information for this dialog. | |
:type instance: :class:`botbuilder.dialogs.DialogInstance` | |
""" | |
# Delegate to inner dialog. | |
dialog_state = instance.state[self.persisted_dialog_state] | |
inner_dc = DialogContext(self._dialogs, context, dialog_state) | |
await inner_dc.reprompt_dialog() | |
# Notify component | |
await self.on_reprompt_dialog(context, instance) | |
async def end_dialog( | |
self, context: TurnContext, instance: DialogInstance, reason: DialogReason | |
) -> None: | |
""" | |
Called when the dialog is ending. | |
:param context: The context object for this turn. | |
:type context: :class:`botbuilder.core.TurnContext` | |
:param instance: State information associated with the instance of this component dialog. | |
:type instance: :class:`botbuilder.dialogs.DialogInstance` | |
:param reason: Reason why the dialog ended. | |
:type reason: :class:`botbuilder.dialogs.DialogReason` | |
""" | |
# Forward cancel to inner dialog | |
if reason == DialogReason.CancelCalled: | |
dialog_state = instance.state[self.persisted_dialog_state] | |
inner_dc = DialogContext(self._dialogs, context, dialog_state) | |
await inner_dc.cancel_all_dialogs() | |
await self.on_end_dialog(context, instance, reason) | |
def add_dialog(self, dialog: Dialog) -> object: | |
""" | |
Adds a :class:`Dialog` to the component dialog and returns the updated component. | |
:param dialog: The dialog to add. | |
:return: The updated :class:`ComponentDialog`. | |
:rtype: :class:`ComponentDialog` | |
""" | |
self._dialogs.add(dialog) | |
if not self.initial_dialog_id: | |
self.initial_dialog_id = dialog.id | |
return self | |
async def find_dialog(self, dialog_id: str) -> Dialog: | |
""" | |
Finds a dialog by ID. | |
:param dialog_id: The dialog to add. | |
:return: The dialog; or None if there is not a match for the ID. | |
:rtype: :class:`botbuilder.dialogs.Dialog` | |
""" | |
return await self._dialogs.find(dialog_id) | |
async def on_begin_dialog( | |
self, inner_dc: DialogContext, options: object | |
) -> DialogTurnResult: | |
""" | |
Called when the dialog is started and pushed onto the parent's dialog stack. | |
.. remarks:: | |
If the task is successful, the result indicates whether the dialog is still | |
active after the turn has been processed by the dialog. | |
By default, this calls the :meth:`botbuilder.dialogs.Dialog.begin_dialog()` | |
method of the component dialog's initial dialog. | |
Override this method in a derived class to implement interrupt logic. | |
:param inner_dc: The inner dialog context for the current turn of conversation. | |
:type inner_dc: :class:`botbuilder.dialogs.DialogContext` | |
:param options: Optional, initial information to pass to the dialog. | |
:type options: object | |
""" | |
return await inner_dc.begin_dialog(self.initial_dialog_id, options) | |
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult: | |
""" | |
Called when the dialog is continued, where it is the active dialog and the user replies with a new activity. | |
:param inner_dc: The inner dialog context for the current turn of conversation. | |
:type inner_dc: :class:`botbuilder.dialogs.DialogContext` | |
""" | |
return await inner_dc.continue_dialog() | |
async def on_end_dialog( # pylint: disable=unused-argument | |
self, context: TurnContext, instance: DialogInstance, reason: DialogReason | |
) -> None: | |
""" | |
Ends the component dialog in its parent's context. | |
:param turn_context: The :class:`botbuilder.core.TurnContext` for the current turn of the conversation. | |
:type turn_context: :class:`botbuilder.core.TurnContext` | |
:param instance: State information associated with the inner dialog stack of this component dialog. | |
:type instance: :class:`botbuilder.dialogs.DialogInstance` | |
:param reason: Reason why the dialog ended. | |
:type reason: :class:`botbuilder.dialogs.DialogReason` | |
""" | |
return | |
async def on_reprompt_dialog( # pylint: disable=unused-argument | |
self, turn_context: TurnContext, instance: DialogInstance | |
) -> None: | |
""" | |
:param turn_context: The :class:`botbuilder.core.TurnContext` for the current turn of the conversation. | |
:type turn_context: :class:`botbuilder.dialogs.DialogInstance` | |
:param instance: State information associated with the inner dialog stack of this component dialog. | |
:type instance: :class:`botbuilder.dialogs.DialogInstance` | |
""" | |
return | |
async def end_component( | |
self, outer_dc: DialogContext, result: object # pylint: disable=unused-argument | |
) -> DialogTurnResult: | |
""" | |
Ends the component dialog in its parent's context. | |
.. remarks:: | |
If the task is successful, the result indicates that the dialog ended after the | |
turn was processed by the dialog. | |
:param outer_dc: The parent dialog context for the current turn of conversation. | |
:type outer_dc: class:`botbuilder.dialogs.DialogContext` | |
:param result: Optional, value to return from the dialog component to the parent context. | |
:type result: object | |
:return: Value to return. | |
:rtype: :class:`botbuilder.dialogs.DialogTurnResult.result` | |
""" | |
return await outer_dc.end_dialog(result) | |