text: # Copyright (c) Microsoft Corporation. All rights reserved.
  # Licensed under the MIT License.
  import inspect
  from hashlib import sha256
  from typing import Dict
  
  from botbuilder.core import (
      NullTelemetryClient,
      BotTelemetryClient,
      TurnContext,
      BotAssert,
      StatePropertyAccessor,
  )
  from .dialog import Dialog
  from .dialog_state import DialogState
  
  
  class DialogSet:
      def __init__(self, dialog_state: StatePropertyAccessor = None):
          # pylint: disable=import-outside-toplevel
          if dialog_state is None:
              frame = inspect.currentframe().f_back
              try:
                  # try to access the caller's "self"
                  try:
                      self_obj = frame.f_locals["self"]
                  except KeyError:
                      raise TypeError("DialogSet(): dialog_state cannot be None.")
                  # Only ComponentDialog can initialize with None dialog_state
                  from .component_dialog import ComponentDialog
                  from .dialog_manager import DialogManager
                  from .dialog_container import DialogContainer
  
                  if not isinstance(
                      self_obj, (ComponentDialog, DialogContainer, DialogManager)
                  ):
                      raise TypeError("DialogSet(): dialog_state cannot be None.")
              finally:
                  # make sure to clean up the frame at the end to avoid ref cycles
                  del frame
  
          self._dialog_state = dialog_state
          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.
          <returns>Version will change when any of the child dialogs version changes.</returns>
          """
          if not self._version:
              version = ""
              for _, dialog in self._dialogs.items():
                  aux_version = dialog.get_version()
                  if aux_version:
                      version += aux_version
  
              self._version = sha256(version)
  
          return self._version
  
      def add(self, dialog: Dialog):
          """
          Adds a new dialog to the set and returns the added dialog.
          :param dialog: The dialog to add.
          """
          if dialog is None or not isinstance(dialog, Dialog):
              raise TypeError(
                  "DialogSet.add(): dialog cannot be None and must be a Dialog or derived class."
              )
  
          if dialog.id in self._dialogs:
              raise TypeError(
                  "DialogSet.add(): A dialog with an id of '%s' already added."
                  % dialog.id
              )
  
          # dialog.telemetry_client = this._telemetry_client;
          self._dialogs[dialog.id] = dialog
  
          return self
  
      async def create_context(self, turn_context: TurnContext) -> "DialogContext":
          # This import prevents circular dependency issues
          # pylint: disable=import-outside-toplevel
          from .dialog_context import DialogContext
  
          # pylint: disable=unnecessary-lambda
          BotAssert.context_not_none(turn_context)
  
          if not self._dialog_state:
              raise RuntimeError(
                  "DialogSet.CreateContextAsync(): DialogSet created with a null IStatePropertyAccessor."
              )
  
          state: DialogState = await self._dialog_state.get(
              turn_context, lambda: DialogState()
          )
  
          return DialogContext(self, turn_context, state)
  
      async def find(self, dialog_id: str) -> Dialog:
          """
          Finds a dialog that was previously added to the set using add(dialog)
          :param dialog_id: ID of the dialog/prompt to look up.
          :return: The dialog if found, otherwise null.
          """
          if not dialog_id:
              raise TypeError("DialogContext.find(): dialog_id cannot be None.")
  
          if dialog_id in self._dialogs:
              return self._dialogs[dialog_id]
  
          return None
  
      def find_dialog(self, dialog_id: str) -> Dialog:
          """
          Finds a dialog that was previously added to the set using add(dialog)
          :param dialog_id: ID of the dialog/prompt to look up.
          :return: The dialog if found, otherwise null.
          """
          if not dialog_id:
              raise TypeError("DialogContext.find(): dialog_id cannot be None.")
  
          if dialog_id in self._dialogs:
              return self._dialogs[dialog_id]
  
          return None
  
      def __str__(self):
          if self._dialogs:
              return "dialog set empty!"
          return " ".join(map(str, self._dialogs.keys()))
  