Repository navigation
Using callable object instances as middleware does not work. #215
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Jan 19, 2021 - Reacted by Nicko van Someren and Kazuhiro Sera
@nickovs Thanks for sharing the thorough report of the issue. You are right about the issue described here, and the suggested solution looks great to me.
If possible, I would love to have your contribution in the commit history. Do you have the time to send a pull request? Having corresponding tests in the following files would be appreciated.
- https://github.com/slackapi/bolt-python/blob/v1.2.2/tests/scenario_tests/test_middleware.py
- https://github.com/slackapi/bolt-python/blob/v1.2.2/tests/scenario_tests_async/test_middleware.py
If you want me to work on the fix, I am happy to do so. Let me know your availability.
- added a commit that references this issue
on Jan 20, 2021 I have created a fix and test cases for the fix, and submitted a Pull Request.
Note that my suspicions about a similar problem existing in the lazy function handling were correct, so I have also fixed the code there and I've added test cases for passing callable-class instances as lazy functions as well.
While the issue also in theory existed for async middleware and async lazy functions, right now there is no way to trigger the bug. This is because an instance of a class that has an async
__call__method fails the testinspect.iscoroutinefunction(), so the registration functions will not accept these objects. I've fixed the code in the async paths anyway, in case someone comes up with a more reliable test for async callables.- added a commit that references this issue
on Jan 20, 2021 - added a commit that references this issue
on Jan 20, 2021 Thanks for your contribution #216 🎉 I will release a new patch version shortly.
Reacted by Nicko van SomerenGlad to help. Thank you for the fast turn-around!
Summary
The various methods for registering middleware in Bolt ostensibly take any
Callableitem. If the middleware is an instance of a callable class (i.e. a class that defines a__call__method) rather than a function or method, and if the logging level is turned upDEBUG, then Bolt throws an exception just before it would have called the middleware.Registering a callable object as middleware is useful for a variety of activities such as authentication handlers that need to maintain context or connections to other services.
Reproducible in:
The
slack_boltversionslack-bolt==1.2.1
slack-sdk==3.2.0
Python runtime version
Python 3.8.2
OS info
ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H2
Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64
Steps to reproduce:
Create some callable class:
Turn the logging level up to
DEBUG:Create an app that uses this middleware:
Run the Bolt app and trigger a handler
Expected result:
The Middleware object's
__call__method should be called prior to any handlers.Actual result:
The Bolt framework throws an
AttributeErrorexception inmiddleware/custom_middleware.pybecause, unlike a regular function or method, a callable object does not have a__name__attribute.Analysis
The problem is caused by debug logging in app/app.py trying to read the
nameproperty of theCustomMiddlewareobject, which naïvely attempts to read the__name__attribute of the registered callable, but instances of callable classes do not have this attribute. The same bug is present inAsyncCustomMiddleware.It is possible that a related bug exists (without needing the logging level to be turned up) if a callable object instance is used for a "lazy function", since multiple pieces of code in
listener/thread_runner.pyandlistener/asyncio_runner.pyattempt to check the lazy function__name__attribute againstrequest.lazy_function_name.The solution to this is likely to introduce a new utility function to find the name of a callable, e.g.:
and then use this wherever we need the name of a callable provided by the user.