Repository: microsoft/botbuilder-python

Issue:
[PORT] Replace UseState() with UseBotState() 
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/3862

Fixes #3859 
and use untyped params so that order and type are not fixed.
Tweak RegisterMiddlewareClass so you can provide the key for the turnstate.


# Changed projects
* Microsoft.Bot.Builder.Dialogs.Adaptive.Testing
* Microsoft.Bot.Builder
* Microsoft.Bot.Builder.AI.QnA.Tests
* Microsoft.Bot.Builder.Dialogs.Adaptive.Templates.Tests
* Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
* Microsoft.Bot.Builder.Dialogs.Declarative.Tests
* Microsoft.Bot.Builder.Dialogs.Tests
* Microsoft.Bot.Builder.TestBot.Json
* 






Discussion:
This applies to AdapterExtensions, and callers of that method.

Resolving patch:
diff --git a/libraries/botbuilder-core/botbuilder/core/adapter_extensions.py b/libraries/botbuilder-core/botbuilder/core/adapter_extensions.py
--- a/libraries/botbuilder-core/botbuilder/core/adapter_extensions.py
+++ b/libraries/botbuilder-core/botbuilder/core/adapter_extensions.py
@@ -1,7 +1,10 @@
 # Copyright (c) Microsoft Corporation. All rights reserved.
 # Licensed under the MIT License.
+from warnings import warn
+
 from botbuilder.core import (
     BotAdapter,
+    BotState,
     Storage,
     RegisterClassMiddleware,
     UserState,
@@ -23,6 +26,39 @@ def use_storage(adapter: BotAdapter, storage: Storage) -> BotAdapter:
         """
         return adapter.use(RegisterClassMiddleware(storage))
 
+    @staticmethod
+    def use_bot_state(
+        bot_adapter: BotAdapter, *bot_states: BotState, auto: bool = True
+    ) -> BotAdapter:
+        """
+        Registers bot state object into the TurnContext. The botstate will be available via the turn context.
+
+        :param bot_adapter: The BotAdapter on which to register the state objects.
+        :param bot_states: One or more BotState objects to register.
+        :return: The updated adapter.
+        """
+        if not bot_states:
+            raise TypeError("At least one BotAdapter is required")
+
+        for bot_state in bot_states:
+            bot_adapter.use(
+                RegisterClassMiddleware(
+                    bot_state, AdapterExtensions.fullname(bot_state)
+                )
+            )
+
+        if auto:
+            bot_adapter.use(AutoSaveStateMiddleware(bot_states))
+
+        return bot_adapter
+
+    @staticmethod
+    def fullname(obj):
+        module = obj.__class__.__module__
+        if module is None or module == str.__class__.__module__:
+            return obj.__class__.__name__  # Avoid reporting __builtin__
+        return module + "." + obj.__class__.__name__
+
     @staticmethod
     def use_state(
         adapter: BotAdapter,
@@ -31,7 +67,7 @@ def use_state(
         auto: bool = True,
     ) -> BotAdapter:
         """
-        Registers user and conversation state objects with the adapter. These objects will be available via
+        [DEPRECATED] Registers user and conversation state objects with the adapter. These objects will be available via
         the turn context's `turn_state` property.
 
         :param adapter: The BotAdapter on which to register the state objects.
@@ -40,6 +76,11 @@ def use_state(
         :param auto: True to automatically persist state each turn.
         :return: The BotAdapter
         """
+        warn(
+            "This method is deprecated in 4.9. You should use the method .use_bot_state() instead.",
+            DeprecationWarning,
+        )
+
         if not adapter:
             raise TypeError("BotAdapter is required")
 
diff --git a/libraries/botbuilder-core/botbuilder/core/register_class_middleware.py b/libraries/botbuilder-core/botbuilder/core/register_class_middleware.py
--- a/libraries/botbuilder-core/botbuilder/core/register_class_middleware.py
+++ b/libraries/botbuilder-core/botbuilder/core/register_class_middleware.py
@@ -10,8 +10,9 @@ class RegisterClassMiddleware(Middleware):
     Middleware for adding an object to or registering a service with the current turn context.
     """
 
-    def __init__(self, service):
+    def __init__(self, service, key: str = None):
         self.service = service
+        self._key = key
 
     async def on_turn(
         self, context: TurnContext, logic: Callable[[TurnContext], Awaitable]
@@ -19,7 +20,8 @@ async def on_turn(
         # C# has TurnStateCollection with has overrides for adding items
         # to TurnState.  Python does not.  In C#'s case, there is an 'Add'
         # to handle adding object, and that uses the fully qualified class name.
-        context.turn_state[self.fullname(self.service)] = self.service
+        key = self._key or self.fullname(self.service)
+        context.turn_state[key] = self.service
         await logic()
 
     @staticmethod


Test patch:
diff --git a/libraries/botbuilder-dialogs/tests/test_dialogextensions.py b/libraries/botbuilder-dialogs/tests/test_dialogextensions.py
--- a/libraries/botbuilder-dialogs/tests/test_dialogextensions.py
+++ b/libraries/botbuilder-dialogs/tests/test_dialogextensions.py
@@ -221,7 +221,7 @@ async def capture_eoc(
             logic, TestAdapter.create_conversation_reference(conversation_id)
         )
         AdapterExtensions.use_storage(adapter, storage)
-        AdapterExtensions.use_state(adapter, user_state, convo_state)
+        AdapterExtensions.use_bot_state(adapter, user_state, convo_state)
         adapter.use(TranscriptLoggerMiddleware(ConsoleTranscriptLogger()))
 
         return TestFlow(None, adapter)