Python-slack-sdk: AsyncIO loop is not being shared from RTMClient to WebClient

Created on 4 Mar 2020  路  5Comments  路  Source: slackapi/python-slack-sdk

Description

Describe your issue here.

What type of issue is this? (place an x in one of the [ ])

  • [X] bug
  • [ ] enhancement (feature request)
  • [ ] question
  • [ ] documentation related
  • [ ] testing related
  • [ ] discussion

Requirements (place an x in each of the [ ])

  • [X] I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • [X] I've read and agree to the Code of Conduct.
  • [X] I've searched for any related issues and avoided creating a duplicate issue.

Bug Report

Filling out the following details about bugs will help us solve your issue sooner.

Reproducible in:

slackclient version: 2.5.0

python version: 3.7.6 (venv)

Steps to reproduce:

  1. Use SlackClient as part of a project, not only as a standalone stuff. The project uses other libraries that use asyncio
  2. Create a RTMClient, and set the loop as asyncio.get_event_loop() in order to use the same as the project
  3. When receiving a message, get the attached web_client and use it to send a message back

Expected result:

The message is sent

Actual result:

RuntimeError: this event loop is already running.

Explanation of what's going on and workaround:

What is actually happening is that the loop set to the RTMClient is not being shared with the attached WebClient.

The following code does the trick, but it's a workaround and not a real solution

import os
from slack import RTMClient
import asyncio
loop = asyncio.get_event_loop()

@RTMClient.run_on(event="message")
def say_hello(**payload):
  data = payload['data']
  web_client = payload['web_client']

#### WORKAROUND #########################
# Manually set the project's loop on the WebClient, even when it was set on the parent RTMClient
  web_client._event_loop = loop
  # Maybe you will also need the following line uncommented
  # web_client.run_async = True
##########################################

  if 'Hello' in data['text']:
    channel_id = data['channel']
    thread_ts = data['ts']
    user = data['user']

    web_client.chat_postMessage(
      channel=channel_id,
      text=f"Hi <@{user}>!",
      thread_ts=thread_ts
    )

slack_token = os.environ["SLACK_API_TOKEN"]
rtm_client = RTMClient(token=slack_token, loop=loop)
rtm_client.start()

Regarding uvloop:

If the main project's loop is not the common one but an uvloop, you will need to make some mods before getting the loop in order to set it on the RTMClient

...
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.get_event_loop()
...
2x concurrency rtm-client web-client

Most helpful comment

FYI: this commit contains a test case reproducing this issue and it shows two possible solutions for it even with slackclient 2.5.0. https://github.com/seratch/python-slackclient/commit/6fb428723a67e1e3b22da27c4378f3c9b18291d3

In the forthcoming release, the library will address the issue for run_async=False mode (the default). The solution I'm currently thinking as the best is implementing the internals of the WebClient never relies on asyncio's event loop if run_async is False.

The internal modification won't bring any breaking changes to the library users. WebClient just ignores the event loop in the case. So, with future versions, you no longer need to have the necessity to share an event loop as long as you go with the default run_async=False option.

All 5 comments

FYI: this commit contains a test case reproducing this issue and it shows two possible solutions for it even with slackclient 2.5.0. https://github.com/seratch/python-slackclient/commit/6fb428723a67e1e3b22da27c4378f3c9b18291d3

In the forthcoming release, the library will address the issue for run_async=False mode (the default). The solution I'm currently thinking as the best is implementing the internals of the WebClient never relies on asyncio's event loop if run_async is False.

The internal modification won't bring any breaking changes to the library users. WebClient just ignores the event loop in the case. So, with future versions, you no longer need to have the necessity to share an event loop as long as you go with the default run_async=False option.

On the other hand, for the run_async=True option, the solution is as easy as checking if a loop was set on the RTMClient. If so, add it to the params when creating the WebClient (action the RTMClient does internally).

If you manually create a WebClient, you can set your own loop (as you can do on RTMClient creation)

I've updated the demo code to highlight the workaround I've made and need to be solved by doing the proper loop/run_async params passing from RTMClient to WebClient

Let me close this issue now as #662 resolved this for the run_async=False mode.

馃憢 slackclient 2.6.0rc1 is out. The pre-release version contains fixes for your issue described here.
https://pypi.org/project/slackclient/2.6.0rc1/

One week later from now, we'll be releasing version 2.6.0 to PyPI.

If you have a chance, could you try the release candidate version out and let us know your feedback? Thank you very much for being patient with this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

charlesreid1 picture charlesreid1  路  3Comments

LMPK picture LMPK  路  3Comments

sofya-salmanova picture sofya-salmanova  路  5Comments

kompotkot picture kompotkot  路  4Comments

schlegelp picture schlegelp  路  3Comments