diff --git a/slack_bolt/listener/asyncio_runner.py b/slack_bolt/listener/asyncio_runner.py index 1ea9de2a2..b35f05e23 100644 --- a/slack_bolt/listener/asyncio_runner.py +++ b/slack_bolt/listener/asyncio_runner.py @@ -15,7 +15,7 @@ ) from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse -from slack_bolt.util.utils import create_copy +from slack_bolt.util.utils import create_copy, name_for_callable class AsyncioListenerRunner: @@ -71,7 +71,7 @@ async def run( for lazy_func in listener.lazy_functions: if request.lazy_function_name: - func_name = lazy_func.__name__ + func_name = name_for_callable(lazy_func) if func_name == request.lazy_function_name: await self.lazy_listener_runner.run( function=lazy_func, request=request @@ -128,7 +128,7 @@ async def run_ack_function_asynchronously( for lazy_func in listener.lazy_functions: if request.lazy_function_name: - func_name = lazy_func.__name__ + func_name = name_for_callable(lazy_func) if func_name == request.lazy_function_name: await self.lazy_listener_runner.run( function=lazy_func, request=request @@ -163,7 +163,7 @@ def _start_lazy_function( self, lazy_func: Callable[..., Awaitable[None]], request: AsyncBoltRequest ) -> None: # Start a lazy function asynchronously - func_name: str = lazy_func.__name__ + func_name: str = name_for_callable(lazy_func) self.logger.debug(debug_running_lazy_listener(func_name)) copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) diff --git a/slack_bolt/listener/thread_runner.py b/slack_bolt/listener/thread_runner.py index 2e60e1c41..05601a511 100644 --- a/slack_bolt/listener/thread_runner.py +++ b/slack_bolt/listener/thread_runner.py @@ -13,7 +13,7 @@ ) from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse -from slack_bolt.util.utils import create_copy +from slack_bolt.util.utils import create_copy, name_for_callable class ThreadListenerRunner: @@ -72,7 +72,7 @@ def run( # type: ignore for lazy_func in listener.lazy_functions: if request.lazy_function_name: - func_name = lazy_func.__name__ + func_name = name_for_callable(lazy_func) if func_name == request.lazy_function_name: self.lazy_listener_runner.run( function=lazy_func, request=request @@ -127,7 +127,7 @@ def run_ack_function_asynchronously(): for lazy_func in listener.lazy_functions: if request.lazy_function_name: - func_name = lazy_func.__name__ + func_name = name_for_callable(lazy_func) if func_name == request.lazy_function_name: self.lazy_listener_runner.run( function=lazy_func, request=request @@ -162,7 +162,7 @@ def _start_lazy_function( self, lazy_func: Callable[..., None], request: BoltRequest ) -> None: # Start a lazy function asynchronously - func_name: str = lazy_func.__name__ + func_name: str = name_for_callable(lazy_func) self.logger.debug(debug_running_lazy_listener(func_name)) copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) diff --git a/slack_bolt/middleware/async_custom_middleware.py b/slack_bolt/middleware/async_custom_middleware.py index be44cf2db..5d6b6837c 100644 --- a/slack_bolt/middleware/async_custom_middleware.py +++ b/slack_bolt/middleware/async_custom_middleware.py @@ -7,6 +7,7 @@ from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from .async_middleware import AsyncMiddleware +from slack_bolt.util.utils import name_for_callable class AsyncCustomMiddleware(AsyncMiddleware): @@ -45,4 +46,4 @@ async def async_process( @property def name(self) -> str: - return f"AsyncCustomMiddleware(func={self.func.__name__})" + return f"AsyncCustomMiddleware(func={name_for_callable(self.func)})" diff --git a/slack_bolt/middleware/custom_middleware.py b/slack_bolt/middleware/custom_middleware.py index f3e4afff8..a24513c24 100644 --- a/slack_bolt/middleware/custom_middleware.py +++ b/slack_bolt/middleware/custom_middleware.py @@ -7,6 +7,7 @@ from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse from .middleware import Middleware +from slack_bolt.util.utils import name_for_callable class CustomMiddleware(Middleware): @@ -41,4 +42,4 @@ def process( @property def name(self) -> str: - return f"CustomMiddleware(func={self.func.__name__})" + return f"CustomMiddleware(func={name_for_callable(self.func)})" diff --git a/slack_bolt/util/utils.py b/slack_bolt/util/utils.py index e52a53674..272c7d382 100644 --- a/slack_bolt/util/utils.py +++ b/slack_bolt/util/utils.py @@ -1,6 +1,6 @@ import copy import sys -from typing import Optional, Union, Dict, Any, Sequence +from typing import Optional, Union, Dict, Any, Sequence, Callable from slack_sdk import WebClient from slack_sdk.models import JsonObject @@ -57,3 +57,11 @@ def get_boot_message(development_server: bool = False) -> str: return "⚡️ Bolt app is running! (development server)" else: return "⚡️ Bolt app is running!" + + +def name_for_callable(func: Callable) -> str: + if hasattr(func, "__name__"): + func_name = func.__name__ + else: + func_name = f"{func.__class__.__module__}.{func.__class__.__name__}" + return func_name diff --git a/tests/scenario_tests/test_lazy.py b/tests/scenario_tests/test_lazy.py index 8a633f8cc..6a2c89607 100644 --- a/tests/scenario_tests/test_lazy.py +++ b/tests/scenario_tests/test_lazy.py @@ -110,3 +110,31 @@ def async2(say): assert response.status == 200 time.sleep(1) # wait a bit assert self.mock_received_requests["/chat.postMessage"] == 2 + + def test_lazy_class(self): + def just_ack(ack): + ack() + + class LazyClass: + def __call__(self, say): + time.sleep(0.3) + say(text="lazy function 1") + + def async2(say): + time.sleep(0.5) + say(text="lazy function 2") + + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.action("a")( + ack=just_ack, + lazy=[LazyClass(), async2], + ) + + request = self.build_valid_request() + response = app.dispatch(request) + assert response.status == 200 + time.sleep(1) # wait a bit + assert self.mock_received_requests["/chat.postMessage"] == 2 diff --git a/tests/scenario_tests/test_listener_middleware.py b/tests/scenario_tests/test_listener_middleware.py index 4906375d8..11ca31115 100644 --- a/tests/scenario_tests/test_listener_middleware.py +++ b/tests/scenario_tests/test_listener_middleware.py @@ -93,6 +93,23 @@ def handle(ack): response = app.dispatch(self.build_request()) assert response.status == 200 + def test_class_next(self): + class NextClass: + def __call__(self, next): + next() + + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + + @app.shortcut(constraints="test-shortcut", middleware=[NextClass()]) + def handle(ack): + ack() + + response = app.dispatch(self.build_request()) + assert response.status == 200 + def listener_middleware_returning_response(): return BoltResponse(status=200, body="listener middleware") diff --git a/tests/scenario_tests/test_middleware.py b/tests/scenario_tests/test_middleware.py index 569dc6261..deb677d1d 100644 --- a/tests/scenario_tests/test_middleware.py +++ b/tests/scenario_tests/test_middleware.py @@ -86,6 +86,23 @@ def test_next_call(self): assert response.body == "acknowledged!" assert self.mock_received_requests["/auth.test"] == 1 + def test_class_call(self): + class NextClass: + def __call__(self, next): + next() + + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.use(NextClass()) + app.shortcut("test-shortcut")(just_ack) + + response = app.dispatch(self.build_request()) + assert response.status == 200 + assert response.body == "acknowledged!" + assert self.mock_received_requests["/auth.test"] == 1 + def just_ack(ack): ack("acknowledged!")