We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[PORT] Move TelemetryClient property to DialogContainer
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/4178

Fixes #2638 

Move TelemetryClient onto DialogContainer (implementation the same in AdaptiveDialog and ComponentDialog).


# Changed projects
* Microsoft.Bot.Builder.Dialogs.Adaptive
* Microsoft.Bot.Builder.Dialogs





--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 import inspect
4 from hashlib import sha256
5 from typing import Dict
6 
7 from botbuilder.core import TurnContext, BotAssert, StatePropertyAccessor
8 from .dialog import Dialog
9 from .dialog_state import DialogState
10 
11 
12 class DialogSet:
13     def __init__(self, dialog_state: StatePropertyAccessor = None):
14         # pylint: disable=import-outside-toplevel
15         if dialog_state is None:
16             frame = inspect.currentframe().f_back
17             try:
18                 # try to access the caller's "self"
19                 try:
20                     self_obj = frame.f_locals["self"]
21                 except KeyError:
22                     raise TypeError("DialogSet(): dialog_state cannot be None.")
23                 # Only ComponentDialog can initialize with None dialog_state
24                 from .component_dialog import ComponentDialog
25                 from .dialog_manager import DialogManager
26                 from .dialog_container import DialogContainer
27 
28                 if not isinstance(
29                     self_obj, (ComponentDialog, DialogContainer, DialogManager)
30                 ):
31                     raise TypeError("DialogSet(): dialog_state cannot be None.")
32             finally:
33                 # make sure to clean up the frame at the end to avoid ref cycles
34                 del frame
35 
36         self._dialog_state = dialog_state
37         # self.__telemetry_client = NullBotTelemetryClient.Instance;
38 
39         self._dialogs: Dict[str, Dialog] = {}
40         self._version: str = None
41 
42     def get_version(self) -> str:
43         """
44         Gets a unique string which represents the combined versions of all dialogs in this this dialogset.
45         <returns>Version will change when any of the child dialogs version changes.</returns>
46         """
47         if not self._version:
48             version = ""
49             for _, dialog in self._dialogs.items():
50                 aux_version = dialog.get_version()
51                 if aux_version:
52                     version += aux_version
53 
54             self._version = sha256(version)
55 
56         return self._version
57 
58     def add(self, dialog: Dialog):
59         """
60         Adds a new dialog to the set and returns the added dialog.
61         :param dialog: The dialog to add.
62         """
63         if dialog is None or not isinstance(dialog, Dialog):
64             raise TypeError(
65                 "DialogSet.add(): dialog cannot be None and must be a Dialog or derived class."
66             )
67 
68         if dialog.id in self._dialogs:
69             raise TypeError(
70                 "DialogSet.add(): A dialog with an id of '%s' already added."
71                 % dialog.id
72             )
73 
74         # dialog.telemetry_client = this._telemetry_client;
75         self._dialogs[dialog.id] = dialog
76 
77         return self
78 
79     async def create_context(self, turn_context: TurnContext) -> "DialogContext":
80         # This import prevents circular dependency issues
81         # pylint: disable=import-outside-toplevel
82         from .dialog_context import DialogContext
83 
84         # pylint: disable=unnecessary-lambda
85         BotAssert.context_not_none(turn_context)
86 
87         if not self._dialog_state:
88             raise RuntimeError(
89                 "DialogSet.CreateContextAsync(): DialogSet created with a null IStatePropertyAccessor."
90             )
91 
92         state: DialogState = await self._dialog_state.get(
93             turn_context, lambda: DialogState()
94         )
95 
96         return DialogContext(self, turn_context, state)
97 
98     async def find(self, dialog_id: str) -> Dialog:
99         """
100         Finds a dialog that was previously added to the set using add(dialog)
101         :param dialog_id: ID of the dialog/prompt to look up.
102         :return: The dialog if found, otherwise null.
103         """
104         if not dialog_id:
105             raise TypeError("DialogContext.find(): dialog_id cannot be None.")
106 
107         if dialog_id in self._dialogs:
108             return self._dialogs[dialog_id]
109 
110         return None
111 
112     def find_dialog(self, dialog_id: str) -> Dialog:
113         """
114         Finds a dialog that was previously added to the set using add(dialog)
115         :param dialog_id: ID of the dialog/prompt to look up.
116         :return: The dialog if found, otherwise null.
117         """
118         if not dialog_id:
119             raise TypeError("DialogContext.find(): dialog_id cannot be None.")
120 
121         if dialog_id in self._dialogs:
122             return self._dialogs[dialog_id]
123 
124         return None
125 
126     def __str__(self):
127         if self._dialogs:
128             return "dialog set empty!"
129         return " ".join(map(str, self._dialogs.keys()))
130
```

Path: `libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 
4 from abc import ABC, abstractmethod
5 
6 
7 from .dialog import Dialog
8 from .dialog_context import DialogContext
9 from .dialog_event import DialogEvent
10 from .dialog_events import DialogEvents
11 from .dialog_set import DialogSet
12 
13 
14 class DialogContainer(Dialog, ABC):
15     def __init__(self, dialog_id: str = None):
16         super().__init__(dialog_id)
17 
18         self.dialogs = DialogSet()
19 
20     @abstractmethod
21     def create_child_context(self, dialog_context: DialogContext) -> DialogContext:
22         raise NotImplementedError()
23 
24     def find_dialog(self, dialog_id: str) -> Dialog:
25         # TODO: deprecate DialogSet.find
26         return self.dialogs.find_dialog(dialog_id)
27 
28     async def on_dialog_event(
29         self, dialog_context: DialogContext, dialog_event: DialogEvent
30     ) -> bool:
31         """
32         Called when an event has been raised, using `DialogContext.emitEvent()`, by either the current dialog or a
33          dialog that the current dialog started.
34         :param dialog_context: The dialog context for the current turn of conversation.
35         :param dialog_event: The event being raised.
36         :return: True if the event is handled by the current dialog and bubbling should stop.
37         """
38         handled = await super().on_dialog_event(dialog_context, dialog_event)
39 
40         # Trace unhandled "versionChanged" events.
41         if not handled and dialog_event.name == DialogEvents.version_changed:
42 
43             trace_message = (
44                 f"Unhandled dialog event: {dialog_event.name}. Active Dialog: "
45                 f"{dialog_context.active_dialog.id}"
46             )
47 
48             await dialog_context.context.send_trace_activity(trace_message)
49 
50         return handled
51 
52     def get_internal_version(self) -> str:
53         """
54         GetInternalVersion - Returns internal version identifier for this container.
55         DialogContainers detect changes of all sub-components in the container and map that to an DialogChanged event.
56         Because they do this, DialogContainers "hide" the internal changes and just have the .id. This isolates changes
57         to the container level unless a container doesn't handle it.  To support this DialogContainers define a
58         protected virtual method GetInternalVersion() which computes if this dialog or child dialogs have changed
59         which is then examined via calls to check_for_version_change_async().
60         :return: version which represents the change of the internals of this container.
61         """
62         return self.dialogs.get_version()
63 
64     async def check_for_version_change_async(self, dialog_context: DialogContext):
65         """
66         :param dialog_context: dialog context.
67         :return: task.
68         Checks to see if a containers child dialogs have changed since the current dialog instance
69         was started.
70 
71         This should be called at the start of `beginDialog()`, `continueDialog()`, and `resumeDialog()`.
72         """
73         current = dialog_context.active_dialog.version
74         dialog_context.active_dialog.version = self.get_internal_version()
75 
76         # Check for change of previously stored hash
77         if current and current != dialog_context.active_dialog.version:
78             # Give bot an opportunity to handle the change.
79             # - If bot handles it the changeHash will have been updated as to avoid triggering the
80             #   change again.
81             await dialog_context.emit_event(
82                 DialogEvents.version_changed, self.id, True, False
83             )
84
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py
@@ -4,6 +4,7 @@
 from abc import ABC, abstractmethod
 
 
+from botbuilder.core import NullTelemetryClient, BotTelemetryClient
 from .dialog import Dialog
 from .dialog_context import DialogContext
 from .dialog_event import DialogEvent
@@ -17,6 +18,31 @@
 
         self.dialogs = DialogSet()
 
+    @property
+    def telemetry_client(self) -> BotTelemetryClient:
+        """
+        Gets the telemetry client for logging events.
+        """
+        return self._telemetry_client
+
+    @telemetry_client.setter
+    def telemetry_client(self, value: BotTelemetryClient) -> None:
+        """
+        Sets the telemetry client for all dialogs in this set.
+        """
+        if value is None:
+            self._telemetry_client = NullTelemetryClient()
+        else:
+            self._telemetry_client = value
+
+        # Care! Dialogs.TelemetryClient assignment internally assigns the
+        # TelemetryClient for each dialog which could lead to an eventual stack
+        # overflow in cyclical dialog structures.
+        # Don't set the telemetry client if the candidate instance is the same as
+        # the currently set one.
+        if self.dialogs.telemetry_client != value:
+            self.dialogs.telemetry_client = self._telemetry_client
+
     @abstractmethod
     def create_child_context(self, dialog_context: DialogContext) -> DialogContext:
         raise NotImplementedError()
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
@@ -4,7 +4,13 @@
 from hashlib import sha256
 from typing import Dict
 
-from botbuilder.core import TurnContext, BotAssert, StatePropertyAccessor
+from botbuilder.core import (
+    NullTelemetryClient,
+    BotTelemetryClient,
+    TurnContext,
+    BotAssert,
+    StatePropertyAccessor,
+)
 from .dialog import Dialog
 from .dialog_state import DialogState
 
@@ -34,11 +40,31 @@
                 del frame
 
         self._dialog_state = dialog_state
-        # self.__telemetry_client = NullBotTelemetryClient.Instance;
+        self.__telemetry_client = NullTelemetryClient()
 
         self._dialogs: Dict[str, Dialog] = {}
         self._version: str = None
 
+    @property
+    def telemetry_client(self) -> BotTelemetryClient:
+        """
+        Gets the telemetry client for logging events.
+        """
+        return self.__telemetry_client
+
+    @telemetry_client.setter
+    def telemetry_client(self, value: BotTelemetryClient) -> None:
+        """
+        Sets the telemetry client for all dialogs in this set.
+        """
+        if value is None:
+            self.__telemetry_client = NullTelemetryClient()
+        else:
+            self.__telemetry_client = value
+
+        for dialog in self._dialogs.values():
+            dialog.telemetry_client = self.__telemetry_client
+
     def get_version(self) -> str:
         """
         Gets a unique string which represents the combined versions of all dialogs in this this dialogset.

{"golden_diff": "diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py\n--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py\n+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py\n@@ -4,6 +4,7 @@\n from abc import ABC, abstractmethod\n \n \n+from botbuilder.core import NullTelemetryClient, BotTelemetryClient\n from .dialog import Dialog\n from .dialog_context import DialogContext\n from .dialog_event import DialogEvent\n@@ -17,6 +18,31 @@\n \n         self.dialogs = DialogSet()\n \n+    @property\n+    def telemetry_client(self) -> BotTelemetryClient:\n+        \"\"\"\n+        Gets the telemetry client for logging events.\n+        \"\"\"\n+        return self._telemetry_client\n+\n+    @telemetry_client.setter\n+    def telemetry_client(self, value: BotTelemetryClient) -> None:\n+        \"\"\"\n+        Sets the telemetry client for all dialogs in this set.\n+        \"\"\"\n+        if value is None:\n+            self._telemetry_client = NullTelemetryClient()\n+        else:\n+            self._telemetry_client = value\n+\n+        # Care! Dialogs.TelemetryClient assignment internally assigns the\n+        # TelemetryClient for each dialog which could lead to an eventual stack\n+        # overflow in cyclical dialog structures.\n+        # Don't set the telemetry client if the candidate instance is the same as\n+        # the currently set one.\n+        if self.dialogs.telemetry_client != value:\n+            self.dialogs.telemetry_client = self._telemetry_client\n+\n     @abstractmethod\n     def create_child_context(self, dialog_context: DialogContext) -> DialogContext:\n         raise NotImplementedError()\ndiff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py\n--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py\n+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py\n@@ -4,7 +4,13 @@\n from hashlib import sha256\n from typing import Dict\n \n-from botbuilder.core import TurnContext, BotAssert, StatePropertyAccessor\n+from botbuilder.core import (\n+    NullTelemetryClient,\n+    BotTelemetryClient,\n+    TurnContext,\n+    BotAssert,\n+    StatePropertyAccessor,\n+)\n from .dialog import Dialog\n from .dialog_state import DialogState\n \n@@ -34,11 +40,31 @@\n                 del frame\n \n         self._dialog_state = dialog_state\n-        # self.__telemetry_client = NullBotTelemetryClient.Instance;\n+        self.__telemetry_client = NullTelemetryClient()\n \n         self._dialogs: Dict[str, Dialog] = {}\n         self._version: str = None\n \n+    @property\n+    def telemetry_client(self) -> BotTelemetryClient:\n+        \"\"\"\n+        Gets the telemetry client for logging events.\n+        \"\"\"\n+        return self.__telemetry_client\n+\n+    @telemetry_client.setter\n+    def telemetry_client(self, value: BotTelemetryClient) -> None:\n+        \"\"\"\n+        Sets the telemetry client for all dialogs in this set.\n+        \"\"\"\n+        if value is None:\n+            self.__telemetry_client = NullTelemetryClient()\n+        else:\n+            self.__telemetry_client = value\n+\n+        for dialog in self._dialogs.values():\n+            dialog.telemetry_client = self.__telemetry_client\n+\n     def get_version(self) -> str:\n         \"\"\"\n         Gets a unique string which represents the combined versions of all dialogs in this this dialogset.\n", "issue": "[PORT] Move TelemetryClient property to DialogContainer\n> Port this change from botbuilder-dotnet/master branch:\nhttps://github.com/microsoft/botbuilder-dotnet/pull/4178\n\nFixes #2638 \r\n\r\nMove TelemetryClient onto DialogContainer (implementation the same in AdaptiveDialog and ComponentDialog).\n\n\r\n# Changed projects\r\n* Microsoft.Bot.Builder.Dialogs.Adaptive\r\n* Microsoft.Bot.Builder.Dialogs\r\n\r\n\r\n\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\nimport inspect\nfrom hashlib import sha256\nfrom typing import Dict\n\nfrom botbuilder.core import TurnContext, BotAssert, StatePropertyAccessor\nfrom .dialog import Dialog\nfrom .dialog_state import DialogState\n\n\nclass DialogSet:\n    def __init__(self, dialog_state: StatePropertyAccessor = None):\n        # pylint: disable=import-outside-toplevel\n        if dialog_state is None:\n            frame = inspect.currentframe().f_back\n            try:\n                # try to access the caller's \"self\"\n                try:\n                    self_obj = frame.f_locals[\"self\"]\n                except KeyError:\n                    raise TypeError(\"DialogSet(): dialog_state cannot be None.\")\n                # Only ComponentDialog can initialize with None dialog_state\n                from .component_dialog import ComponentDialog\n                from .dialog_manager import DialogManager\n                from .dialog_container import DialogContainer\n\n                if not isinstance(\n                    self_obj, (ComponentDialog, DialogContainer, DialogManager)\n                ):\n                    raise TypeError(\"DialogSet(): dialog_state cannot be None.\")\n            finally:\n                # make sure to clean up the frame at the end to avoid ref cycles\n                del frame\n\n        self._dialog_state = dialog_state\n        # self.__telemetry_client = NullBotTelemetryClient.Instance;\n\n        self._dialogs: Dict[str, Dialog] = {}\n        self._version: str = None\n\n    def get_version(self) -> str:\n        \"\"\"\n        Gets a unique string which represents the combined versions of all dialogs in this this dialogset.\n        <returns>Version will change when any of the child dialogs version changes.</returns>\n        \"\"\"\n        if not self._version:\n            version = \"\"\n            for _, dialog in self._dialogs.items():\n                aux_version = dialog.get_version()\n                if aux_version:\n                    version += aux_version\n\n            self._version = sha256(version)\n\n        return self._version\n\n    def add(self, dialog: Dialog):\n        \"\"\"\n        Adds a new dialog to the set and returns the added dialog.\n        :param dialog: The dialog to add.\n        \"\"\"\n        if dialog is None or not isinstance(dialog, Dialog):\n            raise TypeError(\n                \"DialogSet.add(): dialog cannot be None and must be a Dialog or derived class.\"\n            )\n\n        if dialog.id in self._dialogs:\n            raise TypeError(\n                \"DialogSet.add(): A dialog with an id of '%s' already added.\"\n                % dialog.id\n            )\n\n        # dialog.telemetry_client = this._telemetry_client;\n        self._dialogs[dialog.id] = dialog\n\n        return self\n\n    async def create_context(self, turn_context: TurnContext) -> \"DialogContext\":\n        # This import prevents circular dependency issues\n        # pylint: disable=import-outside-toplevel\n        from .dialog_context import DialogContext\n\n        # pylint: disable=unnecessary-lambda\n        BotAssert.context_not_none(turn_context)\n\n        if not self._dialog_state:\n            raise RuntimeError(\n                \"DialogSet.CreateContextAsync(): DialogSet created with a null IStatePropertyAccessor.\"\n            )\n\n        state: DialogState = await self._dialog_state.get(\n            turn_context, lambda: DialogState()\n        )\n\n        return DialogContext(self, turn_context, state)\n\n    async def find(self, dialog_id: str) -> Dialog:\n        \"\"\"\n        Finds a dialog that was previously added to the set using add(dialog)\n        :param dialog_id: ID of the dialog/prompt to look up.\n        :return: The dialog if found, otherwise null.\n        \"\"\"\n        if not dialog_id:\n            raise TypeError(\"DialogContext.find(): dialog_id cannot be None.\")\n\n        if dialog_id in self._dialogs:\n            return self._dialogs[dialog_id]\n\n        return None\n\n    def find_dialog(self, dialog_id: str) -> Dialog:\n        \"\"\"\n        Finds a dialog that was previously added to the set using add(dialog)\n        :param dialog_id: ID of the dialog/prompt to look up.\n        :return: The dialog if found, otherwise null.\n        \"\"\"\n        if not dialog_id:\n            raise TypeError(\"DialogContext.find(): dialog_id cannot be None.\")\n\n        if dialog_id in self._dialogs:\n            return self._dialogs[dialog_id]\n\n        return None\n\n    def __str__(self):\n        if self._dialogs:\n            return \"dialog set empty!\"\n        return \" \".join(map(str, self._dialogs.keys()))\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py"}, {"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nfrom abc import ABC, abstractmethod\n\n\nfrom .dialog import Dialog\nfrom .dialog_context import DialogContext\nfrom .dialog_event import DialogEvent\nfrom .dialog_events import DialogEvents\nfrom .dialog_set import DialogSet\n\n\nclass DialogContainer(Dialog, ABC):\n    def __init__(self, dialog_id: str = None):\n        super().__init__(dialog_id)\n\n        self.dialogs = DialogSet()\n\n    @abstractmethod\n    def create_child_context(self, dialog_context: DialogContext) -> DialogContext:\n        raise NotImplementedError()\n\n    def find_dialog(self, dialog_id: str) -> Dialog:\n        # TODO: deprecate DialogSet.find\n        return self.dialogs.find_dialog(dialog_id)\n\n    async def on_dialog_event(\n        self, dialog_context: DialogContext, dialog_event: DialogEvent\n    ) -> bool:\n        \"\"\"\n        Called when an event has been raised, using `DialogContext.emitEvent()`, by either the current dialog or a\n         dialog that the current dialog started.\n        :param dialog_context: The dialog context for the current turn of conversation.\n        :param dialog_event: The event being raised.\n        :return: True if the event is handled by the current dialog and bubbling should stop.\n        \"\"\"\n        handled = await super().on_dialog_event(dialog_context, dialog_event)\n\n        # Trace unhandled \"versionChanged\" events.\n        if not handled and dialog_event.name == DialogEvents.version_changed:\n\n            trace_message = (\n                f\"Unhandled dialog event: {dialog_event.name}. Active Dialog: \"\n                f\"{dialog_context.active_dialog.id}\"\n            )\n\n            await dialog_context.context.send_trace_activity(trace_message)\n\n        return handled\n\n    def get_internal_version(self) -> str:\n        \"\"\"\n        GetInternalVersion - Returns internal version identifier for this container.\n        DialogContainers detect changes of all sub-components in the container and map that to an DialogChanged event.\n        Because they do this, DialogContainers \"hide\" the internal changes and just have the .id. This isolates changes\n        to the container level unless a container doesn't handle it.  To support this DialogContainers define a\n        protected virtual method GetInternalVersion() which computes if this dialog or child dialogs have changed\n        which is then examined via calls to check_for_version_change_async().\n        :return: version which represents the change of the internals of this container.\n        \"\"\"\n        return self.dialogs.get_version()\n\n    async def check_for_version_change_async(self, dialog_context: DialogContext):\n        \"\"\"\n        :param dialog_context: dialog context.\n        :return: task.\n        Checks to see if a containers child dialogs have changed since the current dialog instance\n        was started.\n\n        This should be called at the start of `beginDialog()`, `continueDialog()`, and `resumeDialog()`.\n        \"\"\"\n        current = dialog_context.active_dialog.version\n        dialog_context.active_dialog.version = self.get_internal_version()\n\n        # Check for change of previously stored hash\n        if current and current != dialog_context.active_dialog.version:\n            # Give bot an opportunity to handle the change.\n            # - If bot handles it the changeHash will have been updated as to avoid triggering the\n            #   change again.\n            await dialog_context.emit_event(\n                DialogEvents.version_changed, self.id, True, False\n            )\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\nimport inspect\nfrom hashlib import sha256\nfrom typing import Dict\n\nfrom botbuilder.core import (\n    NullTelemetryClient,\n    BotTelemetryClient,\n    TurnContext,\n    BotAssert,\n    StatePropertyAccessor,\n)\nfrom .dialog import Dialog\nfrom .dialog_state import DialogState\n\n\nclass DialogSet:\n    def __init__(self, dialog_state: StatePropertyAccessor = None):\n        # pylint: disable=import-outside-toplevel\n        if dialog_state is None:\n            frame = inspect.currentframe().f_back\n            try:\n                # try to access the caller's \"self\"\n                try:\n                    self_obj = frame.f_locals[\"self\"]\n                except KeyError:\n                    raise TypeError(\"DialogSet(): dialog_state cannot be None.\")\n                # Only ComponentDialog can initialize with None dialog_state\n                from .component_dialog import ComponentDialog\n                from .dialog_manager import DialogManager\n                from .dialog_container import DialogContainer\n\n                if not isinstance(\n                    self_obj, (ComponentDialog, DialogContainer, DialogManager)\n                ):\n                    raise TypeError(\"DialogSet(): dialog_state cannot be None.\")\n            finally:\n                # make sure to clean up the frame at the end to avoid ref cycles\n                del frame\n\n        self._dialog_state = dialog_state\n        self.__telemetry_client = NullTelemetryClient()\n\n        self._dialogs: Dict[str, Dialog] = {}\n        self._version: str = None\n\n    @property\n    def telemetry_client(self) -> BotTelemetryClient:\n        \"\"\"\n        Gets the telemetry client for logging events.\n        \"\"\"\n        return self.__telemetry_client\n\n    @telemetry_client.setter\n    def telemetry_client(self, value: BotTelemetryClient) -> None:\n        \"\"\"\n        Sets the telemetry client for all dialogs in this set.\n        \"\"\"\n        if value is None:\n            self.__telemetry_client = NullTelemetryClient()\n        else:\n            self.__telemetry_client = value\n\n        for dialog in self._dialogs.values():\n            dialog.telemetry_client = self.__telemetry_client\n\n    def get_version(self) -> str:\n        \"\"\"\n        Gets a unique string which represents the combined versions of all dialogs in this this dialogset.\n        <returns>Version will change when any of the child dialogs version changes.</returns>\n        \"\"\"\n        if not self._version:\n            version = \"\"\n            for _, dialog in self._dialogs.items():\n                aux_version = dialog.get_version()\n                if aux_version:\n                    version += aux_version\n\n            self._version = sha256(version)\n\n        return self._version\n\n    def add(self, dialog: Dialog):\n        \"\"\"\n        Adds a new dialog to the set and returns the added dialog.\n        :param dialog: The dialog to add.\n        \"\"\"\n        if dialog is None or not isinstance(dialog, Dialog):\n            raise TypeError(\n                \"DialogSet.add(): dialog cannot be None and must be a Dialog or derived class.\"\n            )\n\n        if dialog.id in self._dialogs:\n            raise TypeError(\n                \"DialogSet.add(): A dialog with an id of '%s' already added.\"\n                % dialog.id\n            )\n\n        # dialog.telemetry_client = this._telemetry_client;\n        self._dialogs[dialog.id] = dialog\n\n        return self\n\n    async def create_context(self, turn_context: TurnContext) -> \"DialogContext\":\n        # This import prevents circular dependency issues\n        # pylint: disable=import-outside-toplevel\n        from .dialog_context import DialogContext\n\n        # pylint: disable=unnecessary-lambda\n        BotAssert.context_not_none(turn_context)\n\n        if not self._dialog_state:\n            raise RuntimeError(\n                \"DialogSet.CreateContextAsync(): DialogSet created with a null IStatePropertyAccessor.\"\n            )\n\n        state: DialogState = await self._dialog_state.get(\n            turn_context, lambda: DialogState()\n        )\n\n        return DialogContext(self, turn_context, state)\n\n    async def find(self, dialog_id: str) -> Dialog:\n        \"\"\"\n        Finds a dialog that was previously added to the set using add(dialog)\n        :param dialog_id: ID of the dialog/prompt to look up.\n        :return: The dialog if found, otherwise null.\n        \"\"\"\n        if not dialog_id:\n            raise TypeError(\"DialogContext.find(): dialog_id cannot be None.\")\n\n        if dialog_id in self._dialogs:\n            return self._dialogs[dialog_id]\n\n        return None\n\n    def find_dialog(self, dialog_id: str) -> Dialog:\n        \"\"\"\n        Finds a dialog that was previously added to the set using add(dialog)\n        :param dialog_id: ID of the dialog/prompt to look up.\n        :return: The dialog if found, otherwise null.\n        \"\"\"\n        if not dialog_id:\n            raise TypeError(\"DialogContext.find(): dialog_id cannot be None.\")\n\n        if dialog_id in self._dialogs:\n            return self._dialogs[dialog_id]\n\n        return None\n\n    def __str__(self):\n        if self._dialogs:\n            return \"dialog set empty!\"\n        return \" \".join(map(str, self._dialogs.keys()))\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py"}, {"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nfrom abc import ABC, abstractmethod\n\n\nfrom botbuilder.core import NullTelemetryClient, BotTelemetryClient\nfrom .dialog import Dialog\nfrom .dialog_context import DialogContext\nfrom .dialog_event import DialogEvent\nfrom .dialog_events import DialogEvents\nfrom .dialog_set import DialogSet\n\n\nclass DialogContainer(Dialog, ABC):\n    def __init__(self, dialog_id: str = None):\n        super().__init__(dialog_id)\n\n        self.dialogs = DialogSet()\n\n    @property\n    def telemetry_client(self) -> BotTelemetryClient:\n        \"\"\"\n        Gets the telemetry client for logging events.\n        \"\"\"\n        return self._telemetry_client\n\n    @telemetry_client.setter\n    def telemetry_client(self, value: BotTelemetryClient) -> None:\n        \"\"\"\n        Sets the telemetry client for all dialogs in this set.\n        \"\"\"\n        if value is None:\n            self._telemetry_client = NullTelemetryClient()\n        else:\n            self._telemetry_client = value\n\n        # Care! Dialogs.TelemetryClient assignment internally assigns the\n        # TelemetryClient for each dialog which could lead to an eventual stack\n        # overflow in cyclical dialog structures.\n        # Don't set the telemetry client if the candidate instance is the same as\n        # the currently set one.\n        if self.dialogs.telemetry_client != value:\n            self.dialogs.telemetry_client = self._telemetry_client\n\n    @abstractmethod\n    def create_child_context(self, dialog_context: DialogContext) -> DialogContext:\n        raise NotImplementedError()\n\n    def find_dialog(self, dialog_id: str) -> Dialog:\n        # TODO: deprecate DialogSet.find\n        return self.dialogs.find_dialog(dialog_id)\n\n    async def on_dialog_event(\n        self, dialog_context: DialogContext, dialog_event: DialogEvent\n    ) -> bool:\n        \"\"\"\n        Called when an event has been raised, using `DialogContext.emitEvent()`, by either the current dialog or a\n         dialog that the current dialog started.\n        :param dialog_context: The dialog context for the current turn of conversation.\n        :param dialog_event: The event being raised.\n        :return: True if the event is handled by the current dialog and bubbling should stop.\n        \"\"\"\n        handled = await super().on_dialog_event(dialog_context, dialog_event)\n\n        # Trace unhandled \"versionChanged\" events.\n        if not handled and dialog_event.name == DialogEvents.version_changed:\n\n            trace_message = (\n                f\"Unhandled dialog event: {dialog_event.name}. Active Dialog: \"\n                f\"{dialog_context.active_dialog.id}\"\n            )\n\n            await dialog_context.context.send_trace_activity(trace_message)\n\n        return handled\n\n    def get_internal_version(self) -> str:\n        \"\"\"\n        GetInternalVersion - Returns internal version identifier for this container.\n        DialogContainers detect changes of all sub-components in the container and map that to an DialogChanged event.\n        Because they do this, DialogContainers \"hide\" the internal changes and just have the .id. This isolates changes\n        to the container level unless a container doesn't handle it.  To support this DialogContainers define a\n        protected virtual method GetInternalVersion() which computes if this dialog or child dialogs have changed\n        which is then examined via calls to check_for_version_change_async().\n        :return: version which represents the change of the internals of this container.\n        \"\"\"\n        return self.dialogs.get_version()\n\n    async def check_for_version_change_async(self, dialog_context: DialogContext):\n        \"\"\"\n        :param dialog_context: dialog context.\n        :return: task.\n        Checks to see if a containers child dialogs have changed since the current dialog instance\n        was started.\n\n        This should be called at the start of `beginDialog()`, `continueDialog()`, and `resumeDialog()`.\n        \"\"\"\n        current = dialog_context.active_dialog.version\n        dialog_context.active_dialog.version = self.get_internal_version()\n\n        # Check for change of previously stored hash\n        if current and current != dialog_context.active_dialog.version:\n            # Give bot an opportunity to handle the change.\n            # - If bot handles it the changeHash will have been updated as to avoid triggering the\n            #   change again.\n            await dialog_context.emit_event(\n                DialogEvents.version_changed, self.id, True, False\n            )\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_container.py"}]}