Repository: microsoft/botbuilder-python

Issue:
DialogContext.cancel_all_dialogs(True) does not propagate cancellation through parent dialogs
## Version
4.13.0

## Describe the bug

Within a child dialog we should be able to `cancel_all_dialogs` by setting the first parameter (`cancel_parents`) to `True` and that propagates up through the parents thus cancelling the conversation.

However due to the following statement in [dialog_context.py#L224](
https://github.com/microsoft/botbuilder-python/blob/885d3d11304aa3fedefee400378721c2c3e423bd/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_context.py#L224) this does not happen and instead the parent dialog's next step is run.

```
if cancel_parents is None:
```

## To Reproduce
Steps to reproduce the behavior:
1. In parent dialog MainDialog call `begin_dialog('ChildDialog')`
2. In ChildDialog waterfall step call `step.cancel_all_dialogs(True)`

## Expected behavior
All dialogs/conversation are ended

## Additional Notes

[botbuilder-js dialogContext.cancelAllDialogs](https://github.com/microsoft/botbuilder-js/blob/d57152ab47e0b02cef23f629775ba9f77d930b0f/libraries/botbuilder-dialogs/src/dialogContext.ts#L285)



Discussion:
@axelsrz could you take a look
Hello @cas-- , thanks for reporting this issue, we will schedule a fix for our next release. 

Resolving patch:
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_context.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_context.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_context.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_context.py
@@ -219,50 +219,38 @@ async def cancel_all_dialogs(
         :param event_value:
         :return:
         """
-        # pylint: disable=too-many-nested-blocks
         try:
-            if cancel_parents is None:
-                event_name = event_name or DialogEvents.cancel_dialog
-
-                if self.stack or self.parent:
-                    # Cancel all local and parent dialogs while checking for interception
-                    notify = False
-                    dialog_context = self
-
-                    while dialog_context:
-                        if dialog_context.stack:
-                            # Check to see if the dialog wants to handle the event
-                            if notify:
-                                event_handled = await dialog_context.emit_event(
-                                    event_name,
-                                    event_value,
-                                    bubble=False,
-                                    from_leaf=False,
-                                )
-
-                                if event_handled:
-                                    break
-
-                            # End the active dialog
-                            await dialog_context.end_active_dialog(
-                                DialogReason.CancelCalled
-                            )
-                        else:
-                            dialog_context = (
-                                dialog_context.parent if cancel_parents else None
+            event_name = event_name or DialogEvents.cancel_dialog
+            if self.stack or self.parent:
+                # Cancel all local and parent dialogs while checking for interception
+                notify = False
+                dialog_context = self
+
+                while dialog_context:
+                    if dialog_context.stack:
+                        # Check to see if the dialog wants to handle the event
+                        if notify:
+                            event_handled = await dialog_context.emit_event(
+                                event_name, event_value, bubble=False, from_leaf=False,
                             )
 
-                        notify = True
+                            if event_handled:
+                                break
+
+                        # End the active dialog
+                        await dialog_context.end_active_dialog(
+                            DialogReason.CancelCalled
+                        )
+                    else:
+                        dialog_context = (
+                            dialog_context.parent if cancel_parents else None
+                        )
 
-                    return DialogTurnResult(DialogTurnStatus.Cancelled)
-                # Stack was empty and no parent
-                return DialogTurnResult(DialogTurnStatus.Empty)
+                    notify = True
 
-            if self.stack:
-                while self.stack:
-                    await self.end_active_dialog(DialogReason.CancelCalled)
                 return DialogTurnResult(DialogTurnStatus.Cancelled)
 
+            # Stack was empty and no parent
             return DialogTurnResult(DialogTurnStatus.Empty)
         except Exception as err:
             self.__set_exception_context_data(err)


Test patch: