python-socketio 5.11.2__tar.gz → 5.11.3__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 (86) hide show
  1. {python-socketio-5.11.2/src/python_socketio.egg-info → python_socketio-5.11.3}/PKG-INFO +1 -1
  2. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/intro.rst +6 -8
  3. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/server.rst +2 -2
  4. {python-socketio-5.11.2 → python_socketio-5.11.3}/pyproject.toml +1 -1
  5. {python-socketio-5.11.2 → python_socketio-5.11.3/src/python_socketio.egg-info}/PKG-INFO +1 -1
  6. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_client.py +28 -3
  7. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_server.py +3 -0
  8. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/client.py +20 -1
  9. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/packet.py +1 -1
  10. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/server.py +3 -0
  11. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_client.py +61 -0
  12. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_client.py +58 -0
  13. {python-socketio-5.11.2 → python_socketio-5.11.3}/LICENSE +0 -0
  14. {python-socketio-5.11.2 → python_socketio-5.11.3}/MANIFEST.in +0 -0
  15. {python-socketio-5.11.2 → python_socketio-5.11.3}/README.md +0 -0
  16. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/Makefile +0 -0
  17. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/_static/README.md +0 -0
  18. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/_static/custom.css +0 -0
  19. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/api.rst +0 -0
  20. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/client.rst +0 -0
  21. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/conf.py +0 -0
  22. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/index.rst +0 -0
  23. {python-socketio-5.11.2 → python_socketio-5.11.3}/docs/make.bat +0 -0
  24. {python-socketio-5.11.2 → python_socketio-5.11.3}/setup.cfg +0 -0
  25. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/python_socketio.egg-info/SOURCES.txt +0 -0
  26. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/python_socketio.egg-info/dependency_links.txt +0 -0
  27. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/python_socketio.egg-info/not-zip-safe +0 -0
  28. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/python_socketio.egg-info/requires.txt +0 -0
  29. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/python_socketio.egg-info/top_level.txt +0 -0
  30. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/__init__.py +0 -0
  31. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/admin.py +0 -0
  32. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/asgi.py +0 -0
  33. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_admin.py +0 -0
  34. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_aiopika_manager.py +0 -0
  35. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_manager.py +0 -0
  36. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_namespace.py +0 -0
  37. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_pubsub_manager.py +0 -0
  38. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_redis_manager.py +0 -0
  39. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/async_simple_client.py +0 -0
  40. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/base_client.py +0 -0
  41. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/base_manager.py +0 -0
  42. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/base_namespace.py +0 -0
  43. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/base_server.py +0 -0
  44. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/exceptions.py +0 -0
  45. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/kafka_manager.py +0 -0
  46. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/kombu_manager.py +0 -0
  47. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/manager.py +0 -0
  48. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/middleware.py +0 -0
  49. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/msgpack_packet.py +0 -0
  50. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/namespace.py +0 -0
  51. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/pubsub_manager.py +0 -0
  52. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/redis_manager.py +0 -0
  53. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/simple_client.py +0 -0
  54. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/tornado.py +0 -0
  55. {python-socketio-5.11.2 → python_socketio-5.11.3}/src/socketio/zmq_manager.py +0 -0
  56. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/__init__.py +0 -0
  57. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/__init__.py +0 -0
  58. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/helpers.py +0 -0
  59. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_admin.py +0 -0
  60. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_manager.py +0 -0
  61. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_namespace.py +0 -0
  62. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_pubsub_manager.py +0 -0
  63. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_server.py +0 -0
  64. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/async/test_simple_client.py +0 -0
  65. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/asyncio_web_server.py +0 -0
  66. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/__init__.py +0 -0
  67. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_admin.py +0 -0
  68. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_manager.py +0 -0
  69. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_middleware.py +0 -0
  70. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_msgpack_packet.py +0 -0
  71. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_namespace.py +0 -0
  72. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_packet.py +0 -0
  73. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_pubsub_manager.py +0 -0
  74. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_server.py +0 -0
  75. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/common/test_simple_client.py +0 -0
  76. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/README.md +0 -0
  77. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/binary_packet.py +0 -0
  78. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/json_packet.py +0 -0
  79. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/namespace_packet.py +0 -0
  80. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/run.sh +0 -0
  81. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/server_receive.py +0 -0
  82. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/server_send.py +0 -0
  83. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/server_send_broadcast.py +0 -0
  84. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/performance/text_packet.py +0 -0
  85. {python-socketio-5.11.2 → python_socketio-5.11.3}/tests/web_server.py +0 -0
  86. {python-socketio-5.11.2 → python_socketio-5.11.3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-socketio
3
- Version: 5.11.2
3
+ Version: 5.11.3
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
@@ -101,9 +101,8 @@ Client Features
101
101
  ---------------
102
102
 
103
103
  - Can connect to other Socket.IO servers that are compatible with the
104
- JavaScript Socket.IO 1.x and 2.x releases. Work to support release 3.x is in
105
- progress.
106
- - Compatible with Python 3.6+.
104
+ JavaScript Socket.IO reference server.
105
+ - Compatible with Python 3.8+.
107
106
  - Two versions of the client, one for standard Python and another for
108
107
  asyncio.
109
108
  - Uses an event-based architecture implemented with decorators that
@@ -181,9 +180,8 @@ Server Features
181
180
  ---------------
182
181
 
183
182
  - Can connect to servers running other Socket.IO clients that are compatible
184
- with the JavaScript client versions 1.x and 2.x. Work to support the 3.x
185
- release is in progress.
186
- - Compatible with Python 3.6+.
183
+ with the JavaScript reference client.
184
+ - Compatible with Python 3.8+.
187
185
  - Two versions of the server, one for standard Python and another for
188
186
  asyncio.
189
187
  - Supports large number of clients even on modest hardware due to being
@@ -195,8 +193,8 @@ Server Features
195
193
  - Can be integrated with WSGI applications written in frameworks such as Flask, Django,
196
194
  etc.
197
195
  - Can be integrated with `aiohttp <http://aiohttp.readthedocs.io/>`_,
198
- `sanic <http://sanic.readthedocs.io/>`_ and `tornado <http://www.tornadoweb.org/>`_
199
- ``asyncio`` applications.
196
+ `FastAPI <https://fastapi.tiangolo.com/>`_, `sanic <http://sanic.readthedocs.io/>`_
197
+ and `tornado <http://www.tornadoweb.org/>`_ ``asyncio`` applications.
200
198
  - Broadcasting of messages to all connected clients, or to subsets of them
201
199
  assigned to "rooms".
202
200
  - Optional support for multiple servers, connected through a messaging queue
@@ -722,7 +722,7 @@ ASGI web application and the Socket.IO server as a bundle::
722
722
  sio = socketio.AsyncServer(async_mode='asgi')
723
723
  app = socketio.ASGIApp(sio, other_app)
724
724
 
725
- The ``ASGIApp`` instance is a fully complaint ASGI instance that can be
725
+ The ``ASGIApp`` instance is a fully compliant ASGI instance that can be
726
726
  deployed with an ASGI compatible web server.
727
727
 
728
728
  Aiohttp
@@ -947,7 +947,7 @@ constructor::
947
947
  sio = socketio.Server(async_mode='threading')
948
948
 
949
949
  A server configured for threading is deployed as a regular web application,
950
- using any WSGI complaint multi-threaded server. The example below deploys an
950
+ using any WSGI compliant multi-threaded server. The example below deploys an
951
951
  Socket.IO application combined with a Flask web application, using Flask's
952
952
  development web server based on Werkzeug::
953
953
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-socketio"
3
- version = "5.11.2"
3
+ version = "5.11.3"
4
4
  authors = [
5
5
  { name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
6
6
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-socketio
3
- Version: 5.11.2
3
+ Version: 5.11.3
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
@@ -53,11 +53,14 @@ class AsyncClient(base_client.BaseClient):
53
53
  :param http_session: an initialized ``aiohttp.ClientSession`` object to be
54
54
  used when sending requests to the server. Use it if
55
55
  you need to add special client options such as proxy
56
- servers, SSL certificates, etc.
56
+ servers, SSL certificates, custom CA bundle, etc.
57
57
  :param ssl_verify: ``True`` to verify SSL certificates, or ``False`` to
58
58
  skip SSL certificate verification, allowing
59
59
  connections to servers with self signed certificates.
60
60
  The default is ``True``.
61
+ :param websocket_extra_options: Dictionary containing additional keyword
62
+ arguments passed to
63
+ ``websocket.create_connection()``.
61
64
  :param engineio_logger: To enable Engine.IO logging set to ``True`` or pass
62
65
  a logger object to use. To disable logging set to
63
66
  ``False``. The default is ``False``. Note that
@@ -128,6 +131,8 @@ class AsyncClient(base_client.BaseClient):
128
131
  if namespaces is None:
129
132
  namespaces = list(set(self.handlers.keys()).union(
130
133
  set(self.namespace_handlers.keys())))
134
+ if '*' in namespaces:
135
+ namespaces.remove('*')
131
136
  if len(namespaces) == 0:
132
137
  namespaces = ['/']
133
138
  elif isinstance(namespaces, str):
@@ -318,6 +323,21 @@ class AsyncClient(base_client.BaseClient):
318
323
  namespace=n))
319
324
  await self.eio.disconnect(abort=True)
320
325
 
326
+ async def shutdown(self):
327
+ """Stop the client.
328
+
329
+ If the client is connected to a server, it is disconnected. If the
330
+ client is attempting to reconnect to server, the reconnection attempts
331
+ are stopped. If the client is not connected to a server and is not
332
+ attempting to reconnect, then this function does nothing.
333
+ """
334
+ if self.connected:
335
+ await self.disconnect()
336
+ elif self._reconnect_task: # pragma: no branch
337
+ self._reconnect_abort.set()
338
+ print(self._reconnect_task)
339
+ await self._reconnect_task
340
+
321
341
  def start_background_task(self, target, *args, **kwargs):
322
342
  """Start a background task using the appropriate async model.
323
343
 
@@ -467,15 +487,20 @@ class AsyncClient(base_client.BaseClient):
467
487
  self.logger.info(
468
488
  'Connection failed, new attempt in {:.02f} seconds'.format(
469
489
  delay))
490
+ abort = False
470
491
  try:
471
492
  await asyncio.wait_for(self._reconnect_abort.wait(), delay)
493
+ abort = True
494
+ except asyncio.TimeoutError:
495
+ pass
496
+ except asyncio.CancelledError: # pragma: no cover
497
+ abort = True
498
+ if abort:
472
499
  self.logger.info('Reconnect task aborted')
473
500
  for n in self.connection_namespaces:
474
501
  await self._trigger_event('__disconnect_final',
475
502
  namespace=n)
476
503
  break
477
- except (asyncio.TimeoutError, asyncio.CancelledError):
478
- pass
479
504
  attempt_count += 1
480
505
  try:
481
506
  await self.connect(self.connection_url,
@@ -102,6 +102,9 @@ class AsyncServer(base_server.BaseServer):
102
102
  inactive clients are closed. Set to ``False`` to
103
103
  disable the monitoring task (not recommended). The
104
104
  default is ``True``.
105
+ :param transports: The list of allowed transports. Valid transports
106
+ are ``'polling'`` and ``'websocket'``. Defaults to
107
+ ``['polling', 'websocket']``.
105
108
  :param engineio_logger: To enable Engine.IO logging set to ``True`` or pass
106
109
  a logger object to use. To disable logging set to
107
110
  ``False``. The default is ``False``. Note that
@@ -56,11 +56,14 @@ class Client(base_client.BaseClient):
56
56
  :param http_session: an initialized ``requests.Session`` object to be used
57
57
  when sending requests to the server. Use it if you
58
58
  need to add special client options such as proxy
59
- servers, SSL certificates, etc.
59
+ servers, SSL certificates, custom CA bundle, etc.
60
60
  :param ssl_verify: ``True`` to verify SSL certificates, or ``False`` to
61
61
  skip SSL certificate verification, allowing
62
62
  connections to servers with self signed certificates.
63
63
  The default is ``True``.
64
+ :param websocket_extra_options: Dictionary containing additional keyword
65
+ arguments passed to
66
+ ``websocket.create_connection()``.
64
67
  :param engineio_logger: To enable Engine.IO logging set to ``True`` or pass
65
68
  a logger object to use. To disable logging set to
66
69
  ``False``. The default is ``False``. Note that
@@ -126,6 +129,8 @@ class Client(base_client.BaseClient):
126
129
  if namespaces is None:
127
130
  namespaces = list(set(self.handlers.keys()).union(
128
131
  set(self.namespace_handlers.keys())))
132
+ if '*' in namespaces:
133
+ namespaces.remove('*')
129
134
  if len(namespaces) == 0:
130
135
  namespaces = ['/']
131
136
  elif isinstance(namespaces, str):
@@ -298,6 +303,20 @@ class Client(base_client.BaseClient):
298
303
  packet.DISCONNECT, namespace=n))
299
304
  self.eio.disconnect(abort=True)
300
305
 
306
+ def shutdown(self):
307
+ """Stop the client.
308
+
309
+ If the client is connected to a server, it is disconnected. If the
310
+ client is attempting to reconnect to server, the reconnection attempts
311
+ are stopped. If the client is not connected to a server and is not
312
+ attempting to reconnect, then this function does nothing.
313
+ """
314
+ if self.connected:
315
+ self.disconnect()
316
+ elif self._reconnect_task: # pragma: no branch
317
+ self._reconnect_abort.set()
318
+ self._reconnect_task.join()
319
+
301
320
  def start_background_task(self, target, *args, **kwargs):
302
321
  """Start a background task using the appropriate async model.
303
322
 
@@ -185,6 +185,6 @@ class Packet(object):
185
185
  'data': self.data,
186
186
  'nsp': self.namespace,
187
187
  }
188
- if self.id:
188
+ if self.id is not None:
189
189
  d['id'] = self.id
190
190
  return d
@@ -106,6 +106,9 @@ class Server(base_server.BaseServer):
106
106
  inactive clients are closed. Set to ``False`` to
107
107
  disable the monitoring task (not recommended). The
108
108
  default is ``True``.
109
+ :param transports: The list of allowed transports. Valid transports
110
+ are ``'polling'`` and ``'websocket'``. Defaults to
111
+ ``['polling', 'websocket']``.
109
112
  :param engineio_logger: To enable Engine.IO logging set to ``True`` or pass
110
113
  a logger object to use. To disable logging set to
111
114
  ``False``. The default is ``False``. Note that
@@ -98,6 +98,7 @@ class TestAsyncClient(unittest.TestCase):
98
98
  c.eio.connect = AsyncMock()
99
99
  c.on('foo', mock.MagicMock(), namespace='/foo')
100
100
  c.on('bar', mock.MagicMock(), namespace='/')
101
+ c.on('baz', mock.MagicMock(), namespace='*')
101
102
  _run(
102
103
  c.connect(
103
104
  'url',
@@ -990,6 +991,66 @@ class TestAsyncClient(unittest.TestCase):
990
991
  c._trigger_event.mock.assert_called_once_with('__disconnect_final',
991
992
  namespace='/')
992
993
 
994
+ def test_shutdown_disconnect(self):
995
+ c = async_client.AsyncClient()
996
+ c.connected = True
997
+ c.namespaces = {'/': '1'}
998
+ c._trigger_event = AsyncMock()
999
+ c._send_packet = AsyncMock()
1000
+ c.eio = mock.MagicMock()
1001
+ c.eio.disconnect = AsyncMock()
1002
+ c.eio.state = 'connected'
1003
+ _run(c.shutdown())
1004
+ assert c._trigger_event.mock.call_count == 0
1005
+ assert c._send_packet.mock.call_count == 1
1006
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
1007
+ assert (
1008
+ c._send_packet.mock.call_args_list[0][0][0].encode()
1009
+ == expected_packet.encode()
1010
+ )
1011
+ c.eio.disconnect.mock.assert_called_once_with(abort=True)
1012
+
1013
+ def test_shutdown_disconnect_namespaces(self):
1014
+ c = async_client.AsyncClient()
1015
+ c.connected = True
1016
+ c.namespaces = {'/foo': '1', '/bar': '2'}
1017
+ c._trigger_event = AsyncMock()
1018
+ c._send_packet = AsyncMock()
1019
+ c.eio = mock.MagicMock()
1020
+ c.eio.disconnect = AsyncMock()
1021
+ c.eio.state = 'connected'
1022
+ _run(c.shutdown())
1023
+ assert c._trigger_event.mock.call_count == 0
1024
+ assert c._send_packet.mock.call_count == 2
1025
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/foo')
1026
+ assert (
1027
+ c._send_packet.mock.call_args_list[0][0][0].encode()
1028
+ == expected_packet.encode()
1029
+ )
1030
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/bar')
1031
+ assert (
1032
+ c._send_packet.mock.call_args_list[1][0][0].encode()
1033
+ == expected_packet.encode()
1034
+ )
1035
+
1036
+ @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
1037
+ def test_shutdown_reconnect(self, random):
1038
+ c = async_client.AsyncClient()
1039
+ c.connection_namespaces = ['/']
1040
+ c._reconnect_task = AsyncMock()()
1041
+ c._trigger_event = AsyncMock()
1042
+ c.connect = AsyncMock(side_effect=exceptions.ConnectionError)
1043
+
1044
+ async def r():
1045
+ task = c.start_background_task(c._handle_reconnect)
1046
+ await asyncio.sleep(0.1)
1047
+ await c.shutdown()
1048
+ await task
1049
+
1050
+ _run(r())
1051
+ c._trigger_event.mock.assert_called_once_with('__disconnect_final',
1052
+ namespace='/')
1053
+
993
1054
  def test_handle_eio_connect(self):
994
1055
  c = async_client.AsyncClient()
995
1056
  c.connection_namespaces = ['/', '/foo']
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import time
2
3
  import unittest
3
4
  from unittest import mock
4
5
 
@@ -235,6 +236,7 @@ class TestClient(unittest.TestCase):
235
236
  c.eio.connect = mock.MagicMock()
236
237
  c.on('foo', mock.MagicMock(), namespace='/foo')
237
238
  c.on('bar', mock.MagicMock(), namespace='/')
239
+ c.on('baz', mock.MagicMock(), namespace='*')
238
240
  c.connect(
239
241
  'url',
240
242
  headers='headers',
@@ -636,6 +638,7 @@ class TestClient(unittest.TestCase):
636
638
 
637
639
  def test_disconnect_namespaces(self):
638
640
  c = client.Client()
641
+ c.connected = True
639
642
  c.namespaces = {'/foo': '1', '/bar': '2'}
640
643
  c._trigger_event = mock.MagicMock()
641
644
  c._send_packet = mock.MagicMock()
@@ -1128,6 +1131,61 @@ class TestClient(unittest.TestCase):
1128
1131
  c._trigger_event.assert_called_once_with('__disconnect_final',
1129
1132
  namespace='/')
1130
1133
 
1134
+ def test_shutdown_disconnect(self):
1135
+ c = client.Client()
1136
+ c.connected = True
1137
+ c.namespaces = {'/': '1'}
1138
+ c._trigger_event = mock.MagicMock()
1139
+ c._send_packet = mock.MagicMock()
1140
+ c.eio = mock.MagicMock()
1141
+ c.eio.state = 'connected'
1142
+ c.shutdown()
1143
+ assert c._trigger_event.call_count == 0
1144
+ assert c._send_packet.call_count == 1
1145
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/')
1146
+ assert (
1147
+ c._send_packet.call_args_list[0][0][0].encode()
1148
+ == expected_packet.encode()
1149
+ )
1150
+ c.eio.disconnect.assert_called_once_with(abort=True)
1151
+
1152
+ def test_shutdown_disconnect_namespaces(self):
1153
+ c = client.Client()
1154
+ c.connected = True
1155
+ c.namespaces = {'/foo': '1', '/bar': '2'}
1156
+ c._trigger_event = mock.MagicMock()
1157
+ c._send_packet = mock.MagicMock()
1158
+ c.eio = mock.MagicMock()
1159
+ c.eio.state = 'connected'
1160
+ c.shutdown()
1161
+ assert c._trigger_event.call_count == 0
1162
+ assert c._send_packet.call_count == 2
1163
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/foo')
1164
+ assert (
1165
+ c._send_packet.call_args_list[0][0][0].encode()
1166
+ == expected_packet.encode()
1167
+ )
1168
+ expected_packet = packet.Packet(packet.DISCONNECT, namespace='/bar')
1169
+ assert (
1170
+ c._send_packet.call_args_list[1][0][0].encode()
1171
+ == expected_packet.encode()
1172
+ )
1173
+
1174
+ @mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
1175
+ def test_shutdown_reconnect(self, random):
1176
+ c = client.Client()
1177
+ c.connection_namespaces = ['/']
1178
+ c._reconnect_task = mock.MagicMock()
1179
+ c._trigger_event = mock.MagicMock()
1180
+ c.connect = mock.MagicMock(side_effect=exceptions.ConnectionError)
1181
+ task = c.start_background_task(c._handle_reconnect)
1182
+ time.sleep(0.1)
1183
+ c.shutdown()
1184
+ task.join()
1185
+ c._trigger_event.assert_called_once_with('__disconnect_final',
1186
+ namespace='/')
1187
+ c._reconnect_task.join.assert_called_once_with()
1188
+
1131
1189
  def test_handle_eio_connect(self):
1132
1190
  c = client.Client()
1133
1191
  c.connection_namespaces = ['/', '/foo']