Skip to content

Commit 88dcf7d

Browse files
change async_handlers default to true, and add call() method
1 parent 7c952de commit 88dcf7d

File tree

8 files changed

+353
-180
lines changed

8 files changed

+353
-180
lines changed

socketio/asyncio_client.py

+36-29
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ async def wait(self):
120120
if self.eio.state != 'connected':
121121
break
122122

123-
async def emit(self, event, data=None, namespace=None, callback=None,
124-
wait=False, timeout=60):
123+
async def emit(self, event, data=None, namespace=None, callback=None):
125124
"""Emit a custom event to one or more connected clients.
126125
127126
:param event: The event name. It can be any string. The event names
@@ -138,30 +137,11 @@ async def emit(self, event, data=None, namespace=None, callback=None,
138137
that will be passed to the function are those provided
139138
by the client. Callback functions can only be used
140139
when addressing an individual client.
141-
:param wait: If set to ``True``, this function will wait for the
142-
server to handle the event and acknowledge it via its
143-
callback function. The value(s) passed by the server to
144-
its callback will be returned. If set to ``False``,
145-
this function emits the event and returns immediately.
146-
:param timeout: If ``wait`` is set to ``True``, this parameter
147-
specifies a waiting timeout. If the timeout is reached
148-
before the server acknowledges the event, then a
149-
``TimeoutError`` exception is raised.
150140
151141
Note: this method is a coroutine.
152142
"""
153143
namespace = namespace or '/'
154144
self.logger.info('Emitting event "%s" [%s]', event, namespace)
155-
if wait is True:
156-
callback_event = self.eio.create_event()
157-
callback_args = []
158-
159-
def event_callback(*args):
160-
callback_args.append(args)
161-
callback_event.set()
162-
163-
callback = event_callback
164-
165145
if callback is not None:
166146
id = self._generate_ack_id(namespace, callback)
167147
else:
@@ -181,14 +161,6 @@ def event_callback(*args):
181161
await self._send_packet(packet.Packet(
182162
packet.EVENT, namespace=namespace, data=[event] + data, id=id,
183163
binary=binary))
184-
if wait is True:
185-
try:
186-
await asyncio.wait_for(callback_event.wait(), timeout)
187-
except asyncio.TimeoutError:
188-
six.raise_from(exceptions.TimeoutError(), None)
189-
return callback_args[0] if len(callback_args[0]) > 1 \
190-
else callback_args[0][0] if len(callback_args[0]) == 1 \
191-
else None
192164

193165
async def send(self, data, namespace=None, callback=None, wait=False,
194166
timeout=60):
@@ -223,6 +195,41 @@ async def send(self, data, namespace=None, callback=None, wait=False,
223195
await self.emit('message', data=data, namespace=namespace,
224196
callback=callback, wait=wait, timeout=timeout)
225197

198+
async def call(self, event, data=None, namespace=None, timeout=60):
199+
"""Emit a custom event to a client and wait for the response.
200+
201+
:param event: The event name. It can be any string. The event names
202+
``'connect'``, ``'message'`` and ``'disconnect'`` are
203+
reserved and should not be used.
204+
:param data: The data to send to the client or clients. Data can be of
205+
type ``str``, ``bytes``, ``list`` or ``dict``. If a
206+
``list`` or ``dict``, the data will be serialized as JSON.
207+
:param namespace: The Socket.IO namespace for the event. If this
208+
argument is omitted the event is emitted to the
209+
default namespace.
210+
:param timeout: The waiting timeout. If the timeout is reached before
211+
the client acknowledges the event, then a
212+
``TimeoutError`` exception is raised.
213+
214+
Note: this method is a coroutine.
215+
"""
216+
callback_event = self.eio.create_event()
217+
callback_args = []
218+
219+
def event_callback(*args):
220+
callback_args.append(args)
221+
callback_event.set()
222+
223+
await self.emit(event, data=data, namespace=namespace,
224+
callback=event_callback)
225+
try:
226+
await asyncio.wait_for(callback_event.wait(), timeout)
227+
except asyncio.TimeoutError:
228+
six.raise_from(exceptions.TimeoutError(), None)
229+
return callback_args[0] if len(callback_args[0]) > 1 \
230+
else callback_args[0][0] if len(callback_args[0]) == 1 \
231+
else None
232+
226233
async def disconnect(self):
227234
"""Disconnect from the server.
228235

socketio/asyncio_server.py

+54-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import asyncio
22

33
import engineio
4+
import six
45

56
from . import asyncio_manager
7+
from . import exceptions
68
from . import packet
79
from . import server
810

@@ -26,7 +28,7 @@ class AsyncServer(server.Server):
2628
versions.
2729
:param async_handlers: If set to ``True``, event handlers are executed in
2830
separate threads. To run handlers synchronously,
29-
set to ``False``. The default is ``False``.
31+
set to ``False``. The default is ``True``.
3032
:param kwargs: Connection parameters for the underlying Engine.IO server.
3133
3234
The Engine.IO configuration supports the following settings:
@@ -59,7 +61,7 @@ class AsyncServer(server.Server):
5961
``False``.
6062
"""
6163
def __init__(self, client_manager=None, logger=False, json=None,
62-
async_handlers=False, **kwargs):
64+
async_handlers=True, **kwargs):
6365
if client_manager is None:
6466
client_manager = asyncio_manager.AsyncManager()
6567
super().__init__(client_manager=client_manager, logger=logger,
@@ -112,8 +114,9 @@ async def emit(self, event, data=None, room=None, skip_sid=None,
112114
namespace = namespace or '/'
113115
self.logger.info('emitting event "%s" to %s [%s]', event,
114116
room or 'all', namespace)
115-
await self.manager.emit(event, data, namespace, room, skip_sid,
116-
callback, **kwargs)
117+
await self.manager.emit(event, data, namespace, room=room,
118+
skip_sid=skip_sid, callback=callback,
119+
**kwargs)
117120

118121
async def send(self, data, room=None, skip_sid=None, namespace=None,
119122
callback=None, **kwargs):
@@ -151,9 +154,54 @@ async def send(self, data, room=None, skip_sid=None, namespace=None,
151154
152155
Note: this method is a coroutine.
153156
"""
154-
await self.emit('message', data, room, skip_sid, namespace, callback,
155-
**kwargs)
157+
await self.emit('message', data=data, room=room, skip_sid=skip_sid,
158+
namespace=namespace, callback=callback, **kwargs)
156159

160+
async def call(self, event, data=None, sid=None, namespace=None,
161+
timeout=60, **kwargs):
162+
"""Emit a custom event to a client and wait for the response.
163+
164+
:param event: The event name. It can be any string. The event names
165+
``'connect'``, ``'message'`` and ``'disconnect'`` are
166+
reserved and should not be used.
167+
:param data: The data to send to the client or clients. Data can be of
168+
type ``str``, ``bytes``, ``list`` or ``dict``. If a
169+
``list`` or ``dict``, the data will be serialized as JSON.
170+
:param sid: The session ID of the recipient client.
171+
:param namespace: The Socket.IO namespace for the event. If this
172+
argument is omitted the event is emitted to the
173+
default namespace.
174+
:param timeout: The waiting timeout. If the timeout is reached before
175+
the client acknowledges the event, then a
176+
``TimeoutError`` exception is raised.
177+
:param ignore_queue: Only used when a message queue is configured. If
178+
set to ``True``, the event is emitted to the
179+
client directly, without going through the queue.
180+
This is more efficient, but only works when a
181+
single server process is used. It is recommended
182+
to always leave this parameter with its default
183+
value of ``False``.
184+
"""
185+
if not self.async_handlers:
186+
raise RuntimeError(
187+
'Cannot use call() when async_handlers is False.')
188+
callback_event = self.eio.create_event()
189+
callback_args = []
190+
191+
def event_callback(*args):
192+
callback_args.append(args)
193+
callback_event.set()
194+
195+
await self.emit(event, data=data, room=sid, namespace=namespace,
196+
callback=event_callback, **kwargs)
197+
try:
198+
await asyncio.wait_for(callback_event.wait(), timeout)
199+
except asyncio.TimeoutError:
200+
six.raise_from(exceptions.TimeoutError(), None)
201+
return callback_args[0] if len(callback_args[0]) > 1 \
202+
else callback_args[0][0] if len(callback_args[0]) == 1 \
203+
else None
204+
157205
async def close_room(self, room, namespace=None):
158206
"""Close a room.
159207

socketio/client.py

+32-27
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,7 @@ def wait(self):
224224
if self.eio.state != 'connected':
225225
break
226226

227-
def emit(self, event, data=None, namespace=None, callback=None,
228-
wait=False, timeout=60):
227+
def emit(self, event, data=None, namespace=None, callback=None):
229228
"""Emit a custom event to one or more connected clients.
230229
231230
:param event: The event name. It can be any string. The event names
@@ -242,28 +241,9 @@ def emit(self, event, data=None, namespace=None, callback=None,
242241
that will be passed to the function are those provided
243242
by the client. Callback functions can only be used
244243
when addressing an individual client.
245-
:param wait: If set to ``True``, this function will wait for the
246-
server to handle the event and acknowledge it via its
247-
callback function. The value(s) passed by the server to
248-
its callback will be returned. If set to ``False``,
249-
this function emits the event and returns immediately.
250-
:param timeout: If ``wait`` is set to ``True``, this parameter
251-
specifies a waiting timeout. If the timeout is reached
252-
before the server acknowledges the event, then a
253-
``TimeoutError`` exception is raised.
254244
"""
255245
namespace = namespace or '/'
256246
self.logger.info('Emitting event "%s" [%s]', event, namespace)
257-
if wait is True:
258-
callback_event = self.eio.create_event()
259-
callback_args = []
260-
261-
def event_callback(*args):
262-
callback_args.append(args)
263-
callback_event.set()
264-
265-
callback = event_callback
266-
267247
if callback is not None:
268248
id = self._generate_ack_id(namespace, callback)
269249
else:
@@ -283,12 +263,6 @@ def event_callback(*args):
283263
self._send_packet(packet.Packet(packet.EVENT, namespace=namespace,
284264
data=[event] + data, id=id,
285265
binary=binary))
286-
if wait is True:
287-
if not callback_event.wait(timeout=timeout):
288-
raise exceptions.TimeoutError()
289-
return callback_args[0] if len(callback_args[0]) > 1 \
290-
else callback_args[0][0] if len(callback_args[0]) == 1 \
291-
else None
292266

293267
def send(self, data, namespace=None, callback=None, wait=False,
294268
timeout=60):
@@ -321,6 +295,37 @@ def send(self, data, namespace=None, callback=None, wait=False,
321295
self.emit('message', data=data, namespace=namespace,
322296
callback=callback, wait=wait, timeout=timeout)
323297

298+
def call(self, event, data=None, namespace=None, timeout=60):
299+
"""Emit a custom event to a client and wait for the response.
300+
301+
:param event: The event name. It can be any string. The event names
302+
``'connect'``, ``'message'`` and ``'disconnect'`` are
303+
reserved and should not be used.
304+
:param data: The data to send to the client or clients. Data can be of
305+
type ``str``, ``bytes``, ``list`` or ``dict``. If a
306+
``list`` or ``dict``, the data will be serialized as JSON.
307+
:param namespace: The Socket.IO namespace for the event. If this
308+
argument is omitted the event is emitted to the
309+
default namespace.
310+
:param timeout: The waiting timeout. If the timeout is reached before
311+
the client acknowledges the event, then a
312+
``TimeoutError`` exception is raised.
313+
"""
314+
callback_event = self.eio.create_event()
315+
callback_args = []
316+
317+
def event_callback(*args):
318+
callback_args.append(args)
319+
callback_event.set()
320+
321+
self.emit(event, data=data, namespace=namespace,
322+
callback=event_callback)
323+
if not callback_event.wait(timeout=timeout):
324+
raise exceptions.TimeoutError()
325+
return callback_args[0] if len(callback_args[0]) > 1 \
326+
else callback_args[0][0] if len(callback_args[0]) == 1 \
327+
else None
328+
324329
def disconnect(self):
325330
"""Disconnect from the server."""
326331
for n in self.namespaces:

socketio/server.py

+55-10
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
import six
55

66
from . import base_manager
7-
from . import packet
7+
from . import exceptions
88
from . import namespace
9+
from . import packet
910

1011
default_logger = logging.getLogger('socketio.server')
1112

@@ -33,9 +34,10 @@ class Server(object):
3334
packets. Custom json modules must have ``dumps`` and ``loads``
3435
functions that are compatible with the standard library
3536
versions.
36-
:param async_handlers: If set to ``True``, event handlers are executed in
37-
separate threads. To run handlers synchronously,
38-
set to ``False``. The default is ``False``.
37+
:param async_handlers: If set to ``True``, event handlers for a client are
38+
executed in separate threads. To run handlers for a
39+
client synchronously, set to ``False``. The default
40+
is ``True``.
3941
:param kwargs: Connection parameters for the underlying Engine.IO server.
4042
4143
The Engine.IO configuration supports the following settings:
@@ -77,7 +79,7 @@ class Server(object):
7779
``False``. The default is ``False``.
7880
"""
7981
def __init__(self, client_manager=None, logger=False, binary=False,
80-
json=None, async_handlers=False, **kwargs):
82+
json=None, async_handlers=True, **kwargs):
8183
engineio_options = kwargs
8284
engineio_logger = engineio_options.pop('engineio_logger', None)
8385
if engineio_logger is not None:
@@ -224,11 +226,11 @@ def emit(self, event, data=None, room=None, skip_sid=None, namespace=None,
224226
namespace = namespace or '/'
225227
self.logger.info('emitting event "%s" to %s [%s]', event,
226228
room or 'all', namespace)
227-
self.manager.emit(event, data, namespace, room, skip_sid, callback,
228-
**kwargs)
229+
self.manager.emit(event, data, namespace, room=room,
230+
skip_sid=skip_sid, callback=callback, **kwargs)
229231

230232
def send(self, data, room=None, skip_sid=None, namespace=None,
231-
callback=None, **kwargs):
233+
callback=None, wait=False, timeout=60, **kwargs):
232234
"""Send a message to one or more connected clients.
233235
234236
This function emits an event with the name ``'message'``. Use
@@ -261,8 +263,51 @@ def send(self, data, room=None, skip_sid=None, namespace=None,
261263
to always leave this parameter with its default
262264
value of ``False``.
263265
"""
264-
self.emit('message', data, room, skip_sid, namespace, callback,
265-
**kwargs)
266+
self.emit('message', data=data, room=room, skip_sid=skip_sid,
267+
namespace=namespace, callback=callback, **kwargs)
268+
269+
def call(self, event, data=None, sid=None, namespace=None, timeout=60,
270+
**kwargs):
271+
"""Emit a custom event to a client and wait for the response.
272+
273+
:param event: The event name. It can be any string. The event names
274+
``'connect'``, ``'message'`` and ``'disconnect'`` are
275+
reserved and should not be used.
276+
:param data: The data to send to the client or clients. Data can be of
277+
type ``str``, ``bytes``, ``list`` or ``dict``. If a
278+
``list`` or ``dict``, the data will be serialized as JSON.
279+
:param sid: The session ID of the recipient client.
280+
:param namespace: The Socket.IO namespace for the event. If this
281+
argument is omitted the event is emitted to the
282+
default namespace.
283+
:param timeout: The waiting timeout. If the timeout is reached before
284+
the client acknowledges the event, then a
285+
``TimeoutError`` exception is raised.
286+
:param ignore_queue: Only used when a message queue is configured. If
287+
set to ``True``, the event is emitted to the
288+
client directly, without going through the queue.
289+
This is more efficient, but only works when a
290+
single server process is used. It is recommended
291+
to always leave this parameter with its default
292+
value of ``False``.
293+
"""
294+
if not self.async_handlers:
295+
raise RuntimeError(
296+
'Cannot use call() when async_handlers is False.')
297+
callback_event = self.eio.create_event()
298+
callback_args = []
299+
300+
def event_callback(*args):
301+
callback_args.append(args)
302+
callback_event.set()
303+
304+
self.emit(event, data=data, room=sid, namespace=namespace,
305+
callback=event_callback, **kwargs)
306+
if not callback_event.wait(timeout=timeout):
307+
raise exceptions.TimeoutError()
308+
return callback_args[0] if len(callback_args[0]) > 1 \
309+
else callback_args[0][0] if len(callback_args[0]) == 1 \
310+
else None
266311

267312
def enter_room(self, sid, room, namespace=None):
268313
"""Enter a room.

0 commit comments

Comments
 (0)