Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions integration_tests/rtm/test_issue_558.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def tearDown(self):
# Reset the decorators by @RTMClient.run_on
RTMClient._callbacks = collections.defaultdict(list)

@pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed")
@pytest.mark.skipif(condition=is_not_specified(), reason="To avoid rate limited errors")
@async_test
async def test_issue_558(self):
channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID]
Expand All @@ -38,7 +38,7 @@ async def test_issue_558(self):
async def process_messages(**payload):
self.logger.debug(payload)
self.message_count += 1
await asyncio.sleep(10) # this blocks all handlers
await asyncio.sleep(10) # this used to block all other handlers

async def process_reactions(**payload):
self.logger.debug(payload)
Expand All @@ -65,15 +65,15 @@ async def process_reactions(**payload):

message = await web_client.chat_postMessage(channel=channel_id, text=text)
self.assertFalse("error" in message)
# start blocking here
# used to start blocking here

# This reaction_add event won't be handled due to a bug
second_reaction = await web_client.reactions_add(channel=channel_id, timestamp=ts, name="tada")
self.assertFalse("error" in second_reaction)
await asyncio.sleep(2)

self.assertEqual(self.message_count, 1)
self.assertEqual(self.reaction_count, 2) # fails
self.assertEqual(self.reaction_count, 2) # used to fail
finally:
if not rtm._stopped:
rtm.stop()
19 changes: 17 additions & 2 deletions slack/rtm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import concurrent
import inspect
import signal
from typing import Optional, Callable, DefaultDict
from asyncio import Future
from typing import Optional, Callable, DefaultDict, List
from ssl import SSLContext
from threading import current_thread, main_thread

Expand Down Expand Up @@ -364,7 +365,12 @@ async def _connect_and_read(self):

async def _read_messages(self):
"""Process messages received on the WebSocket connection."""
text_message_callback_executions: List[Future] = []
while not self._stopped and self._websocket is not None:
for future in text_message_callback_executions:
if future.done():
text_message_callback_executions.remove(future)

try:
# Wait for a message to be received, but timeout after a second so that
# we can check if the socket has been closed, or if self._stopped is
Expand All @@ -384,11 +390,20 @@ async def _read_messages(self):
)
self._websocket = None
await self._dispatch_event(event="close")
num_of_running_callbacks = len(text_message_callback_executions)
if num_of_running_callbacks > 0:
self._logger.info(
"WebSocket connection has been closed "
f"though {num_of_running_callbacks} callback executions were still in progress"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a msg for the devs that they lost some executions by running close? Guessing we wouldn't want to halt the close event

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as you guessed, this message is meant for giving a bit more information for developers for the case they unexpectedly encounter this situation. This message indicates that if some of the remaining tasks are going to send messages back to the Slack server over a WebSocket connection, it won't work. If the remaining tasks don't talk to the Slack server, they're not terminated yet, so that they may result in success.

)
return

if message.type == aiohttp.WSMsgType.TEXT:
payload = message.json()
event = payload.pop("type", "Unknown")
await self._dispatch_event(event, data=payload)
# Asynchronously run callbacks to handle simultaneous incoming messages from Slack
f = asyncio.ensure_future(self._dispatch_event(event, data=payload))
text_message_callback_executions.append(f)
elif message.type == aiohttp.WSMsgType.ERROR:
self._logger.error("Received an error on the websocket: %r", message)
await self._dispatch_event(event="error", data=message)
Expand Down