Skip to content

Commit 8ef2407

Browse files
author
Irving Popovetsky
committed
fix: join_team was broken due to an Python 3.14 incompatibility. improved the tests and verified no other of this class of errors exist
1 parent 2286e71 commit 8ef2407

5 files changed

Lines changed: 47 additions & 17 deletions

File tree

pybot/_vendor/sirbot/plugins/slack/plugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Vendored sirbot Slack plugin for Python 3.12+
33
"""
44

5-
import asyncio
5+
import inspect
66
import logging
77
import os
88
from collections.abc import Callable, Coroutine
@@ -24,7 +24,7 @@
2424

2525
def _ensure_async(handler: Callable) -> AsyncHandler:
2626
"""Ensure handler is an async function."""
27-
if not asyncio.iscoroutinefunction(handler):
27+
if not inspect.iscoroutinefunction(handler):
2828
raise TypeError(
2929
f"Handler {handler.__name__} must be an async function (defined with 'async def')"
3030
)

pybot/endpoints/slack/events.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@ async def team_join(event: Event, app: SirBot) -> None:
2929
user_id = event["user"]["id"]
3030

3131
*user_messages, community_message, outreach_team_message = build_messages(user_id)
32-
futures = [
33-
send_user_greetings(user_messages, slack_api),
34-
send_community_notification(community_message, slack_api),
35-
send_community_notification(outreach_team_message, slack_api),
36-
]
3732

3833
logger.info(f"New team join event: {event}")
3934
await asyncio.sleep(30)
40-
await asyncio.wait(futures)
35+
await asyncio.gather(
36+
send_user_greetings(user_messages, slack_api),
37+
send_community_notification(community_message, slack_api),
38+
send_community_notification(outreach_team_message, slack_api),
39+
)
4140

4241
headers = await get_backend_auth_headers(app.http_session)
4342
if headers:

pybot/plugins/api/plugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import asyncio
1+
import inspect
22
import logging
33
from collections import defaultdict
44
from collections.abc import Callable, Coroutine
@@ -14,7 +14,7 @@
1414

1515
def _ensure_async(handler: Callable) -> AsyncHandler:
1616
"""Ensure handler is an async function."""
17-
if not asyncio.iscoroutinefunction(handler):
17+
if not inspect.iscoroutinefunction(handler):
1818
raise TypeError(
1919
f"Handler {handler.__name__} must be an async function (defined with 'async def')"
2020
)

tests/endpoints/slack/test_slack_events.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import asyncio
1+
import inspect
22
import logging
3+
from unittest.mock import AsyncMock, patch
34

45
from pybot import endpoints
5-
from tests.data.events import MESSAGE_DELETE, MESSAGE_EDIT, PLAIN_MESSAGE
6+
from pybot.endpoints.slack.events import team_join
7+
from pybot._vendor.slack.events import Event
8+
from tests.data.events import MESSAGE_DELETE, MESSAGE_EDIT, PLAIN_MESSAGE, TEAM_JOIN
69

710

811
async def test_team_join_handler_exists(bot):
912
endpoints.slack.create_endpoints(bot["plugins"]["slack"])
1013

11-
assert asyncio.iscoroutinefunction(
14+
assert inspect.iscoroutinefunction(
1215
bot["plugins"]["slack"].routers["event"]._routes["team_join"]["*"]["*"][0][0]
1316
)
1417

@@ -35,3 +38,31 @@ async def test_no_other_messages_logged(bot, aiohttp_client, caplog):
3538
with caplog.at_level(logging.INFO):
3639
await client.post("/slack/events", json=PLAIN_MESSAGE)
3740
assert not any("CHANGE_LOGGING" in record.message for record in caplog.records)
41+
42+
43+
async def test_team_join_asyncio_gather_does_not_raise_typeerror(bot):
44+
"""
45+
Regression test for Python 3.14 compatibility.
46+
47+
In Python 3.14, asyncio.wait() no longer accepts bare coroutines.
48+
This test verifies that team_join uses asyncio.gather() correctly.
49+
"""
50+
event = Event.from_http(TEAM_JOIN, verification_token="supersecuretoken")
51+
52+
with (
53+
patch("pybot.endpoints.slack.events.asyncio.sleep", new_callable=AsyncMock),
54+
patch(
55+
"pybot.endpoints.slack.events.send_user_greetings", new_callable=AsyncMock
56+
),
57+
patch(
58+
"pybot.endpoints.slack.events.send_community_notification",
59+
new_callable=AsyncMock,
60+
),
61+
patch(
62+
"pybot.endpoints.slack.events.get_backend_auth_headers",
63+
new_callable=AsyncMock,
64+
return_value={},
65+
),
66+
):
67+
# This should not raise TypeError about coroutines
68+
await team_join(event, bot)

tests/test_upgrade_safety.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
- Handler registration verification
88
"""
99

10-
import asyncio
1110
import importlib
11+
import inspect
1212

1313
import pytest
1414

@@ -70,12 +70,12 @@ def test_slash_commands_are_async(self):
7070
for handler in handlers:
7171
# Get the wrapped function if decorated
7272
func = getattr(handler, "__wrapped__", handler)
73-
assert asyncio.iscoroutinefunction(func), f"{handler.__name__} must be async"
73+
assert inspect.iscoroutinefunction(func), f"{handler.__name__} must be async"
7474

7575
def test_event_handlers_are_async(self):
7676
from pybot.endpoints.slack.events import team_join
7777

78-
assert asyncio.iscoroutinefunction(team_join), "team_join must be async"
78+
assert inspect.iscoroutinefunction(team_join), "team_join must be async"
7979

8080
def test_action_handlers_are_async(self):
8181
from pybot.endpoints.slack.actions.general_actions import (
@@ -86,7 +86,7 @@ def test_action_handlers_are_async(self):
8686

8787
handlers = [claimed, delete_message, reset_claim]
8888
for handler in handlers:
89-
assert asyncio.iscoroutinefunction(handler), f"{handler.__name__} must be async"
89+
assert inspect.iscoroutinefunction(handler), f"{handler.__name__} must be async"
9090

9191

9292
class TestPluginLoading:

0 commit comments

Comments
 (0)