question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using callable object instances as middleware does not work.

See original GitHub issue

Summary

The various methods for registering middleware in Bolt ostensibly take any Callable item. 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 up DEBUG, 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_bolt version

slack-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:

class MyMiddleware:
    def __call__(self, body, context, logger):
        user = context['foo'] = body["user_id"] if "user_id" in body else body["user"]["id"]
        logger.debug(f"The user is: {user}")

Turn the logging level up to DEBUG:

import logging
logging.basicConfig(level=logging.DEBUG)

Create an app that uses this middleware:

app = slack_bolt.App()
my_middleware = MyMiddleware()
app.use(my_middleware)

@app.command("/foobar")
def my_command(ack):
    ack("Hello!")

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 AttributeError exception in middleware/custom_middleware.py because, 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 name property of the CustomMiddleware object, 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 in AsyncCustomMiddleware.

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.py and listener/asyncio_runner.py attempt to check the lazy function __name__ attribute against request.lazy_function_name.

The solution to this is likely to introduce a new utility function to find the name of a callable, e.g.:

def callable_name(func):
    if hasattr(func, "__name__"):
        return func.__name__
    else:
        return f"{func. __class__.__module__}.{func.__class__.__name__}"

and then use this wherever we need the name of a callable provided by the user.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
mwbrookscommented, Jan 19, 2021

Hey @nickovs, thanks for catching this bug, providing a well thought out and detailed analysis, and suggesting an elegant solution. 👌🏻

Your suggestion appears sound to me, but I’m going to let @seratch step-in on this issue to discuss the solution before we open a pull request to solve.

1reaction
seratchcommented, Jan 20, 2021

Thanks for your contribution #216 🎉 I will release a new patch version shortly.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to make middleware object callable in Django 2.2
I'm trying to update a django/mezzanine application from python 2.7 to python 3.7. Can you help me in fixing the error below (...
Read more >
Middleware - Django documentation
A middleware is a callable that takes a request and returns a response, just like a view. A middleware can be written as...
Read more >
Application — Sanic 22.9.1 documentation - Read the Docs
A helper method to register class instance or functions as a handler to the application url routes. ... Schedule a task to run...
Read more >
PSR-15 Meta Document - PHP-FIG
Middleware has existed for many years in the PHP ecosystem. ... handler and middleware interfaces eliminates several problems and has a number of...
Read more >
Handlers and middleware in the AWS SDK for PHP Version 3
Each SDK client class owns an Aws\HandlerList instance that is accessible through the getHandlerList() method of a client. You can retrieve a client's ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found