Skip to content

Commit 70ebfdb

Browse files
New @event decorator for handler registration
1 parent aaa87a8 commit 70ebfdb

File tree

7 files changed

+219
-87
lines changed

7 files changed

+219
-87
lines changed

docs/client.rst

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -40,45 +40,51 @@ appropriate client class::
4040
Defining Event Handlers
4141
-----------------------
4242

43-
To responds to events triggered by the connection or the server, event Handler
44-
functions must be defined using the ``on`` decorator::
43+
The Socket.IO protocol is event based. When a server wants to communicate with
44+
a client it *emits* an event. Each event has a name, and a list of
45+
arguments. The client registers event handler functions with the
46+
:func:`socketio.Client.event` or :func:`socketio.Client.on` decorators::
4547

46-
@sio.on('connect')
47-
def on_connect():
48-
print('I\'m connected!')
49-
50-
@sio.on('message')
51-
def on_message(data):
48+
@sio.event
49+
def message(data):
5250
print('I received a message!')
5351

5452
@sio.on('my message')
5553
def on_message(data):
56-
print('I received a custom message!')
54+
print('I received a message!')
5755

58-
@sio.on('disconnect')
59-
def on_disconnect():
60-
print('I\'m disconnected!')
56+
In the first example the event name is obtained from the name of the
57+
handler function. The second example is slightly more verbose, but it
58+
allows the event name to be different than the function name or to include
59+
characters that are illegal in function names, such as spaces.
6160

62-
For the ``asyncio`` server, event handlers can be regular functions as above,
61+
For the ``asyncio`` client, event handlers can be regular functions as above,
6362
or can also be coroutines::
6463

65-
@sio.on('message')
66-
async def on_message(data):
64+
@sio.event
65+
async def message(data):
6766
print('I received a message!')
6867

69-
The argument given to the ``on`` decorator is the event name. The predefined
70-
events that are supported are ``connect``, ``message`` and ``disconnect``. The
71-
application can define any other desired event names.
68+
The ``connect`` and ``disconnect`` events are special; they are invoked
69+
automatically when a client connects or disconnects from the server::
70+
71+
@sio.event
72+
def connect():
73+
print("I'm connected!")
74+
75+
@sio.event
76+
def disconnect():
77+
print("I'm disconnected!")
7278

7379
Note that the ``disconnect`` handler is invoked for application initiated
7480
disconnects, server initiated disconnects, or accidental disconnects, for
75-
example due to networking failures. In the case of an accidental disconnection,
76-
the client is going to attempt to reconnect immediately after invoking the
77-
disconnect handler. As soon as the connection is re-established the connect
78-
handler will be invoked once again.
81+
example due to networking failures. In the case of an accidental
82+
disconnection, the client is going to attempt to reconnect immediately after
83+
invoking the disconnect handler. As soon as the connection is re-established
84+
the connect handler will be invoked once again.
7985

80-
The ``data`` argument passed to the ``'message'`` and custom event Handlers
81-
contains application-specific data provided by the server.
86+
If the server includes arguments with an event, those are passed to the
87+
handler function as arguments.
8288

8389
Connecting to a Server
8490
----------------------
@@ -109,24 +115,15 @@ Or in the case of ``asyncio``, as a coroutine::
109115
await sio.emit('my message', {'foo': 'bar'})
110116

111117
The single argument provided to the method is the data that is passed on
112-
to the server. The data can be of type ``str``, ``bytes``, ``dict`` or
113-
``list``. The data included inside dictionaries and lists is also
114-
constrained to these types.
118+
to the server. The data can be of type ``str``, ``bytes``, ``dict``,
119+
``list`` or ``tuple``. When sending a ``tuple``, the elements in it need to
120+
be of any of the other four allowed types. The elements of the tuple will be
121+
passed as multiple arguments to the server-side event handler function.
115122

116123
The ``emit()`` method can be invoked inside an event handler as a response
117124
to a server event, or in any other part of the application, including in
118125
background tasks.
119126

120-
For convenience, a ``send()`` method is also provided. This method accepts
121-
a data element as its only argument, and emits the standard ``message``
122-
event with it::
123-
124-
sio.send('some data')
125-
126-
In the case of ``asyncio``, ``send()`` is a coroutine::
127-
128-
await sio.send('some data')
129-
130127
Event Callbacks
131128
---------------
132129

@@ -137,8 +134,8 @@ client can provide a list of return values that are to be passed on to the
137134
callback function set up by the server. This is achieves simply by returning
138135
the desired values from the handler function::
139136

140-
@sio.on('my event', namespace='/chat')
141-
def my_event_handler(sid, data):
137+
@sio.event
138+
def my_event(sid, data):
142139
# handle the message
143140
return "OK", 123
144141

@@ -163,11 +160,15 @@ namespace::
163160
sio.connect('http://localhost:5000', namespaces=['/chat'])
164161

165162
To define event handlers on a namespace, the ``namespace`` argument must be
166-
added to the ``on`` decorator::
163+
added to the corresponding decorator::
164+
165+
@sio.event(namespace='/chat')
166+
def my_custom_event(sid, data):
167+
pass
167168

168169
@sio.on('connect', namespace='/chat')
169170
def on_connect():
170-
print('I\'m connected to the /chat namespace!')
171+
print("I'm connected to the /chat namespace!")
171172

172173
Likewise, the client can emit an event to the server on a namespace by
173174
providing its in the ``emit()`` call::

docs/intro.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ The example that follows shows a simple Python client:
2626
2727
sio = socketio.Client()
2828
29-
@sio.on('connect')
30-
def on_connect():
29+
@sio.event
30+
def connect():
3131
print('connection established')
3232
33-
@sio.on('my message')
34-
def on_message(data):
33+
@sio.event
34+
def my_message(data):
3535
print('message received with ', data)
3636
sio.emit('my response', {'response': 'my response'})
3737
38-
@sio.on('disconnect')
39-
def on_disconnect():
38+
@sio.event
39+
def disconnect():
4040
print('disconnected from server')
4141
4242
sio.connect('http://localhost:5000')
@@ -71,15 +71,15 @@ asynchronous server:
7171
'/': {'content_type': 'text/html', 'filename': 'index.html'}
7272
})
7373
74-
@sio.on('connect')
74+
@sio.event
7575
def connect(sid, environ):
7676
print('connect ', sid)
7777
78-
@sio.on('my message')
79-
def message(sid, data):
78+
@sio.event
79+
def my_message(sid, data):
8080
print('message ', data)
8181
82-
@sio.on('disconnect')
82+
@sio.event
8383
def disconnect(sid):
8484
print('disconnect ', sid)
8585
@@ -103,16 +103,16 @@ Uvicorn web server:
103103
with open('index.html') as f:
104104
return web.Response(text=f.read(), content_type='text/html')
105105
106-
@sio.on('connect', namespace='/chat')
106+
@sio.event
107107
def connect(sid, environ):
108108
print("connect ", sid)
109109
110-
@sio.on('chat message', namespace='/chat')
111-
async def message(sid, data):
110+
@sio.event
111+
async def chat_message(sid, data):
112112
print("message ", data)
113113
await sio.emit('reply', room=sid)
114114
115-
@sio.on('disconnect', namespace='/chat')
115+
@sio.event
116116
def disconnect(sid):
117117
print('disconnect ', sid)
118118

docs/server.rst

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -135,30 +135,39 @@ Defining Event Handlers
135135
The Socket.IO protocol is event based. When a client wants to communicate with
136136
the server it *emits* an event. Each event has a name, and a list of
137137
arguments. The server registers event handler functions with the
138-
:func:`socketio.Server.on` decorator::
138+
:func:`socketio.Server.event` or :func:`socketio.Server.on` decorators::
139+
140+
@sio.event
141+
def my_event(sid, data):
142+
pass
139143

140144
@sio.on('my custom event')
141-
def my_custom_event(sid, data):
145+
def another_event(sid, data):
142146
pass
143147

148+
In the first example the event name is obtained from the name of the handler
149+
function. The second example is slightly more verbose, but it allows the event
150+
name to be different than the function name or to include characters that are
151+
illegal in function names, such as spaces.
152+
144153
For asyncio servers, event handlers can optionally be given as coroutines::
145154

146-
@sio.on('my custom event')
147-
async def my_custom_event(sid, data):
155+
@sio.event
156+
async def my_event(sid, data):
148157
pass
149158

150159
The ``sid`` argument is the Socket.IO session id, a unique identifier of each
151160
client connection. All the events sent by a given client will have the same
152161
``sid`` value.
153162

154-
The ``connect`` and ``disconnect`` are special; they are invoked automatically
155-
when a client connects or disconnects from the server::
163+
The ``connect`` and ``disconnect`` events are special; they are invoked
164+
automatically when a client connects or disconnects from the server::
156165

157-
@sio.on('connect')
166+
@sio.event
158167
def connect(sid, environ):
159168
print('connect ', sid)
160169

161-
@sio.on('disconnect')
170+
@sio.event
162171
def disconnect(sid):
163172
print('disconnect ', sid)
164173

@@ -172,9 +181,9 @@ headers. After inspecting the request, the connect event handler can return
172181
Sometimes it is useful to pass data back to the client being rejected. In that
173182
case instead of returning ``False``
174183
:class:`socketio.exceptions.ConnectionRefusedError` can be raised, and all of
175-
its argument will be sent to the client with the rejection::
184+
its arguments will be sent to the client with the rejection message::
176185

177-
@sio.on('connect')
186+
@sio.event
178187
def connect(sid, environ):
179188
raise ConnectionRefusedError('authentication failed')
180189

@@ -210,8 +219,8 @@ has processed the event. While this is entirely managed by the client, the
210219
server can provide a list of values that are to be passed on to the callback
211220
function, simply by returning them from the handler function::
212221

213-
@sio.on('my event', namespace='/chat')
214-
def my_event_handler(sid, data):
222+
@sio.event
223+
def my_event(sid, data):
215224
# handle the message
216225
return "OK", 123
217226

@@ -240,6 +249,10 @@ that use multiple namespaces specify the correct namespace when setting up
240249
their event handlers and rooms, using the optional ``namespace`` argument
241250
available in all the methods in the :class:`socketio.Server` class::
242251

252+
@sio.event(namespace='/chat')
253+
def my_custom_event(sid, data):
254+
pass
255+
243256
@sio.on('my custom event', namespace='/chat')
244257
def my_custom_event(sid, data):
245258
pass
@@ -322,11 +335,11 @@ rooms as needed and can be moved between rooms as often as necessary.
322335

323336
::
324337

325-
@sio.on('chat')
338+
@sio.event
326339
def begin_chat(sid):
327340
sio.enter_room(sid, 'chat_users')
328341

329-
@sio.on('exit_chat')
342+
@sio.event
330343
def exit_chat(sid):
331344
sio.leave_room(sid, 'chat_users')
332345

@@ -338,8 +351,8 @@ during the broadcast.
338351

339352
::
340353

341-
@sio.on('my message')
342-
def message(sid, data):
354+
@sio.event
355+
def my_message(sid, data):
343356
sio.emit('my reply', data, room='chat_users', skip_sid=sid)
344357

345358
User Sessions
@@ -353,52 +366,52 @@ of the connection, such as usernames or user ids.
353366
The ``save_session()`` and ``get_session()`` methods are used to store and
354367
retrieve information in the user session::
355368

356-
@sio.on('connect')
357-
def on_connect(sid, environ):
369+
@sio.event
370+
def connect(sid, environ):
358371
username = authenticate_user(environ)
359372
sio.save_session(sid, {'username': username})
360373

361-
@sio.on('message')
362-
def on_message(sid, data):
374+
@sio.event
375+
def message(sid, data):
363376
session = sio.get_session(sid)
364377
print('message from ', session['username'])
365378

366379
For the ``asyncio`` server, these methods are coroutines::
367380

368381

369-
@sio.on('connect')
370-
async def on_connect(sid, environ):
382+
@sio.event
383+
async def connect(sid, environ):
371384
username = authenticate_user(environ)
372385
await sio.save_session(sid, {'username': username})
373386

374-
@sio.on('message')
375-
async def on_message(sid, data):
387+
@sio.event
388+
async def message(sid, data):
376389
session = await sio.get_session(sid)
377390
print('message from ', session['username'])
378391

379392
The session can also be manipulated with the `session()` context manager::
380393

381-
@sio.on('connect')
382-
def on_connect(sid, environ):
394+
@sio.event
395+
def connect(sid, environ):
383396
username = authenticate_user(environ)
384397
with sio.session(sid) as session:
385398
session['username'] = username
386399

387-
@sio.on('message')
388-
def on_message(sid, data):
400+
@sio.event
401+
def message(sid, data):
389402
with sio.session(sid) as session:
390403
print('message from ', session['username'])
391404

392405
For the ``asyncio`` server, an asynchronous context manager is used::
393406

394-
@sio.on('connect')
395-
def on_connect(sid, environ):
407+
@sio.event
408+
def connect(sid, environ):
396409
username = authenticate_user(environ)
397410
async with sio.session(sid) as session:
398411
session['username'] = username
399412

400-
@sio.on('message')
401-
def on_message(sid, data):
413+
@sio.event
414+
def message(sid, data):
402415
async with sio.session(sid) as session:
403416
print('message from ', session['username'])
404417

0 commit comments

Comments
 (0)