python-socketio 5.11.4__tar.gz → 5.12.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.
Files changed (89) hide show
  1. {python_socketio-5.11.4/src/python_socketio.egg-info → python_socketio-5.12.0}/PKG-INFO +2 -2
  2. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/client.rst +18 -5
  3. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/server.rst +19 -4
  4. {python_socketio-5.11.4 → python_socketio-5.12.0}/pyproject.toml +6 -2
  5. {python_socketio-5.11.4 → python_socketio-5.12.0/src/python_socketio.egg-info}/PKG-INFO +2 -2
  6. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/python_socketio.egg-info/SOURCES.txt +0 -1
  7. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/python_socketio.egg-info/requires.txt +1 -1
  8. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/admin.py +1 -1
  9. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_client.py +30 -12
  10. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_namespace.py +36 -4
  11. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_server.py +26 -9
  12. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/base_client.py +4 -1
  13. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/base_manager.py +1 -2
  14. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/base_namespace.py +1 -1
  15. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/base_server.py +3 -0
  16. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/client.py +22 -9
  17. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/kafka_manager.py +1 -2
  18. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/kombu_manager.py +1 -1
  19. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/namespace.py +16 -2
  20. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/packet.py +1 -1
  21. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/redis_manager.py +1 -2
  22. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/server.py +19 -9
  23. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/zmq_manager.py +1 -1
  24. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/async/test_admin.py +20 -21
  25. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/async/test_client.py +485 -503
  26. python_socketio-5.12.0/tests/async/test_manager.py +382 -0
  27. python_socketio-5.12.0/tests/async/test_namespace.py +390 -0
  28. python_socketio-5.12.0/tests/async/test_pubsub_manager.py +555 -0
  29. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/async/test_server.py +442 -432
  30. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/async/test_simple_client.py +54 -56
  31. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_admin.py +19 -19
  32. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_client.py +36 -41
  33. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_manager.py +3 -4
  34. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_middleware.py +1 -2
  35. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_msgpack_packet.py +1 -3
  36. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_namespace.py +38 -3
  37. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_packet.py +1 -3
  38. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_pubsub_manager.py +2 -3
  39. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_server.py +31 -16
  40. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/test_simple_client.py +1 -2
  41. {python_socketio-5.11.4 → python_socketio-5.12.0}/tox.ini +3 -1
  42. python_socketio-5.11.4/tests/async/helpers.py +0 -18
  43. python_socketio-5.11.4/tests/async/test_manager.py +0 -407
  44. python_socketio-5.11.4/tests/async/test_namespace.py +0 -342
  45. python_socketio-5.11.4/tests/async/test_pubsub_manager.py +0 -603
  46. {python_socketio-5.11.4 → python_socketio-5.12.0}/LICENSE +0 -0
  47. {python_socketio-5.11.4 → python_socketio-5.12.0}/MANIFEST.in +0 -0
  48. {python_socketio-5.11.4 → python_socketio-5.12.0}/README.md +0 -0
  49. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/Makefile +0 -0
  50. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/_static/README.md +0 -0
  51. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/_static/custom.css +0 -0
  52. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/api.rst +0 -0
  53. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/conf.py +0 -0
  54. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/index.rst +0 -0
  55. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/intro.rst +0 -0
  56. {python_socketio-5.11.4 → python_socketio-5.12.0}/docs/make.bat +0 -0
  57. {python_socketio-5.11.4 → python_socketio-5.12.0}/setup.cfg +0 -0
  58. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/python_socketio.egg-info/dependency_links.txt +0 -0
  59. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/python_socketio.egg-info/not-zip-safe +0 -0
  60. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/python_socketio.egg-info/top_level.txt +0 -0
  61. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/__init__.py +0 -0
  62. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/asgi.py +0 -0
  63. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_admin.py +0 -0
  64. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_aiopika_manager.py +0 -0
  65. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_manager.py +0 -0
  66. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_pubsub_manager.py +0 -0
  67. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_redis_manager.py +0 -0
  68. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/async_simple_client.py +0 -0
  69. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/exceptions.py +0 -0
  70. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/manager.py +0 -0
  71. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/middleware.py +0 -0
  72. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/msgpack_packet.py +0 -0
  73. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/pubsub_manager.py +0 -0
  74. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/simple_client.py +0 -0
  75. {python_socketio-5.11.4 → python_socketio-5.12.0}/src/socketio/tornado.py +0 -0
  76. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/__init__.py +0 -0
  77. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/async/__init__.py +0 -0
  78. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/asyncio_web_server.py +0 -0
  79. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/common/__init__.py +0 -0
  80. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/README.md +0 -0
  81. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/binary_packet.py +0 -0
  82. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/json_packet.py +0 -0
  83. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/namespace_packet.py +0 -0
  84. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/run.sh +0 -0
  85. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/server_receive.py +0 -0
  86. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/server_send.py +0 -0
  87. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/server_send_broadcast.py +0 -0
  88. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/performance/text_packet.py +0 -0
  89. {python_socketio-5.11.4 → python_socketio-5.12.0}/tests/web_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-socketio
3
- Version: 5.11.4
3
+ Version: 5.12.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
@@ -14,7 +14,7 @@ Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: bidict>=0.21.0
17
- Requires-Dist: python-engineio>=4.8.0
17
+ Requires-Dist: python-engineio>=4.11.0
18
18
  Provides-Extra: client
19
19
  Requires-Dist: requests>=2.21.0; extra == "client"
20
20
  Requires-Dist: websocket-client>=0.54.0; extra == "client"
@@ -312,8 +312,8 @@ server::
312
312
  print("The connection failed!")
313
313
 
314
314
  @sio.event
315
- def disconnect():
316
- print("I'm disconnected!")
315
+ def disconnect(reason):
316
+ print("I'm disconnected! reason:", reason)
317
317
 
318
318
  The ``connect_error`` handler is invoked when a connection attempt fails. If
319
319
  the server provides arguments, these are passed on to the handler. The server
@@ -325,7 +325,20 @@ server initiated disconnects, or accidental disconnects, for example due to
325
325
  networking failures. In the case of an accidental disconnection, the client is
326
326
  going to attempt to reconnect immediately after invoking the disconnect
327
327
  handler. As soon as the connection is re-established the connect handler will
328
- be invoked once again.
328
+ be invoked once again. The handler receives a ``reason`` argument which
329
+ provides the cause of the disconnection::
330
+
331
+ @sio.event
332
+ def disconnect(reason):
333
+ if reason == sio.reason.CLIENT_DISCONNECT:
334
+ print('the client disconnected')
335
+ elif reason == sio.reason.SERVER_DISCONNECT:
336
+ print('the server disconnected the client')
337
+ else:
338
+ print('disconnect reason:', reason)
339
+
340
+ See the The :attr:`socketio.Client.reason` attribute for a list of possible
341
+ disconnection reasons.
329
342
 
330
343
  The ``connect``, ``connect_error`` and ``disconnect`` events have to be
331
344
  defined explicitly and are not invoked on a catch-all event handler.
@@ -509,7 +522,7 @@ that belong to a namespace can be created as methods of a subclass of
509
522
  def on_connect(self):
510
523
  pass
511
524
 
512
- def on_disconnect(self):
525
+ def on_disconnect(self, reason):
513
526
  pass
514
527
 
515
528
  def on_my_event(self, data):
@@ -525,7 +538,7 @@ coroutines if desired::
525
538
  def on_connect(self):
526
539
  pass
527
540
 
528
- def on_disconnect(self):
541
+ def on_disconnect(self, reason):
529
542
  pass
530
543
 
531
544
  async def on_my_event(self, data):
@@ -232,8 +232,8 @@ automatically when a client connects or disconnects from the server::
232
232
  print('connect ', sid)
233
233
 
234
234
  @sio.event
235
- def disconnect(sid):
236
- print('disconnect ', sid)
235
+ def disconnect(sid, reason):
236
+ print('disconnect ', sid, reason)
237
237
 
238
238
  The ``connect`` event is an ideal place to perform user authentication, and
239
239
  any necessary mapping between user entities in the application and the ``sid``
@@ -256,6 +256,21 @@ message::
256
256
  def connect(sid, environ, auth):
257
257
  raise ConnectionRefusedError('authentication failed')
258
258
 
259
+ The disconnect handler receives the ``sid`` assigned to the client and a
260
+ ``reason``, which provides the cause of the disconnection::
261
+
262
+ @sio.event
263
+ def disconnect(sid, reason):
264
+ if reason == sio.reason.CLIENT_DISCONNECT:
265
+ print('the client disconnected')
266
+ elif reason == sio.reason.SERVER_DISCONNECT:
267
+ print('the server disconnected the client')
268
+ else:
269
+ print('disconnect reason:', reason)
270
+
271
+ See the The :attr:`socketio.Server.reason` attribute for a list of possible
272
+ disconnection reasons.
273
+
259
274
  Catch-All Event Handlers
260
275
  ~~~~~~~~~~~~~~~~~~~~~~~~
261
276
 
@@ -433,7 +448,7 @@ belong to a namespace can be created as methods in a subclass of
433
448
  def on_connect(self, sid, environ):
434
449
  pass
435
450
 
436
- def on_disconnect(self, sid):
451
+ def on_disconnect(self, sid, reason):
437
452
  pass
438
453
 
439
454
  def on_my_event(self, sid, data):
@@ -449,7 +464,7 @@ if desired::
449
464
  def on_connect(self, sid, environ):
450
465
  pass
451
466
 
452
- def on_disconnect(self, sid):
467
+ def on_disconnect(self, sid, reason):
453
468
  pass
454
469
 
455
470
  async def on_my_event(self, sid, data):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-socketio"
3
- version = "5.11.4"
3
+ version = "5.12.0"
4
4
  authors = [
5
5
  { name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
6
6
  ]
@@ -15,7 +15,7 @@ classifiers = [
15
15
  requires-python = ">=3.8"
16
16
  dependencies = [
17
17
  "bidict >= 0.21.0",
18
- "python-engineio >= 4.8.0",
18
+ "python-engineio >= 4.11.0",
19
19
  ]
20
20
 
21
21
  [project.readme]
@@ -54,3 +54,7 @@ namespaces = false
54
54
  [build-system]
55
55
  requires = ["setuptools>=61.2"]
56
56
  build-backend = "setuptools.build_meta"
57
+
58
+ [tool.pytest.ini_options]
59
+ asyncio_mode = "auto"
60
+ asyncio_default_fixture_loop_scope = "session"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-socketio
3
- Version: 5.11.4
3
+ Version: 5.12.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
@@ -14,7 +14,7 @@ Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: bidict>=0.21.0
17
- Requires-Dist: python-engineio>=4.8.0
17
+ Requires-Dist: python-engineio>=4.11.0
18
18
  Provides-Extra: client
19
19
  Requires-Dist: requests>=2.21.0; extra == "client"
20
20
  Requires-Dist: websocket-client>=0.54.0; extra == "client"
@@ -54,7 +54,6 @@ tests/__init__.py
54
54
  tests/asyncio_web_server.py
55
55
  tests/web_server.py
56
56
  tests/async/__init__.py
57
- tests/async/helpers.py
58
57
  tests/async/test_admin.py
59
58
  tests/async/test_client.py
60
59
  tests/async/test_manager.py
@@ -1,5 +1,5 @@
1
1
  bidict>=0.21.0
2
- python-engineio>=4.8.0
2
+ python-engineio>=4.11.0
3
3
 
4
4
  [asyncio_client]
5
5
  aiohttp>=3.4
@@ -16,7 +16,7 @@ class EventBuffer:
16
16
 
17
17
  def push(self, type, count=1):
18
18
  timestamp = int(time.time()) * 1000
19
- key = '{};{}'.format(timestamp, type)
19
+ key = f'{timestamp};{type}'
20
20
  if key not in self.buffer:
21
21
  self.buffer[key] = {
22
22
  'timestamp': timestamp,
@@ -116,7 +116,7 @@ class AsyncClient(base_client.BaseClient):
116
116
  Example usage::
117
117
 
118
118
  sio = socketio.AsyncClient()
119
- sio.connect('http://localhost:5000')
119
+ await sio.connect('http://localhost:5000')
120
120
  """
121
121
  if self.connected:
122
122
  raise exceptions.ConnectionError('Already connected')
@@ -189,6 +189,9 @@ class AsyncClient(base_client.BaseClient):
189
189
  await self.eio.wait()
190
190
  await self.sleep(1) # give the reconnect task time to start up
191
191
  if not self._reconnect_task:
192
+ if self.eio.state == 'connected': # pragma: no cover
193
+ # connected while sleeping above
194
+ continue
192
195
  break
193
196
  await self._reconnect_task
194
197
  if self.eio.state != 'connected':
@@ -335,7 +338,6 @@ class AsyncClient(base_client.BaseClient):
335
338
  await self.disconnect()
336
339
  elif self._reconnect_task: # pragma: no branch
337
340
  self._reconnect_abort.set()
338
- print(self._reconnect_task)
339
341
  await self._reconnect_task
340
342
 
341
343
  def start_background_task(self, target, *args, **kwargs):
@@ -386,7 +388,7 @@ class AsyncClient(base_client.BaseClient):
386
388
  async def _handle_connect(self, namespace, data):
387
389
  namespace = namespace or '/'
388
390
  if namespace not in self.namespaces:
389
- self.logger.info('Namespace {} is connected'.format(namespace))
391
+ self.logger.info(f'Namespace {namespace} is connected')
390
392
  self.namespaces[namespace] = (data or {}).get('sid', self.sid)
391
393
  await self._trigger_event('connect', namespace=namespace)
392
394
  self._connect_event.set()
@@ -395,8 +397,9 @@ class AsyncClient(base_client.BaseClient):
395
397
  if not self.connected:
396
398
  return
397
399
  namespace = namespace or '/'
398
- await self._trigger_event('disconnect', namespace=namespace)
399
- await self._trigger_event('__disconnect_final', namespace=namespace)
400
+ await self._trigger_event('disconnect', namespace,
401
+ self.reason.SERVER_DISCONNECT)
402
+ await self._trigger_event('__disconnect_final', namespace)
400
403
  if namespace in self.namespaces:
401
404
  del self.namespaces[namespace]
402
405
  if not self.namespaces:
@@ -459,11 +462,27 @@ class AsyncClient(base_client.BaseClient):
459
462
  if handler:
460
463
  if asyncio.iscoroutinefunction(handler):
461
464
  try:
462
- ret = await handler(*args)
465
+ try:
466
+ ret = await handler(*args)
467
+ except TypeError:
468
+ # the legacy disconnect event does not take a reason
469
+ # argument
470
+ if event == 'disconnect':
471
+ ret = await handler(*args[:-1])
472
+ else: # pragma: no cover
473
+ raise
463
474
  except asyncio.CancelledError: # pragma: no cover
464
475
  ret = None
465
476
  else:
466
- ret = handler(*args)
477
+ try:
478
+ ret = handler(*args)
479
+ except TypeError:
480
+ # the legacy disconnect event does not take a reason
481
+ # argument
482
+ if event == 'disconnect':
483
+ ret = handler(*args[:-1])
484
+ else: # pragma: no cover
485
+ raise
467
486
  return ret
468
487
 
469
488
  # or else, forward the event to a namepsace handler if one exists
@@ -563,22 +582,21 @@ class AsyncClient(base_client.BaseClient):
563
582
  else:
564
583
  raise ValueError('Unknown packet type.')
565
584
 
566
- async def _handle_eio_disconnect(self):
585
+ async def _handle_eio_disconnect(self, reason):
567
586
  """Handle the Engine.IO disconnection event."""
568
587
  self.logger.info('Engine.IO connection dropped')
569
588
  will_reconnect = self.reconnection and self.eio.state == 'connected'
570
589
  if self.connected:
571
590
  for n in self.namespaces:
572
- await self._trigger_event('disconnect', namespace=n)
591
+ await self._trigger_event('disconnect', n, reason)
573
592
  if not will_reconnect:
574
- await self._trigger_event('__disconnect_final',
575
- namespace=n)
593
+ await self._trigger_event('__disconnect_final', n)
576
594
  self.namespaces = {}
577
595
  self.connected = False
578
596
  self.callbacks = {}
579
597
  self._binary_packet = None
580
598
  self.sid = None
581
- if will_reconnect:
599
+ if will_reconnect and not self._reconnect_task:
582
600
  self._reconnect_task = self.start_background_task(
583
601
  self._handle_reconnect)
584
602
 
@@ -34,11 +34,27 @@ class AsyncNamespace(base_namespace.BaseServerNamespace):
34
34
  handler = getattr(self, handler_name)
35
35
  if asyncio.iscoroutinefunction(handler) is True:
36
36
  try:
37
- ret = await handler(*args)
37
+ try:
38
+ ret = await handler(*args)
39
+ except TypeError:
40
+ # legacy disconnect events do not have a reason
41
+ # argument
42
+ if event == 'disconnect':
43
+ ret = await handler(*args[:-1])
44
+ else: # pragma: no cover
45
+ raise
38
46
  except asyncio.CancelledError: # pragma: no cover
39
47
  ret = None
40
48
  else:
41
- ret = handler(*args)
49
+ try:
50
+ ret = handler(*args)
51
+ except TypeError:
52
+ # legacy disconnect events do not have a reason
53
+ # argument
54
+ if event == 'disconnect':
55
+ ret = handler(*args[:-1])
56
+ else: # pragma: no cover
57
+ raise
42
58
  return ret
43
59
 
44
60
  async def emit(self, event, data=None, to=None, room=None, skip_sid=None,
@@ -199,11 +215,27 @@ class AsyncClientNamespace(base_namespace.BaseClientNamespace):
199
215
  handler = getattr(self, handler_name)
200
216
  if asyncio.iscoroutinefunction(handler) is True:
201
217
  try:
202
- ret = await handler(*args)
218
+ try:
219
+ ret = await handler(*args)
220
+ except TypeError:
221
+ # legacy disconnect events do not have a reason
222
+ # argument
223
+ if event == 'disconnect':
224
+ ret = await handler(*args[:-1])
225
+ else: # pragma: no cover
226
+ raise
203
227
  except asyncio.CancelledError: # pragma: no cover
204
228
  ret = None
205
229
  else:
206
- ret = handler(*args)
230
+ try:
231
+ ret = handler(*args)
232
+ except TypeError:
233
+ # legacy disconnect events do not have a reason
234
+ # argument
235
+ if event == 'disconnect':
236
+ ret = handler(*args[:-1])
237
+ else: # pragma: no cover
238
+ raise
207
239
  return ret
208
240
 
209
241
  async def emit(self, event, data=None, namespace=None, callback=None):
@@ -385,7 +385,7 @@ class AsyncServer(base_server.BaseServer):
385
385
  async with eio.session(sid) as session:
386
386
  print('received message from ', session['username'])
387
387
  """
388
- class _session_context_manager(object):
388
+ class _session_context_manager:
389
389
  def __init__(self, server, sid, namespace):
390
390
  self.server = server
391
391
  self.sid = sid
@@ -427,7 +427,8 @@ class AsyncServer(base_server.BaseServer):
427
427
  eio_sid = self.manager.pre_disconnect(sid, namespace=namespace)
428
428
  await self._send_packet(eio_sid, self.packet_class(
429
429
  packet.DISCONNECT, namespace=namespace))
430
- await self._trigger_event('disconnect', namespace, sid)
430
+ await self._trigger_event('disconnect', namespace, sid,
431
+ self.reason.SERVER_DISCONNECT)
431
432
  await self.manager.disconnect(sid, namespace=namespace,
432
433
  ignore_queue=True)
433
434
 
@@ -575,14 +576,15 @@ class AsyncServer(base_server.BaseServer):
575
576
  await self._send_packet(eio_sid, self.packet_class(
576
577
  packet.CONNECT, {'sid': sid}, namespace=namespace))
577
578
 
578
- async def _handle_disconnect(self, eio_sid, namespace):
579
+ async def _handle_disconnect(self, eio_sid, namespace, reason=None):
579
580
  """Handle a client disconnect."""
580
581
  namespace = namespace or '/'
581
582
  sid = self.manager.sid_from_eio_sid(eio_sid, namespace)
582
583
  if not self.manager.is_connected(sid, namespace): # pragma: no cover
583
584
  return
584
585
  self.manager.pre_disconnect(sid, namespace=namespace)
585
- await self._trigger_event('disconnect', namespace, sid)
586
+ await self._trigger_event('disconnect', namespace, sid,
587
+ reason or self.reason.CLIENT_DISCONNECT)
586
588
  await self.manager.disconnect(sid, namespace, ignore_queue=True)
587
589
 
588
590
  async def _handle_event(self, eio_sid, namespace, id, data):
@@ -634,11 +636,25 @@ class AsyncServer(base_server.BaseServer):
634
636
  if handler:
635
637
  if asyncio.iscoroutinefunction(handler):
636
638
  try:
637
- ret = await handler(*args)
639
+ try:
640
+ ret = await handler(*args)
641
+ except TypeError:
642
+ # legacy disconnect events use only one argument
643
+ if event == 'disconnect':
644
+ ret = await handler(*args[:-1])
645
+ else: # pragma: no cover
646
+ raise
638
647
  except asyncio.CancelledError: # pragma: no cover
639
648
  ret = None
640
649
  else:
641
- ret = handler(*args)
650
+ try:
651
+ ret = handler(*args)
652
+ except TypeError:
653
+ # legacy disconnect events use only one argument
654
+ if event == 'disconnect':
655
+ ret = handler(*args[:-1])
656
+ else: # pragma: no cover
657
+ raise
642
658
  return ret
643
659
  # or else, forward the event to a namespace handler if one exists
644
660
  handler, args = self._get_namespace_handler(namespace, args)
@@ -671,7 +687,8 @@ class AsyncServer(base_server.BaseServer):
671
687
  if pkt.packet_type == packet.CONNECT:
672
688
  await self._handle_connect(eio_sid, pkt.namespace, pkt.data)
673
689
  elif pkt.packet_type == packet.DISCONNECT:
674
- await self._handle_disconnect(eio_sid, pkt.namespace)
690
+ await self._handle_disconnect(eio_sid, pkt.namespace,
691
+ self.reason.CLIENT_DISCONNECT)
675
692
  elif pkt.packet_type == packet.EVENT:
676
693
  await self._handle_event(eio_sid, pkt.namespace, pkt.id,
677
694
  pkt.data)
@@ -686,10 +703,10 @@ class AsyncServer(base_server.BaseServer):
686
703
  else:
687
704
  raise ValueError('Unknown packet type.')
688
705
 
689
- async def _handle_eio_disconnect(self, eio_sid):
706
+ async def _handle_eio_disconnect(self, eio_sid, reason):
690
707
  """Handle Engine.IO disconnect event."""
691
708
  for n in list(self.manager.get_namespaces()).copy():
692
- await self._handle_disconnect(eio_sid, n)
709
+ await self._handle_disconnect(eio_sid, n, reason)
693
710
  if eio_sid in self.environ:
694
711
  del self.environ[eio_sid]
695
712
 
@@ -3,6 +3,8 @@ import logging
3
3
  import signal
4
4
  import threading
5
5
 
6
+ import engineio
7
+
6
8
  from . import base_namespace
7
9
  from . import packet
8
10
 
@@ -31,6 +33,7 @@ original_signal_handler = None
31
33
  class BaseClient:
32
34
  reserved_events = ['connect', 'connect_error', 'disconnect',
33
35
  '__disconnect_final']
36
+ reason = engineio.Client.reason
34
37
 
35
38
  def __init__(self, reconnection=True, reconnection_attempts=0,
36
39
  reconnection_delay=1, reconnection_delay_max=5,
@@ -285,7 +288,7 @@ class BaseClient:
285
288
  def _handle_eio_message(self, data): # pragma: no cover
286
289
  raise NotImplementedError()
287
290
 
288
- def _handle_eio_disconnect(self): # pragma: no cover
291
+ def _handle_eio_disconnect(self, reason): # pragma: no cover
289
292
  raise NotImplementedError()
290
293
 
291
294
  def _engineio_client_class(self): # pragma: no cover
@@ -37,8 +37,7 @@ class BaseManager:
37
37
  participants.update(ns[r]._fwdm if r in ns else {})
38
38
  else:
39
39
  participants = ns[room]._fwdm.copy() if room in ns else {}
40
- for sid, eio_sid in participants.items():
41
- yield sid, eio_sid
40
+ yield from participants.items()
42
41
 
43
42
  def connect(self, eio_sid, namespace):
44
43
  """Register a client connection to a namespace."""
@@ -1,4 +1,4 @@
1
- class BaseNamespace(object):
1
+ class BaseNamespace:
2
2
  def __init__(self, namespace=None):
3
3
  self.namespace = namespace or '/'
4
4
 
@@ -1,5 +1,7 @@
1
1
  import logging
2
2
 
3
+ import engineio
4
+
3
5
  from . import manager
4
6
  from . import base_namespace
5
7
  from . import packet
@@ -9,6 +11,7 @@ default_logger = logging.getLogger('socketio.server')
9
11
 
10
12
  class BaseServer:
11
13
  reserved_events = ['connect', 'disconnect']
14
+ reason = engineio.Server.reason
12
15
 
13
16
  def __init__(self, client_manager=None, logger=False, serializer='default',
14
17
  json=None, async_handlers=True, always_connect=False,
@@ -180,7 +180,12 @@ class Client(base_client.BaseClient):
180
180
  self.eio.wait()
181
181
  self.sleep(1) # give the reconnect task time to start up
182
182
  if not self._reconnect_task:
183
- break
183
+ if self.eio.state == 'connected': # pragma: no cover
184
+ # connected while sleeping above
185
+ continue
186
+ else:
187
+ # the reconnect task gave up
188
+ break
184
189
  self._reconnect_task.join()
185
190
  if self.eio.state != 'connected':
186
191
  break
@@ -363,7 +368,7 @@ class Client(base_client.BaseClient):
363
368
  def _handle_connect(self, namespace, data):
364
369
  namespace = namespace or '/'
365
370
  if namespace not in self.namespaces:
366
- self.logger.info('Namespace {} is connected'.format(namespace))
371
+ self.logger.info(f'Namespace {namespace} is connected')
367
372
  self.namespaces[namespace] = (data or {}).get('sid', self.sid)
368
373
  self._trigger_event('connect', namespace=namespace)
369
374
  self._connect_event.set()
@@ -372,8 +377,9 @@ class Client(base_client.BaseClient):
372
377
  if not self.connected:
373
378
  return
374
379
  namespace = namespace or '/'
375
- self._trigger_event('disconnect', namespace=namespace)
376
- self._trigger_event('__disconnect_final', namespace=namespace)
380
+ self._trigger_event('disconnect', namespace,
381
+ self.reason.SERVER_DISCONNECT)
382
+ self._trigger_event('__disconnect_final', namespace)
377
383
  if namespace in self.namespaces:
378
384
  del self.namespaces[namespace]
379
385
  if not self.namespaces:
@@ -431,7 +437,14 @@ class Client(base_client.BaseClient):
431
437
  # first see if we have an explicit handler for the event
432
438
  handler, args = self._get_event_handler(event, namespace, args)
433
439
  if handler:
434
- return handler(*args)
440
+ try:
441
+ return handler(*args)
442
+ except TypeError:
443
+ # the legacy disconnect event does not take a reason argument
444
+ if event == 'disconnect':
445
+ return handler(*args[:-1])
446
+ else: # pragma: no cover
447
+ raise
435
448
 
436
449
  # or else, forward the event to a namespace handler if one exists
437
450
  handler, args = self._get_namespace_handler(namespace, args)
@@ -520,21 +533,21 @@ class Client(base_client.BaseClient):
520
533
  else:
521
534
  raise ValueError('Unknown packet type.')
522
535
 
523
- def _handle_eio_disconnect(self):
536
+ def _handle_eio_disconnect(self, reason):
524
537
  """Handle the Engine.IO disconnection event."""
525
538
  self.logger.info('Engine.IO connection dropped')
526
539
  will_reconnect = self.reconnection and self.eio.state == 'connected'
527
540
  if self.connected:
528
541
  for n in self.namespaces:
529
- self._trigger_event('disconnect', namespace=n)
542
+ self._trigger_event('disconnect', n, reason)
530
543
  if not will_reconnect:
531
- self._trigger_event('__disconnect_final', namespace=n)
544
+ self._trigger_event('__disconnect_final', n)
532
545
  self.namespaces = {}
533
546
  self.connected = False
534
547
  self.callbacks = {}
535
548
  self._binary_packet = None
536
549
  self.sid = None
537
- if will_reconnect:
550
+ if will_reconnect and not self._reconnect_task:
538
551
  self._reconnect_task = self.start_background_task(
539
552
  self._handle_reconnect)
540
553
 
@@ -57,8 +57,7 @@ class KafkaManager(PubSubManager): # pragma: no cover
57
57
  self.producer.flush()
58
58
 
59
59
  def _kafka_listen(self):
60
- for message in self.consumer:
61
- yield message
60
+ yield from self.consumer
62
61
 
63
62
  def _listen(self):
64
63
  for message in self._kafka_listen():
@@ -86,7 +86,7 @@ class KombuManager(PubSubManager): # pragma: no cover
86
86
  return kombu.Exchange(self.channel, **options)
87
87
 
88
88
  def _queue(self):
89
- queue_name = 'flask-socketio.' + str(uuid.uuid4())
89
+ queue_name = 'python-socketio.' + str(uuid.uuid4())
90
90
  options = {'durable': False, 'queue_arguments': {'x-expires': 300000}}
91
91
  options.update(self.queue_options)
92
92
  return kombu.Queue(queue_name, self._exchange(), **options)
@@ -23,7 +23,14 @@ class Namespace(base_namespace.BaseServerNamespace):
23
23
  """
24
24
  handler_name = 'on_' + (event or '')
25
25
  if hasattr(self, handler_name):
26
- return getattr(self, handler_name)(*args)
26
+ try:
27
+ return getattr(self, handler_name)(*args)
28
+ except TypeError:
29
+ # legacy disconnect events do not have a reason argument
30
+ if event == 'disconnect':
31
+ return getattr(self, handler_name)(*args[:-1])
32
+ else: # pragma: no cover
33
+ raise
27
34
 
28
35
  def emit(self, event, data=None, to=None, room=None, skip_sid=None,
29
36
  namespace=None, callback=None, ignore_queue=False):
@@ -154,7 +161,14 @@ class ClientNamespace(base_namespace.BaseClientNamespace):
154
161
  """
155
162
  handler_name = 'on_' + (event or '')
156
163
  if hasattr(self, handler_name):
157
- return getattr(self, handler_name)(*args)
164
+ try:
165
+ return getattr(self, handler_name)(*args)
166
+ except TypeError:
167
+ # legacy disconnect events do not have a reason argument
168
+ if event == 'disconnect':
169
+ return getattr(self, handler_name)(*args[:-1])
170
+ else: # pragma: no cover
171
+ raise
158
172
 
159
173
  def emit(self, event, data=None, namespace=None, callback=None):
160
174
  """Emit a custom event to the server.
@@ -7,7 +7,7 @@ packet_names = ['CONNECT', 'DISCONNECT', 'EVENT', 'ACK', 'CONNECT_ERROR',
7
7
  'BINARY_EVENT', 'BINARY_ACK']
8
8
 
9
9
 
10
- class Packet(object):
10
+ class Packet:
11
11
  """Socket.IO packet."""
12
12
 
13
13
  # the format of the Socket.IO packet is as follows:
@@ -94,8 +94,7 @@ class RedisManager(PubSubManager): # pragma: no cover
94
94
  self._redis_connect()
95
95
  self.pubsub.subscribe(self.channel)
96
96
  retry_sleep = 1
97
- for message in self.pubsub.listen():
98
- yield message
97
+ yield from self.pubsub.listen()
99
98
  except redis.exceptions.RedisError:
100
99
  logger.error('Cannot receive from redis... '
101
100
  'retrying in {} secs'.format(retry_sleep))