miguelgrinberg / python-socketio

Python Socket.IO server and client
MIT License
3.96k stars 587 forks source link

Error when emitting a message to multiple recipients at the same time #1081

Closed DarkAce65 closed 1 year ago

DarkAce65 commented 1 year ago

Describe the bug Hello, big fan of this library, thank you for maintaining it! I wanted to report what looks like a bug in the AsyncManager's emit method. Based on the docs for emit, it seems like I can pass a list of recipients to the to/room argument to emit an event to multiple rooms but I get the error shown below:

Traceback (most recent call last):
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 524, in _handle_event_internal
    r = await server._trigger_event(data[0], namespace, sid, *data[1:])
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 558, in _trigger_event
    ret = await handler(*args)
  File "/tmp/test/server.py", line 12, in ping
    await sio_server.emit("pong", to=[sid])
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 168, in emit
    await self.manager.emit(event, data, namespace, room=room,
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_manager.py", line 18, in emit
    if namespace not in self.rooms or room not in self.rooms[namespace]:
TypeError: unhashable type: 'list'

^ room here is a list which is not hashable and is throwing this TypeError https://github.com/miguelgrinberg/python-socketio/blob/81f872c17051b0d1d0cea7ca49a3bdca6f6bae1d/src/socketio/asyncio_manager.py#L18

To Reproduce I made a simple client/server setup to verify this and confirmed that it only affects the async version of the server (the synchronous server seems to work as expected).

server.py

from aiohttp import web
from socketio import AsyncServer

sio_server = AsyncServer(logger=True, engineio_logger=True)
app = web.Application()
sio_server.attach(app)

@sio_server.event
async def test_event_from_client(sid):
    await sio_server.emit("test_event_from_server", to=sid) # this works as expected
    await sio_server.emit("test_event_from_server", to=[sid]) # this does not

if __name__ == "__main__":
    web.run_app(app, port=8080)

client.py

import asyncio
from socketio import AsyncClient

sio_client = AsyncClient(logger=True, engineio_logger=True)

async def test_client():
    await sio_client.connect("http://localhost:8080")
    await sio_client.emit("test_event_from_client")
    await sio_client.wait()

if __name__ == "__main__":
    asyncio.run(test_client())

Expected behavior I'd expect no error to be raised and that a message would be emitted to the unique list of participants for the list of ids passed to emit() (ex. if I emit to a list of room ids, each client in at least one of the rooms would receive one and only one message).

Logs Server logs:

received event "test_event_from_client" from sNDx2ya64AYLtpHYAAAB [/]
emitting event "test_event_from_server" to sNDx2ya64AYLtpHYAAAB [/]
w_QsRzjHdk0VbslPAAAA: Sending packet MESSAGE data 2["test_event_from_server"]
emitting event "test_event_from_server" to ['sNDx2ya64AYLtpHYAAAB'] [/]
Traceback (most recent call last):
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 524, in _handle_event_internal
    r = await server._trigger_event(data[0], namespace, sid, *data[1:])
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 558, in _trigger_event
    ret = await handler(*args)
  File "/tmp/test/server.py", line 12, in test_event_from_client
    await sio_server.emit("test_event_from_server", to=[sid])
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_server.py", line 168, in emit
    await self.manager.emit(event, data, namespace, room=room,
  File "/home/taha/.cache/pypoetry/virtualenvs/socketio-issue-CHLv_kh8-py3.10/lib/python3.10/site-packages/socketio/asyncio_manager.py", line 18, in emit
    if namespace not in self.rooms or room not in self.rooms[namespace]:
TypeError: unhashable type: 'list'
DarkAce65 commented 1 year ago

Also happy to put in a PR to fix this if it's as simple as updating that single conditional and adding some tests

miguelgrinberg commented 1 year ago

Thanks! This was an unintentional omission, back when I added this option I only implemented it for the sync server.