Skip to content

Does the Tornado adapter support AsyncApp? #754

Closed

Description

@castrapel

Hello, I am trying to set up a slack app using the Tornado adapter. I am trying to use AsyncApp instead of App, as that is the purpose of Tornado as an Asyncio server. however, it looks like the Tornado adapter tries to call self.app.dispatch and doesn't attempt to call await self.app.async_dispatch which is what the AsyncApp class uses. CC @seratch (Saw that you had worked on the Tornado code)

I am successfully able to get it working with App as seen below:

slack_app = App(
    token=config.get("_global_.secrets.slack.bot_token"),
    signing_secret=config.get("_global_.secrets.slack.signing_secret")
)
.... (routes):
("/api/v3/slack/events", SlackEventsHandler, dict(app=slack_app)),
("/api/v3/slack/install", SlackOAuthHandler, dict(app=slack_app)),
("/api/v3/slack/oauth_redirect", SlackOAuthHandler, dict(app=slack_app)),

but I am unable to get this working with an AsyncApp. The app starts correctly but when I receive events, I get an error like this:

HTTPServerRequest(protocol='http', host='REDACTED', method='POST', uri='/api/v3/slack/events', version='HTTP/1.1', remote_ip='127.0.0.1')
Traceback (most recent call last):
  File "env/lib/python3.10/site-packages/tornado/web.py", line 1711, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "env/lib/python3.10/site-packages/slack_bolt/adapter/tornado/handler.py", line 17, in post
    bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(self.request))
AttributeError: 'AsyncApp' object has no attribute 'dispatch'
2022-11-01:16:26:54,418 ERROR    [web.py:2271] 500 POST /api/v3/slack/events (127.0.0.1) 34.36ms
2022-11-01:16:26:55,176 ERROR    [web.py:1798] Uncaught exception POST /api/v3/slack/events (127.0.0.1)

Reproducible in:

slack-bolt==1.15.2
slack-sdk==3.19.2
Python 3.10.8

OS info

#58-Ubuntu SMP Thu Oct 13 08:03:55 UTC 2022

(Ubuntu 22.04)

Activity

  1. seratch commented on Nov 2, 2022

    @seratch
    Contributor

    Hi @castrapel, thanks for asking the question.

    Since tornado.web.RequestHandler does not support async functions for get/post etc., the built-in Tornado adapter does not support AsyncApp use cases. See also: https://www.tornadoweb.org/en/stable/web.html

    Checking the following resources may be helpful too. However, as far as I know, the async related decorator does not work with bolt-python in the way that you expect.

    If you would like to make your listeners/middleware async for some reason, I'd suggest going with other modern asyncio-based frameworks such as AIOHTTP, FastAPI, Falcon, and Sanic. You can find examples here.

    If you have a certain reason to use Tornado over others, I highly recommend going with App and synchronous code. With that being said, if you are keen to explore some ways to adjust the adapter, you can fork it and try whatever may better work for you. Here is the adapter code: https://github.com/slackapi/bolt-python/tree/v1.15.2/slack_bolt/adapter/tornado As you can see, there are only two files and they are quite simple.

    I hope this was helpful to you.

  2. castrapel commented on Nov 2, 2022

    @castrapel
    Author

    Thank you for the response. tornado.web.RequestHandler does indeed support async def post, async def get, the stackoverflow is showing the Python2 way of handling Async. I am not sure why the Tornado team didn't document it well. Here is a benchmark comparison. Here is a way to test Tornado async.

    mkdir -p /tmp/tornadotest
    cd /tmp/tornadotest
    python3 -m venv env
    pip install tornado
    vim server.py # Paste in the contents of server.py below and save
    python server.py 
    # Visit http://localhost to confirm that you receive a text response: `GET request`

    Contents of server.py:

    import tornado.web
    import tornado.ioloop
    import tornado.options
    import tornado.httpserver
    
    import tornado.web
    
    class TestHandler(tornado.web.RequestHandler):
        async def get(self):
            self.write("GET request")
            
        async def post(self):
            self.write("POST request")
    
    routes = [
        (r"/", TestHandler),
    
    ]
    
    def make_app(debug: bool):
        return tornado.web.Application(routes, debug=debug)
    
    def run_service():
        debug_mode = True
        port = "8000"
        app = make_app(debug=debug_mode)
        app.listen(port)
        tornado.ioloop.IOLoop.current().start()
    
    if __name__ == "__main__":
        run_service()
  3. added
    enhancementNew feature or request
    and removed
    questionFurther information is requested
    on Nov 2, 2022
  4. added this to the 1.15.3 milestone on Nov 2, 2022
  5. self-assigned this
    on Nov 2, 2022
  6. seratch commented on Nov 2, 2022

    @seratch
    Contributor

    @castrapel Thanks for sharing this and I'm glad to hear that async functions are actually supported!

    I am taking day offs until Friday. I will look into the enhancement after coming back. If you are in a hurry, please consider copying the adapter code and editing it to work with async functions for now.

  7. castrapel commented on Nov 3, 2022

    @castrapel
    Author

    Thanks @seratch, I'd appreciate your help here! Not in a big hurry on my end.

  8. added a commit that references this issue on Nov 8, 2022
    cbf2383
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions