python-socketio 5.12.0__tar.gz → 5.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_socketio-5.12.0/src/python_socketio.egg-info → python_socketio-5.13.0}/PKG-INFO +3 -2
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/intro.rst +1 -1
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/server.rst +1 -7
- {python_socketio-5.12.0 → python_socketio-5.13.0}/pyproject.toml +1 -1
- {python_socketio-5.12.0 → python_socketio-5.13.0/src/python_socketio.egg-info}/PKG-INFO +3 -2
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/SOURCES.txt +1 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/admin.py +40 -54
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_admin.py +40 -54
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_client.py +3 -2
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_redis_manager.py +17 -5
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_simple_client.py +4 -1
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/client.py +2 -2
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/redis_manager.py +43 -5
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/simple_client.py +4 -1
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_client.py +2 -2
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_simple_client.py +33 -28
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_client.py +2 -2
- python_socketio-5.13.0/tests/common/test_redis_manager.py +38 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_simple_client.py +27 -17
- {python_socketio-5.12.0 → python_socketio-5.13.0}/LICENSE +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/MANIFEST.in +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/README.md +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/Makefile +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/_static/README.md +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/_static/custom.css +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/api.rst +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/client.rst +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/conf.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/index.rst +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/docs/make.bat +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/setup.cfg +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/dependency_links.txt +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/not-zip-safe +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/requires.txt +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/top_level.txt +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/__init__.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/asgi.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_aiopika_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_namespace.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_pubsub_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/async_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/base_client.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/base_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/base_namespace.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/base_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/exceptions.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/kafka_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/kombu_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/middleware.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/msgpack_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/namespace.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/pubsub_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/tornado.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/src/socketio/zmq_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/__init__.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/__init__.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_admin.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_namespace.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_pubsub_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/async/test_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/asyncio_web_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/__init__.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_admin.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_middleware.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_msgpack_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_namespace.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_pubsub_manager.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/common/test_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/README.md +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/binary_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/json_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/namespace_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/run.sh +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/server_receive.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/server_send.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/server_send_broadcast.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/text_packet.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tests/web_server.py +0 -0
- {python_socketio-5.12.0 → python_socketio-5.13.0}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-socketio
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.13.0
|
|
4
4
|
Summary: Socket.IO server and client for Python
|
|
5
5
|
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/miguelgrinberg/python-socketio
|
|
@@ -22,6 +22,7 @@ Provides-Extra: asyncio-client
|
|
|
22
22
|
Requires-Dist: aiohttp>=3.4; extra == "asyncio-client"
|
|
23
23
|
Provides-Extra: docs
|
|
24
24
|
Requires-Dist: sphinx; extra == "docs"
|
|
25
|
+
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
python-socketio
|
|
27
28
|
===============
|
|
@@ -189,7 +189,7 @@ Server Features
|
|
|
189
189
|
- Can be hosted on any `WSGI <https://wsgi.readthedocs.io/en/latest/index.html>`_ or
|
|
190
190
|
`ASGI <https://asgi.readthedocs.io/en/latest/>`_ web server including
|
|
191
191
|
`Gunicorn <https://gunicorn.org/>`_, `Uvicorn <https://github.com/encode/uvicorn>`_,
|
|
192
|
-
`eventlet <http://eventlet.net/>`_ and `gevent <http://gevent.org>`_.
|
|
192
|
+
`eventlet <http://eventlet.net/>`_ and `gevent <http://www.gevent.org>`_.
|
|
193
193
|
- Can be integrated with WSGI applications written in frameworks such as Flask, Django,
|
|
194
194
|
etc.
|
|
195
195
|
- Can be integrated with `aiohttp <http://aiohttp.readthedocs.io/>`_,
|
|
@@ -19,12 +19,6 @@ command::
|
|
|
19
19
|
|
|
20
20
|
pip install python-socketio
|
|
21
21
|
|
|
22
|
-
If you plan to build an asynchronous web server based on the ``asyncio``
|
|
23
|
-
package, then you can install this package and some additional dependencies
|
|
24
|
-
that are needed with::
|
|
25
|
-
|
|
26
|
-
pip install "python-socketio[asyncio]"
|
|
27
|
-
|
|
28
22
|
Creating a Server Instance
|
|
29
23
|
--------------------------
|
|
30
24
|
|
|
@@ -838,7 +832,7 @@ Gevent
|
|
|
838
832
|
|
|
839
833
|
When a multi-threaded web server is unable to satisfy the concurrency and
|
|
840
834
|
scalability requirements of the application, an option to try is
|
|
841
|
-
`Gevent <http://gevent.org>`_. Gevent is a coroutine-based concurrency library
|
|
835
|
+
`Gevent <http://www.gevent.org>`_. Gevent is a coroutine-based concurrency library
|
|
842
836
|
based on greenlets, which are significantly lighter than threads.
|
|
843
837
|
|
|
844
838
|
Instances of class ``socketio.Server`` will automatically use Gevent if the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-socketio
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.13.0
|
|
4
4
|
Summary: Socket.IO server and client for Python
|
|
5
5
|
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/miguelgrinberg/python-socketio
|
|
@@ -22,6 +22,7 @@ Provides-Extra: asyncio-client
|
|
|
22
22
|
Requires-Dist: aiohttp>=3.4; extra == "asyncio-client"
|
|
23
23
|
Provides-Extra: docs
|
|
24
24
|
Requires-Dist: sphinx; extra == "docs"
|
|
25
|
+
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
python-socketio
|
|
27
28
|
===============
|
|
@@ -70,6 +70,7 @@ tests/common/test_msgpack_packet.py
|
|
|
70
70
|
tests/common/test_namespace.py
|
|
71
71
|
tests/common/test_packet.py
|
|
72
72
|
tests/common/test_pubsub_manager.py
|
|
73
|
+
tests/common/test_redis_manager.py
|
|
73
74
|
tests/common/test_server.py
|
|
74
75
|
tests/common/test_simple_client.py
|
|
75
76
|
tests/performance/README.md
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from datetime import datetime
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
2
|
import functools
|
|
3
3
|
import os
|
|
4
4
|
import socket
|
|
@@ -77,13 +77,9 @@ class InstrumentedServer:
|
|
|
77
77
|
# track socket connection times
|
|
78
78
|
self.sio.manager._timestamps = {}
|
|
79
79
|
|
|
80
|
-
# report socket.io connections
|
|
81
|
-
self.sio.
|
|
82
|
-
self.sio.
|
|
83
|
-
|
|
84
|
-
# report socket.io disconnection
|
|
85
|
-
self.sio.manager.__disconnect = self.sio.manager.disconnect
|
|
86
|
-
self.sio.manager.disconnect = self._disconnect
|
|
80
|
+
# report socket.io connections, disconnections and received events
|
|
81
|
+
self.sio.__trigger_event = self.sio._trigger_event
|
|
82
|
+
self.sio._trigger_event = self._trigger_event
|
|
87
83
|
|
|
88
84
|
# report join rooms
|
|
89
85
|
self.sio.manager.__basic_enter_room = \
|
|
@@ -99,10 +95,6 @@ class InstrumentedServer:
|
|
|
99
95
|
self.sio.manager.__emit = self.sio.manager.emit
|
|
100
96
|
self.sio.manager.emit = self._emit
|
|
101
97
|
|
|
102
|
-
# report receive events
|
|
103
|
-
self.sio.__handle_event_internal = self.sio._handle_event_internal
|
|
104
|
-
self.sio._handle_event_internal = self._handle_event_internal
|
|
105
|
-
|
|
106
98
|
# report engine.io connections
|
|
107
99
|
self.sio.eio.on('connect', self._handle_eio_connect)
|
|
108
100
|
self.sio.eio.on('disconnect', self._handle_eio_disconnect)
|
|
@@ -128,14 +120,12 @@ class InstrumentedServer:
|
|
|
128
120
|
|
|
129
121
|
def uninstrument(self): # pragma: no cover
|
|
130
122
|
if self.mode == 'development':
|
|
131
|
-
self.sio.
|
|
132
|
-
self.sio.manager.disconnect = self.sio.manager.__disconnect
|
|
123
|
+
self.sio._trigger_event = self.sio.__trigger_event
|
|
133
124
|
self.sio.manager.basic_enter_room = \
|
|
134
125
|
self.sio.manager.__basic_enter_room
|
|
135
126
|
self.sio.manager.basic_leave_room = \
|
|
136
127
|
self.sio.manager.__basic_leave_room
|
|
137
128
|
self.sio.manager.emit = self.sio.manager.__emit
|
|
138
|
-
self.sio._handle_event_internal = self.sio.__handle_event_internal
|
|
139
129
|
self.sio.eio._ok = self.sio.eio.__ok
|
|
140
130
|
|
|
141
131
|
from engineio.socket import Socket
|
|
@@ -205,26 +195,34 @@ class InstrumentedServer:
|
|
|
205
195
|
self.stop_stats_event.set()
|
|
206
196
|
self.stats_task.join()
|
|
207
197
|
|
|
208
|
-
def
|
|
209
|
-
sid = self.sio.manager.__connect(eio_sid, namespace)
|
|
198
|
+
def _trigger_event(self, event, namespace, *args):
|
|
210
199
|
t = time.time()
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
200
|
+
sid = args[0]
|
|
201
|
+
if event == 'connect':
|
|
202
|
+
eio_sid = self.sio.manager.eio_sid_from_sid(sid, namespace)
|
|
203
|
+
self.sio.manager._timestamps[sid] = t
|
|
204
|
+
serialized_socket = self.serialize_socket(sid, namespace, eio_sid)
|
|
205
|
+
self.sio.emit('socket_connected', (
|
|
206
|
+
serialized_socket,
|
|
207
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
208
|
+
), namespace=self.admin_namespace)
|
|
209
|
+
elif event == 'disconnect':
|
|
210
|
+
del self.sio.manager._timestamps[sid]
|
|
211
|
+
reason = args[1]
|
|
212
|
+
self.sio.emit('socket_disconnected', (
|
|
213
|
+
namespace,
|
|
214
|
+
sid,
|
|
215
|
+
reason,
|
|
216
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
217
|
+
), namespace=self.admin_namespace)
|
|
218
|
+
else:
|
|
219
|
+
self.sio.emit('event_received', (
|
|
220
|
+
namespace,
|
|
221
|
+
sid,
|
|
222
|
+
(event, *args[1:]),
|
|
223
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
224
|
+
), namespace=self.admin_namespace)
|
|
225
|
+
return self.sio.__trigger_event(event, namespace, *args)
|
|
228
226
|
|
|
229
227
|
def _check_for_upgrade(self, eio_sid, sid, namespace): # pragma: no cover
|
|
230
228
|
for _ in range(5):
|
|
@@ -248,7 +246,7 @@ class InstrumentedServer:
|
|
|
248
246
|
namespace,
|
|
249
247
|
room,
|
|
250
248
|
sid,
|
|
251
|
-
datetime.
|
|
249
|
+
datetime.now(timezone.utc).isoformat(),
|
|
252
250
|
), namespace=self.admin_namespace)
|
|
253
251
|
return ret
|
|
254
252
|
|
|
@@ -258,7 +256,7 @@ class InstrumentedServer:
|
|
|
258
256
|
namespace,
|
|
259
257
|
room,
|
|
260
258
|
sid,
|
|
261
|
-
datetime.
|
|
259
|
+
datetime.now(timezone.utc).isoformat(),
|
|
262
260
|
), namespace=self.admin_namespace)
|
|
263
261
|
return self.sio.manager.__basic_leave_room(sid, namespace, room)
|
|
264
262
|
|
|
@@ -269,7 +267,7 @@ class InstrumentedServer:
|
|
|
269
267
|
**kwargs)
|
|
270
268
|
if namespace != self.admin_namespace:
|
|
271
269
|
event_data = [event] + list(data) if isinstance(data, tuple) \
|
|
272
|
-
else [data]
|
|
270
|
+
else [event, data]
|
|
273
271
|
if not isinstance(skip_sid, list): # pragma: no branch
|
|
274
272
|
skip_sid = [skip_sid]
|
|
275
273
|
for sid, _ in self.sio.manager.get_participants(namespace, room):
|
|
@@ -278,22 +276,10 @@ class InstrumentedServer:
|
|
|
278
276
|
namespace,
|
|
279
277
|
sid,
|
|
280
278
|
event_data,
|
|
281
|
-
datetime.
|
|
279
|
+
datetime.now(timezone.utc).isoformat(),
|
|
282
280
|
), namespace=self.admin_namespace)
|
|
283
281
|
return ret
|
|
284
282
|
|
|
285
|
-
def _handle_event_internal(self, server, sid, eio_sid, data, namespace,
|
|
286
|
-
id):
|
|
287
|
-
ret = self.sio.__handle_event_internal(server, sid, eio_sid, data,
|
|
288
|
-
namespace, id)
|
|
289
|
-
self.sio.emit('event_received', (
|
|
290
|
-
namespace,
|
|
291
|
-
sid,
|
|
292
|
-
data,
|
|
293
|
-
datetime.utcnow().isoformat() + 'Z',
|
|
294
|
-
), namespace=self.admin_namespace)
|
|
295
|
-
return ret
|
|
296
|
-
|
|
297
283
|
def _handle_eio_connect(self, eio_sid, environ):
|
|
298
284
|
if self.stop_stats_event is None:
|
|
299
285
|
self.stop_stats_event = self.sio.eio.create_event()
|
|
@@ -303,9 +289,9 @@ class InstrumentedServer:
|
|
|
303
289
|
self.event_buffer.push('rawConnection')
|
|
304
290
|
return self.sio._handle_eio_connect(eio_sid, environ)
|
|
305
291
|
|
|
306
|
-
def _handle_eio_disconnect(self, eio_sid):
|
|
292
|
+
def _handle_eio_disconnect(self, eio_sid, reason):
|
|
307
293
|
self.event_buffer.push('rawDisconnection')
|
|
308
|
-
return self.sio._handle_eio_disconnect(eio_sid)
|
|
294
|
+
return self.sio._handle_eio_disconnect(eio_sid, reason)
|
|
309
295
|
|
|
310
296
|
def _eio_http_response(self, packets=None, headers=None, jsonp_index=None):
|
|
311
297
|
ret = self.sio.eio.__ok(packets=packets, headers=headers,
|
|
@@ -349,7 +335,7 @@ class InstrumentedServer:
|
|
|
349
335
|
eio_sid)
|
|
350
336
|
self.sio.emit('socket_connected', (
|
|
351
337
|
serialized_socket,
|
|
352
|
-
datetime.
|
|
338
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
353
339
|
), namespace=self.admin_namespace)
|
|
354
340
|
return socket.__send_ping()
|
|
355
341
|
|
|
@@ -398,7 +384,7 @@ class InstrumentedServer:
|
|
|
398
384
|
'secure': environ.get('wsgi.url_scheme', '') == 'https',
|
|
399
385
|
'url': environ.get('PATH_INFO', ''),
|
|
400
386
|
'issued': tm * 1000,
|
|
401
|
-
'time': datetime.
|
|
387
|
+
'time': datetime.fromtimestamp(tm, timezone.utc).isoformat()
|
|
402
388
|
if tm else '',
|
|
403
389
|
},
|
|
404
390
|
'rooms': self.sio.manager.get_rooms(sid, namespace),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
import functools
|
|
4
4
|
import os
|
|
5
5
|
import socket
|
|
@@ -58,13 +58,9 @@ class InstrumentedAsyncServer:
|
|
|
58
58
|
# track socket connection times
|
|
59
59
|
self.sio.manager._timestamps = {}
|
|
60
60
|
|
|
61
|
-
# report socket.io connections
|
|
62
|
-
self.sio.
|
|
63
|
-
self.sio.
|
|
64
|
-
|
|
65
|
-
# report socket.io disconnection
|
|
66
|
-
self.sio.manager.__disconnect = self.sio.manager.disconnect
|
|
67
|
-
self.sio.manager.disconnect = self._disconnect
|
|
61
|
+
# report socket.io connections, disconnections and received events
|
|
62
|
+
self.sio.__trigger_event = self.sio._trigger_event
|
|
63
|
+
self.sio._trigger_event = self._trigger_event
|
|
68
64
|
|
|
69
65
|
# report join rooms
|
|
70
66
|
self.sio.manager.__basic_enter_room = \
|
|
@@ -80,10 +76,6 @@ class InstrumentedAsyncServer:
|
|
|
80
76
|
self.sio.manager.__emit = self.sio.manager.emit
|
|
81
77
|
self.sio.manager.emit = self._emit
|
|
82
78
|
|
|
83
|
-
# report receive events
|
|
84
|
-
self.sio.__handle_event_internal = self.sio._handle_event_internal
|
|
85
|
-
self.sio._handle_event_internal = self._handle_event_internal
|
|
86
|
-
|
|
87
79
|
# report engine.io connections
|
|
88
80
|
self.sio.eio.on('connect', self._handle_eio_connect)
|
|
89
81
|
self.sio.eio.on('disconnect', self._handle_eio_disconnect)
|
|
@@ -109,14 +101,12 @@ class InstrumentedAsyncServer:
|
|
|
109
101
|
|
|
110
102
|
def uninstrument(self): # pragma: no cover
|
|
111
103
|
if self.mode == 'development':
|
|
112
|
-
self.sio.
|
|
113
|
-
self.sio.manager.disconnect = self.sio.manager.__disconnect
|
|
104
|
+
self.sio._trigger_event = self.sio.__trigger_event
|
|
114
105
|
self.sio.manager.basic_enter_room = \
|
|
115
106
|
self.sio.manager.__basic_enter_room
|
|
116
107
|
self.sio.manager.basic_leave_room = \
|
|
117
108
|
self.sio.manager.__basic_leave_room
|
|
118
109
|
self.sio.manager.emit = self.sio.manager.__emit
|
|
119
|
-
self.sio._handle_event_internal = self.sio.__handle_event_internal
|
|
120
110
|
self.sio.eio._ok = self.sio.eio.__ok
|
|
121
111
|
|
|
122
112
|
from engineio.async_socket import AsyncSocket
|
|
@@ -193,26 +183,34 @@ class InstrumentedAsyncServer:
|
|
|
193
183
|
self.stop_stats_event.set()
|
|
194
184
|
await asyncio.gather(self.stats_task)
|
|
195
185
|
|
|
196
|
-
async def
|
|
197
|
-
sid = await self.sio.manager.__connect(eio_sid, namespace)
|
|
186
|
+
async def _trigger_event(self, event, namespace, *args):
|
|
198
187
|
t = time.time()
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
188
|
+
sid = args[0]
|
|
189
|
+
if event == 'connect':
|
|
190
|
+
eio_sid = self.sio.manager.eio_sid_from_sid(sid, namespace)
|
|
191
|
+
self.sio.manager._timestamps[sid] = t
|
|
192
|
+
serialized_socket = self.serialize_socket(sid, namespace, eio_sid)
|
|
193
|
+
await self.sio.emit('socket_connected', (
|
|
194
|
+
serialized_socket,
|
|
195
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
196
|
+
), namespace=self.admin_namespace)
|
|
197
|
+
elif event == 'disconnect':
|
|
198
|
+
del self.sio.manager._timestamps[sid]
|
|
199
|
+
reason = args[1]
|
|
200
|
+
await self.sio.emit('socket_disconnected', (
|
|
201
|
+
namespace,
|
|
202
|
+
sid,
|
|
203
|
+
reason,
|
|
204
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
205
|
+
), namespace=self.admin_namespace)
|
|
206
|
+
else:
|
|
207
|
+
await self.sio.emit('event_received', (
|
|
208
|
+
namespace,
|
|
209
|
+
sid,
|
|
210
|
+
(event, *args[1:]),
|
|
211
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
212
|
+
), namespace=self.admin_namespace)
|
|
213
|
+
return await self.sio.__trigger_event(event, namespace, *args)
|
|
216
214
|
|
|
217
215
|
async def _check_for_upgrade(self, eio_sid, sid,
|
|
218
216
|
namespace): # pragma: no cover
|
|
@@ -237,7 +235,7 @@ class InstrumentedAsyncServer:
|
|
|
237
235
|
namespace,
|
|
238
236
|
room,
|
|
239
237
|
sid,
|
|
240
|
-
datetime.
|
|
238
|
+
datetime.now(timezone.utc).isoformat(),
|
|
241
239
|
)))
|
|
242
240
|
return ret
|
|
243
241
|
|
|
@@ -247,7 +245,7 @@ class InstrumentedAsyncServer:
|
|
|
247
245
|
namespace,
|
|
248
246
|
room,
|
|
249
247
|
sid,
|
|
250
|
-
datetime.
|
|
248
|
+
datetime.now(timezone.utc).isoformat(),
|
|
251
249
|
)))
|
|
252
250
|
return self.sio.manager.__basic_leave_room(sid, namespace, room)
|
|
253
251
|
|
|
@@ -258,7 +256,7 @@ class InstrumentedAsyncServer:
|
|
|
258
256
|
callback=callback, **kwargs)
|
|
259
257
|
if namespace != self.admin_namespace:
|
|
260
258
|
event_data = [event] + list(data) if isinstance(data, tuple) \
|
|
261
|
-
else [data]
|
|
259
|
+
else [event, data]
|
|
262
260
|
if not isinstance(skip_sid, list): # pragma: no branch
|
|
263
261
|
skip_sid = [skip_sid]
|
|
264
262
|
for sid, _ in self.sio.manager.get_participants(namespace, room):
|
|
@@ -267,22 +265,10 @@ class InstrumentedAsyncServer:
|
|
|
267
265
|
namespace,
|
|
268
266
|
sid,
|
|
269
267
|
event_data,
|
|
270
|
-
datetime.
|
|
268
|
+
datetime.now(timezone.utc).isoformat(),
|
|
271
269
|
), namespace=self.admin_namespace)
|
|
272
270
|
return ret
|
|
273
271
|
|
|
274
|
-
async def _handle_event_internal(self, server, sid, eio_sid, data,
|
|
275
|
-
namespace, id):
|
|
276
|
-
ret = await self.sio.__handle_event_internal(server, sid, eio_sid,
|
|
277
|
-
data, namespace, id)
|
|
278
|
-
await self.sio.emit('event_received', (
|
|
279
|
-
namespace,
|
|
280
|
-
sid,
|
|
281
|
-
data,
|
|
282
|
-
datetime.utcnow().isoformat() + 'Z',
|
|
283
|
-
), namespace=self.admin_namespace)
|
|
284
|
-
return ret
|
|
285
|
-
|
|
286
272
|
async def _handle_eio_connect(self, eio_sid, environ):
|
|
287
273
|
if self.stop_stats_event is None:
|
|
288
274
|
self.stop_stats_event = self.sio.eio.create_event()
|
|
@@ -292,9 +278,9 @@ class InstrumentedAsyncServer:
|
|
|
292
278
|
self.event_buffer.push('rawConnection')
|
|
293
279
|
return await self.sio._handle_eio_connect(eio_sid, environ)
|
|
294
280
|
|
|
295
|
-
async def _handle_eio_disconnect(self, eio_sid):
|
|
281
|
+
async def _handle_eio_disconnect(self, eio_sid, reason):
|
|
296
282
|
self.event_buffer.push('rawDisconnection')
|
|
297
|
-
return await self.sio._handle_eio_disconnect(eio_sid)
|
|
283
|
+
return await self.sio._handle_eio_disconnect(eio_sid, reason)
|
|
298
284
|
|
|
299
285
|
def _eio_http_response(self, packets=None, headers=None, jsonp_index=None):
|
|
300
286
|
ret = self.sio.eio.__ok(packets=packets, headers=headers,
|
|
@@ -338,7 +324,7 @@ class InstrumentedAsyncServer:
|
|
|
338
324
|
eio_sid)
|
|
339
325
|
await self.sio.emit('socket_connected', (
|
|
340
326
|
serialized_socket,
|
|
341
|
-
datetime.
|
|
327
|
+
datetime.fromtimestamp(t, timezone.utc).isoformat(),
|
|
342
328
|
), namespace=self.admin_namespace)
|
|
343
329
|
return await socket.__send_ping()
|
|
344
330
|
|
|
@@ -391,7 +377,7 @@ class InstrumentedAsyncServer:
|
|
|
391
377
|
'secure': environ.get('wsgi.url_scheme', '') == 'https',
|
|
392
378
|
'url': environ.get('PATH_INFO', ''),
|
|
393
379
|
'issued': tm * 1000,
|
|
394
|
-
'time': datetime.
|
|
380
|
+
'time': datetime.fromtimestamp(tm, timezone.utc).isoformat()
|
|
395
381
|
if tm else '',
|
|
396
382
|
},
|
|
397
383
|
'rooms': self.sio.manager.get_rooms(sid, namespace),
|
|
@@ -158,7 +158,7 @@ class AsyncClient(base_client.BaseClient):
|
|
|
158
158
|
await self._handle_reconnect()
|
|
159
159
|
if self.eio.state == 'connected':
|
|
160
160
|
return
|
|
161
|
-
raise exceptions.ConnectionError(exc.args[0]) from
|
|
161
|
+
raise exceptions.ConnectionError(exc.args[0]) from exc
|
|
162
162
|
|
|
163
163
|
if wait:
|
|
164
164
|
try:
|
|
@@ -191,6 +191,7 @@ class AsyncClient(base_client.BaseClient):
|
|
|
191
191
|
if not self._reconnect_task:
|
|
192
192
|
if self.eio.state == 'connected': # pragma: no cover
|
|
193
193
|
# connected while sleeping above
|
|
194
|
+
print('oops')
|
|
194
195
|
continue
|
|
195
196
|
break
|
|
196
197
|
await self._reconnect_task
|
|
@@ -324,7 +325,7 @@ class AsyncClient(base_client.BaseClient):
|
|
|
324
325
|
for n in self.namespaces:
|
|
325
326
|
await self._send_packet(self.packet_class(packet.DISCONNECT,
|
|
326
327
|
namespace=n))
|
|
327
|
-
await self.eio.disconnect(
|
|
328
|
+
await self.eio.disconnect()
|
|
328
329
|
|
|
329
330
|
async def shutdown(self):
|
|
330
331
|
"""Stop the client.
|
|
@@ -13,6 +13,7 @@ except ImportError: # pragma: no cover
|
|
|
13
13
|
RedisError = None
|
|
14
14
|
|
|
15
15
|
from .async_pubsub_manager import AsyncPubSubManager
|
|
16
|
+
from .redis_manager import parse_redis_sentinel_url
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
|
|
@@ -29,15 +30,18 @@ class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
|
|
|
29
30
|
client_manager=socketio.AsyncRedisManager(url))
|
|
30
31
|
|
|
31
32
|
:param url: The connection URL for the Redis server. For a default Redis
|
|
32
|
-
store running on the same host, use ``redis://``. To use
|
|
33
|
-
|
|
33
|
+
store running on the same host, use ``redis://``. To use a
|
|
34
|
+
TLS connection, use ``rediss://``. To use Redis Sentinel, use
|
|
35
|
+
``redis+sentinel://`` with a comma-separated list of hosts
|
|
36
|
+
and the service name after the db in the URL path. Example:
|
|
37
|
+
``redis+sentinel://user:pw@host1:1234,host2:2345/0/myredis``.
|
|
34
38
|
:param channel: The channel name on which the server sends and receives
|
|
35
39
|
notifications. Must be the same in all the servers.
|
|
36
40
|
:param write_only: If set to ``True``, only initialize to emit events. The
|
|
37
41
|
default of ``False`` initializes the class for emitting
|
|
38
42
|
and receiving.
|
|
39
43
|
:param redis_options: additional keyword arguments to be passed to
|
|
40
|
-
``
|
|
44
|
+
``Redis.from_url()`` or ``Sentinel()``.
|
|
41
45
|
"""
|
|
42
46
|
name = 'aioredis'
|
|
43
47
|
|
|
@@ -54,8 +58,16 @@ class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
|
|
|
54
58
|
super().__init__(channel=channel, write_only=write_only, logger=logger)
|
|
55
59
|
|
|
56
60
|
def _redis_connect(self):
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
if not self.redis_url.startswith('redis+sentinel://'):
|
|
62
|
+
self.redis = aioredis.Redis.from_url(self.redis_url,
|
|
63
|
+
**self.redis_options)
|
|
64
|
+
else:
|
|
65
|
+
sentinels, service_name, connection_kwargs = \
|
|
66
|
+
parse_redis_sentinel_url(self.redis_url)
|
|
67
|
+
kwargs = self.redis_options
|
|
68
|
+
kwargs.update(connection_kwargs)
|
|
69
|
+
sentinel = aioredis.sentinel.Sentinel(sentinels, **kwargs)
|
|
70
|
+
self.redis = sentinel.master_for(service_name or self.channel)
|
|
59
71
|
self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
|
|
60
72
|
|
|
61
73
|
async def _publish(self, data):
|
|
@@ -12,6 +12,8 @@ class AsyncSimpleClient:
|
|
|
12
12
|
The positional and keyword arguments given in the constructor are passed
|
|
13
13
|
to the underlying :func:`socketio.AsyncClient` object.
|
|
14
14
|
"""
|
|
15
|
+
client_class = AsyncClient
|
|
16
|
+
|
|
15
17
|
def __init__(self, *args, **kwargs):
|
|
16
18
|
self.client_args = args
|
|
17
19
|
self.client_kwargs = kwargs
|
|
@@ -60,7 +62,8 @@ class AsyncSimpleClient:
|
|
|
60
62
|
self.namespace = namespace
|
|
61
63
|
self.input_buffer = []
|
|
62
64
|
self.input_event.clear()
|
|
63
|
-
self.client =
|
|
65
|
+
self.client = self.client_class(
|
|
66
|
+
*self.client_args, **self.client_kwargs)
|
|
64
67
|
|
|
65
68
|
@self.client.event(namespace=self.namespace)
|
|
66
69
|
def connect(): # pragma: no cover
|
|
@@ -156,7 +156,7 @@ class Client(base_client.BaseClient):
|
|
|
156
156
|
self._handle_reconnect()
|
|
157
157
|
if self.eio.state == 'connected':
|
|
158
158
|
return
|
|
159
|
-
raise exceptions.ConnectionError(exc.args[0]) from
|
|
159
|
+
raise exceptions.ConnectionError(exc.args[0]) from exc
|
|
160
160
|
|
|
161
161
|
if wait:
|
|
162
162
|
while self._connect_event.wait(timeout=wait_timeout):
|
|
@@ -306,7 +306,7 @@ class Client(base_client.BaseClient):
|
|
|
306
306
|
for n in self.namespaces:
|
|
307
307
|
self._send_packet(self.packet_class(
|
|
308
308
|
packet.DISCONNECT, namespace=n))
|
|
309
|
-
self.eio.disconnect(
|
|
309
|
+
self.eio.disconnect()
|
|
310
310
|
|
|
311
311
|
def shutdown(self):
|
|
312
312
|
"""Stop the client.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import pickle
|
|
3
3
|
import time
|
|
4
|
+
from urllib.parse import urlparse
|
|
4
5
|
|
|
5
6
|
try:
|
|
6
7
|
import redis
|
|
@@ -12,6 +13,32 @@ from .pubsub_manager import PubSubManager
|
|
|
12
13
|
logger = logging.getLogger('socketio')
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
def parse_redis_sentinel_url(url):
|
|
17
|
+
"""Parse a Redis Sentinel URL with the format:
|
|
18
|
+
redis+sentinel://[:password]@host1:port1,host2:port2,.../db/service_name
|
|
19
|
+
"""
|
|
20
|
+
parsed_url = urlparse(url)
|
|
21
|
+
if parsed_url.scheme != 'redis+sentinel':
|
|
22
|
+
raise ValueError('Invalid Redis Sentinel URL')
|
|
23
|
+
sentinels = []
|
|
24
|
+
for host_port in parsed_url.netloc.split('@')[-1].split(','):
|
|
25
|
+
host, port = host_port.rsplit(':', 1)
|
|
26
|
+
sentinels.append((host, int(port)))
|
|
27
|
+
kwargs = {}
|
|
28
|
+
if parsed_url.username:
|
|
29
|
+
kwargs['username'] = parsed_url.username
|
|
30
|
+
if parsed_url.password:
|
|
31
|
+
kwargs['password'] = parsed_url.password
|
|
32
|
+
service_name = None
|
|
33
|
+
if parsed_url.path:
|
|
34
|
+
parts = parsed_url.path.split('/')
|
|
35
|
+
if len(parts) >= 2 and parts[1] != '':
|
|
36
|
+
kwargs['db'] = int(parts[1])
|
|
37
|
+
if len(parts) >= 3 and parts[2] != '':
|
|
38
|
+
service_name = parts[2]
|
|
39
|
+
return sentinels, service_name, kwargs
|
|
40
|
+
|
|
41
|
+
|
|
15
42
|
class RedisManager(PubSubManager): # pragma: no cover
|
|
16
43
|
"""Redis based client manager.
|
|
17
44
|
|
|
@@ -27,15 +54,18 @@ class RedisManager(PubSubManager): # pragma: no cover
|
|
|
27
54
|
server = socketio.Server(client_manager=socketio.RedisManager(url))
|
|
28
55
|
|
|
29
56
|
:param url: The connection URL for the Redis server. For a default Redis
|
|
30
|
-
store running on the same host, use ``redis://``. To use
|
|
31
|
-
|
|
57
|
+
store running on the same host, use ``redis://``. To use a
|
|
58
|
+
TLS connection, use ``rediss://``. To use Redis Sentinel, use
|
|
59
|
+
``redis+sentinel://`` with a comma-separated list of hosts
|
|
60
|
+
and the service name after the db in the URL path. Example:
|
|
61
|
+
``redis+sentinel://user:pw@host1:1234,host2:2345/0/myredis``.
|
|
32
62
|
:param channel: The channel name on which the server sends and receives
|
|
33
63
|
notifications. Must be the same in all the servers.
|
|
34
64
|
:param write_only: If set to ``True``, only initialize to emit events. The
|
|
35
65
|
default of ``False`` initializes the class for emitting
|
|
36
66
|
and receiving.
|
|
37
67
|
:param redis_options: additional keyword arguments to be passed to
|
|
38
|
-
``Redis.from_url()``.
|
|
68
|
+
``Redis.from_url()`` or ``Sentinel()``.
|
|
39
69
|
"""
|
|
40
70
|
name = 'redis'
|
|
41
71
|
|
|
@@ -66,8 +96,16 @@ class RedisManager(PubSubManager): # pragma: no cover
|
|
|
66
96
|
'with ' + self.server.async_mode)
|
|
67
97
|
|
|
68
98
|
def _redis_connect(self):
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
if not self.redis_url.startswith('redis+sentinel://'):
|
|
100
|
+
self.redis = redis.Redis.from_url(self.redis_url,
|
|
101
|
+
**self.redis_options)
|
|
102
|
+
else:
|
|
103
|
+
sentinels, service_name, connection_kwargs = \
|
|
104
|
+
parse_redis_sentinel_url(self.redis_url)
|
|
105
|
+
kwargs = self.redis_options
|
|
106
|
+
kwargs.update(connection_kwargs)
|
|
107
|
+
sentinel = redis.sentinel.Sentinel(sentinels, **kwargs)
|
|
108
|
+
self.redis = sentinel.master_for(service_name or self.channel)
|
|
71
109
|
self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
|
|
72
110
|
|
|
73
111
|
def _publish(self, data):
|
|
@@ -12,6 +12,8 @@ class SimpleClient:
|
|
|
12
12
|
The positional and keyword arguments given in the constructor are passed
|
|
13
13
|
to the underlying :func:`socketio.Client` object.
|
|
14
14
|
"""
|
|
15
|
+
client_class = Client
|
|
16
|
+
|
|
15
17
|
def __init__(self, *args, **kwargs):
|
|
16
18
|
self.client_args = args
|
|
17
19
|
self.client_kwargs = kwargs
|
|
@@ -58,7 +60,8 @@ class SimpleClient:
|
|
|
58
60
|
self.namespace = namespace
|
|
59
61
|
self.input_buffer = []
|
|
60
62
|
self.input_event.clear()
|
|
61
|
-
self.client =
|
|
63
|
+
self.client = self.client_class(
|
|
64
|
+
*self.client_args, **self.client_kwargs)
|
|
62
65
|
|
|
63
66
|
@self.client.event(namespace=self.namespace)
|
|
64
67
|
def connect(): # pragma: no cover
|
|
@@ -475,7 +475,7 @@ class TestAsyncClient:
|
|
|
475
475
|
c._send_packet.await_args_list[0][0][0].encode()
|
|
476
476
|
== expected_packet.encode()
|
|
477
477
|
)
|
|
478
|
-
c.eio.disconnect.assert_awaited_once_with(
|
|
478
|
+
c.eio.disconnect.assert_awaited_once_with()
|
|
479
479
|
|
|
480
480
|
async def test_disconnect_namespaces(self):
|
|
481
481
|
c = async_client.AsyncClient()
|
|
@@ -993,7 +993,7 @@ class TestAsyncClient:
|
|
|
993
993
|
c._send_packet.await_args_list[0][0][0].encode()
|
|
994
994
|
== expected_packet.encode()
|
|
995
995
|
)
|
|
996
|
-
c.eio.disconnect.assert_awaited_once_with(
|
|
996
|
+
c.eio.disconnect.assert_awaited_once_with()
|
|
997
997
|
|
|
998
998
|
async def test_shutdown_disconnect_namespaces(self):
|
|
999
999
|
c = async_client.AsyncClient()
|
|
@@ -16,46 +16,51 @@ class TestAsyncAsyncSimpleClient:
|
|
|
16
16
|
assert not client.connected
|
|
17
17
|
|
|
18
18
|
async def test_connect(self):
|
|
19
|
+
mock_client = mock.MagicMock()
|
|
20
|
+
original_client_class = AsyncSimpleClient.client_class
|
|
21
|
+
AsyncSimpleClient.client_class = mock_client
|
|
22
|
+
|
|
19
23
|
client = AsyncSimpleClient(123, a='b')
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
mock_client.return_value.connect = mock.AsyncMock()
|
|
25
|
+
|
|
26
|
+
await client.connect('url', headers='h', auth='a', transports='t',
|
|
27
|
+
namespace='n', socketio_path='s',
|
|
28
|
+
wait_timeout='w')
|
|
29
|
+
mock_client.assert_called_once_with(123, a='b')
|
|
30
|
+
assert client.client == mock_client()
|
|
31
|
+
mock_client().connect.assert_awaited_once_with(
|
|
32
|
+
'url', headers='h', auth='a', transports='t',
|
|
33
|
+
namespaces=['n'], socketio_path='s', wait_timeout='w')
|
|
34
|
+
mock_client().event.call_count == 3
|
|
35
|
+
mock_client().on.assert_called_once_with('*', namespace='n')
|
|
36
|
+
assert client.namespace == 'n'
|
|
37
|
+
assert not client.input_event.is_set()
|
|
38
|
+
|
|
39
|
+
AsyncSimpleClient.client_class = original_client_class
|
|
40
|
+
|
|
41
|
+
async def test_connect_context_manager(self):
|
|
42
|
+
mock_client = mock.MagicMock()
|
|
43
|
+
original_client_class = AsyncSimpleClient.client_class
|
|
44
|
+
AsyncSimpleClient.client_class = mock_client
|
|
45
|
+
|
|
46
|
+
async with AsyncSimpleClient(123, a='b') as client:
|
|
22
47
|
mock_client.return_value.connect = mock.AsyncMock()
|
|
23
48
|
|
|
24
|
-
await client.connect('url', headers='h', auth='a',
|
|
25
|
-
|
|
26
|
-
wait_timeout='w')
|
|
49
|
+
await client.connect('url', headers='h', auth='a',
|
|
50
|
+
transports='t', namespace='n',
|
|
51
|
+
socketio_path='s', wait_timeout='w')
|
|
27
52
|
mock_client.assert_called_once_with(123, a='b')
|
|
28
53
|
assert client.client == mock_client()
|
|
29
54
|
mock_client().connect.assert_awaited_once_with(
|
|
30
55
|
'url', headers='h', auth='a', transports='t',
|
|
31
56
|
namespaces=['n'], socketio_path='s', wait_timeout='w')
|
|
32
57
|
mock_client().event.call_count == 3
|
|
33
|
-
mock_client().on.assert_called_once_with(
|
|
58
|
+
mock_client().on.assert_called_once_with(
|
|
59
|
+
'*', namespace='n')
|
|
34
60
|
assert client.namespace == 'n'
|
|
35
61
|
assert not client.input_event.is_set()
|
|
36
62
|
|
|
37
|
-
|
|
38
|
-
async def _t():
|
|
39
|
-
async with AsyncSimpleClient(123, a='b') as client:
|
|
40
|
-
with mock.patch('socketio.async_simple_client.AsyncClient') \
|
|
41
|
-
as mock_client:
|
|
42
|
-
mock_client.return_value.connect = mock.AsyncMock()
|
|
43
|
-
|
|
44
|
-
await client.connect('url', headers='h', auth='a',
|
|
45
|
-
transports='t', namespace='n',
|
|
46
|
-
socketio_path='s', wait_timeout='w')
|
|
47
|
-
mock_client.assert_called_once_with(123, a='b')
|
|
48
|
-
assert client.client == mock_client()
|
|
49
|
-
mock_client().connect.assert_awaited_once_with(
|
|
50
|
-
'url', headers='h', auth='a', transports='t',
|
|
51
|
-
namespaces=['n'], socketio_path='s', wait_timeout='w')
|
|
52
|
-
mock_client().event.call_count == 3
|
|
53
|
-
mock_client().on.assert_called_once_with(
|
|
54
|
-
'*', namespace='n')
|
|
55
|
-
assert client.namespace == 'n'
|
|
56
|
-
assert not client.input_event.is_set()
|
|
57
|
-
|
|
58
|
-
await _t()
|
|
63
|
+
AsyncSimpleClient.client_class = original_client_class
|
|
59
64
|
|
|
60
65
|
async def test_connect_twice(self):
|
|
61
66
|
client = AsyncSimpleClient(123, a='b')
|
|
@@ -633,7 +633,7 @@ class TestClient:
|
|
|
633
633
|
c._send_packet.call_args_list[0][0][0].encode()
|
|
634
634
|
== expected_packet.encode()
|
|
635
635
|
)
|
|
636
|
-
c.eio.disconnect.assert_called_once_with(
|
|
636
|
+
c.eio.disconnect.assert_called_once_with()
|
|
637
637
|
|
|
638
638
|
def test_disconnect_namespaces(self):
|
|
639
639
|
c = client.Client()
|
|
@@ -1138,7 +1138,7 @@ class TestClient:
|
|
|
1138
1138
|
c._send_packet.call_args_list[0][0][0].encode()
|
|
1139
1139
|
== expected_packet.encode()
|
|
1140
1140
|
)
|
|
1141
|
-
c.eio.disconnect.assert_called_once_with(
|
|
1141
|
+
c.eio.disconnect.assert_called_once_with()
|
|
1142
1142
|
|
|
1143
1143
|
def test_shutdown_disconnect_namespaces(self):
|
|
1144
1144
|
c = client.Client()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from socketio.redis_manager import parse_redis_sentinel_url
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestPubSubManager:
|
|
7
|
+
def test_sentinel_url_parser(self):
|
|
8
|
+
with pytest.raises(ValueError):
|
|
9
|
+
parse_redis_sentinel_url('redis://localhost:6379/0')
|
|
10
|
+
|
|
11
|
+
assert parse_redis_sentinel_url(
|
|
12
|
+
'redis+sentinel://localhost:6379'
|
|
13
|
+
) == (
|
|
14
|
+
[('localhost', 6379)],
|
|
15
|
+
None,
|
|
16
|
+
{}
|
|
17
|
+
)
|
|
18
|
+
assert parse_redis_sentinel_url(
|
|
19
|
+
'redis+sentinel://192.168.0.1:6379,192.168.0.2:6379/'
|
|
20
|
+
) == (
|
|
21
|
+
[('192.168.0.1', 6379), ('192.168.0.2', 6379)],
|
|
22
|
+
None,
|
|
23
|
+
{}
|
|
24
|
+
)
|
|
25
|
+
assert parse_redis_sentinel_url(
|
|
26
|
+
'redis+sentinel://h1:6379,h2:6379/0'
|
|
27
|
+
) == (
|
|
28
|
+
[('h1', 6379), ('h2', 6379)],
|
|
29
|
+
None,
|
|
30
|
+
{'db': 0}
|
|
31
|
+
)
|
|
32
|
+
assert parse_redis_sentinel_url(
|
|
33
|
+
'redis+sentinel://user:password@h1:6379,h2:6379,h1:6380/0/myredis'
|
|
34
|
+
) == (
|
|
35
|
+
[('h1', 6379), ('h2', 6379), ('h1', 6380)],
|
|
36
|
+
'myredis',
|
|
37
|
+
{'username': 'user', 'password': 'password', 'db': 0}
|
|
38
|
+
)
|
|
@@ -14,10 +14,34 @@ class TestSimpleClient:
|
|
|
14
14
|
assert not client.connected
|
|
15
15
|
|
|
16
16
|
def test_connect(self):
|
|
17
|
+
mock_client = mock.MagicMock()
|
|
18
|
+
original_client_class = SimpleClient.client_class
|
|
19
|
+
SimpleClient.client_class = mock_client
|
|
20
|
+
|
|
17
21
|
client = SimpleClient(123, a='b')
|
|
18
|
-
|
|
22
|
+
client.connect('url', headers='h', auth='a', transports='t',
|
|
23
|
+
namespace='n', socketio_path='s', wait_timeout='w')
|
|
24
|
+
mock_client.assert_called_once_with(123, a='b')
|
|
25
|
+
assert client.client == mock_client()
|
|
26
|
+
mock_client().connect.assert_called_once_with(
|
|
27
|
+
'url', headers='h', auth='a', transports='t',
|
|
28
|
+
namespaces=['n'], socketio_path='s', wait_timeout='w')
|
|
29
|
+
mock_client().event.call_count == 3
|
|
30
|
+
mock_client().on.assert_called_once_with('*', namespace='n')
|
|
31
|
+
assert client.namespace == 'n'
|
|
32
|
+
assert not client.input_event.is_set()
|
|
33
|
+
|
|
34
|
+
SimpleClient.client_class = original_client_class
|
|
35
|
+
|
|
36
|
+
def test_connect_context_manager(self):
|
|
37
|
+
mock_client = mock.MagicMock()
|
|
38
|
+
original_client_class = SimpleClient.client_class
|
|
39
|
+
SimpleClient.client_class = mock_client
|
|
40
|
+
|
|
41
|
+
with SimpleClient(123, a='b') as client:
|
|
19
42
|
client.connect('url', headers='h', auth='a', transports='t',
|
|
20
|
-
namespace='n', socketio_path='s',
|
|
43
|
+
namespace='n', socketio_path='s',
|
|
44
|
+
wait_timeout='w')
|
|
21
45
|
mock_client.assert_called_once_with(123, a='b')
|
|
22
46
|
assert client.client == mock_client()
|
|
23
47
|
mock_client().connect.assert_called_once_with(
|
|
@@ -28,21 +52,7 @@ class TestSimpleClient:
|
|
|
28
52
|
assert client.namespace == 'n'
|
|
29
53
|
assert not client.input_event.is_set()
|
|
30
54
|
|
|
31
|
-
|
|
32
|
-
with SimpleClient(123, a='b') as client:
|
|
33
|
-
with mock.patch('socketio.simple_client.Client') as mock_client:
|
|
34
|
-
client.connect('url', headers='h', auth='a', transports='t',
|
|
35
|
-
namespace='n', socketio_path='s',
|
|
36
|
-
wait_timeout='w')
|
|
37
|
-
mock_client.assert_called_once_with(123, a='b')
|
|
38
|
-
assert client.client == mock_client()
|
|
39
|
-
mock_client().connect.assert_called_once_with(
|
|
40
|
-
'url', headers='h', auth='a', transports='t',
|
|
41
|
-
namespaces=['n'], socketio_path='s', wait_timeout='w')
|
|
42
|
-
mock_client().event.call_count == 3
|
|
43
|
-
mock_client().on.assert_called_once_with('*', namespace='n')
|
|
44
|
-
assert client.namespace == 'n'
|
|
45
|
-
assert not client.input_event.is_set()
|
|
55
|
+
SimpleClient.client_class = original_client_class
|
|
46
56
|
|
|
47
57
|
def test_connect_twice(self):
|
|
48
58
|
client = SimpleClient(123, a='b')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_socketio-5.12.0 → python_socketio-5.13.0}/src/python_socketio.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_socketio-5.12.0 → python_socketio-5.13.0}/tests/performance/server_send_broadcast.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|