python-engineio 4.13.1__tar.gz → 4.13.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 (74) hide show
  1. {python_engineio-4.13.1/src/python_engineio.egg-info → python_engineio-4.13.3}/PKG-INFO +1 -1
  2. {python_engineio-4.13.1 → python_engineio-4.13.3}/pyproject.toml +1 -1
  3. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_client.py +2 -2
  4. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/aiohttp.py +3 -1
  5. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/asgi.py +12 -13
  6. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_server.py +2 -1
  7. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_socket.py +4 -2
  8. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/base_socket.py +4 -1
  9. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/client.py +2 -2
  10. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/server.py +2 -1
  11. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/socket.py +4 -2
  12. {python_engineio-4.13.1 → python_engineio-4.13.3/src/python_engineio.egg-info}/PKG-INFO +1 -1
  13. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/python_engineio.egg-info/SOURCES.txt +0 -9
  14. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/python_engineio.egg-info/dependency_links.txt +0 -0
  15. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/python_engineio.egg-info/not-zip-safe +0 -0
  16. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/python_engineio.egg-info/requires.txt +0 -0
  17. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/python_engineio.egg-info/top_level.txt +0 -0
  18. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_socket.py +18 -2
  19. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_socket.py +18 -1
  20. {python_engineio-4.13.1 → python_engineio-4.13.3}/tox.ini +0 -15
  21. python_engineio-4.13.1/docs/Makefile +0 -19
  22. python_engineio-4.13.1/docs/api.rst +0 -14
  23. python_engineio-4.13.1/docs/client.rst +0 -211
  24. python_engineio-4.13.1/docs/conf.py +0 -177
  25. python_engineio-4.13.1/docs/index.rst +0 -23
  26. python_engineio-4.13.1/docs/intro.rst +0 -152
  27. python_engineio-4.13.1/docs/make.bat +0 -35
  28. python_engineio-4.13.1/docs/server.rst +0 -674
  29. python_engineio-4.13.1/tests/common/__init__.py +0 -0
  30. {python_engineio-4.13.1 → python_engineio-4.13.3}/LICENSE +0 -0
  31. {python_engineio-4.13.1 → python_engineio-4.13.3}/MANIFEST.in +0 -0
  32. {python_engineio-4.13.1 → python_engineio-4.13.3}/README.md +0 -0
  33. {python_engineio-4.13.1 → python_engineio-4.13.3}/docs/_static/README.md +0 -0
  34. {python_engineio-4.13.1 → python_engineio-4.13.3}/setup.cfg +0 -0
  35. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/__init__.py +0 -0
  36. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/__init__.py +0 -0
  37. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/_websocket_wsgi.py +0 -0
  38. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/eventlet.py +0 -0
  39. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/gevent.py +0 -0
  40. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/gevent_uwsgi.py +0 -0
  41. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/sanic.py +0 -0
  42. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/threading.py +0 -0
  43. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/async_drivers/tornado.py +0 -0
  44. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/base_client.py +0 -0
  45. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/base_server.py +0 -0
  46. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/exceptions.py +0 -0
  47. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/json.py +0 -0
  48. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/middleware.py +0 -0
  49. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/packet.py +0 -0
  50. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/payload.py +0 -0
  51. {python_engineio-4.13.1 → python_engineio-4.13.3}/src/engineio/static_files.py +0 -0
  52. {python_engineio-4.13.1/tests → python_engineio-4.13.3/tests/async}/__init__.py +0 -0
  53. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/index.html +0 -0
  54. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_aiohttp.py +0 -0
  55. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_asgi.py +0 -0
  56. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_client.py +0 -0
  57. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_sanic.py +0 -0
  58. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_server.py +0 -0
  59. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/async/test_tornado.py +0 -0
  60. {python_engineio-4.13.1/tests/async → python_engineio-4.13.3/tests/common}/__init__.py +0 -0
  61. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/index.html +0 -0
  62. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_client.py +0 -0
  63. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_middleware.py +0 -0
  64. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_packet.py +0 -0
  65. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_payload.py +0 -0
  66. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/common/test_server.py +0 -0
  67. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/README.md +0 -0
  68. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/binary_b64_packet.py +0 -0
  69. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/binary_packet.py +0 -0
  70. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/json_packet.py +0 -0
  71. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/payload.py +0 -0
  72. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/run.sh +0 -0
  73. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/server_receive.py +0 -0
  74. {python_engineio-4.13.1 → python_engineio-4.13.3}/tests/performance/text_packet.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-engineio
3
- Version: 4.13.1
3
+ Version: 4.13.3
4
4
  Summary: Engine.IO server and client for Python
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-engineio"
3
- version = "4.13.1"
3
+ version = "4.13.3"
4
4
  license = {text = "MIT"}
5
5
  authors = [{name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com"}]
6
6
  description = "Engine.IO server and client for Python"
@@ -571,7 +571,7 @@ class AsyncClient(base_client.BaseClient):
571
571
 
572
572
  async def _read_loop_websocket(self):
573
573
  """Read packets from the Engine.IO WebSocket connection."""
574
- while self.state == 'connected':
574
+ while self.state == 'connected' and self.write_loop_task:
575
575
  p = None
576
576
  try:
577
577
  p = await asyncio.wait_for(
@@ -672,7 +672,6 @@ class AsyncClient(base_client.BaseClient):
672
672
  if r.status < 200 or r.status >= 300:
673
673
  self.logger.warning('Unexpected status code %s in server '
674
674
  'response, aborting', r.status)
675
- self.write_loop_task = None
676
675
  break
677
676
  else:
678
677
  # websocket
@@ -690,3 +689,4 @@ class AsyncClient(base_client.BaseClient):
690
689
  'aborting')
691
690
  break
692
691
  self.logger.info('Exiting write loop task')
692
+ self.write_loop_task = None
@@ -79,11 +79,13 @@ class WebSocket: # pragma: no cover
79
79
  """
80
80
  def __init__(self, handler, server):
81
81
  self.handler = handler
82
+ self.server = server
82
83
  self._sock = None
83
84
 
84
85
  async def __call__(self, environ):
85
86
  request = environ['aiohttp.request']
86
- self._sock = WebSocketResponse(max_msg_size=0)
87
+ self._sock = WebSocketResponse(
88
+ max_msg_size=self.server.max_http_buffer_size)
87
89
  await self._sock.prepare(request)
88
90
 
89
91
  self.environ = environ
@@ -136,10 +136,18 @@ class ASGIApp:
136
136
 
137
137
  async def translate_request(scope, receive, send):
138
138
  class AwaitablePayload: # pragma: no cover
139
- def __init__(self, payload):
140
- self.payload = payload or b''
139
+ def __init__(self, event):
140
+ self.event = event
141
+ self.payload = None
141
142
 
142
143
  async def read(self, length=None):
144
+ if self.payload is None and event['type'] == 'http.request':
145
+ # read payload from http request
146
+ self.payload = self.event.get('body') or b''
147
+ while self.event.get('more_body'):
148
+ self.event = await receive()
149
+ if self.event['type'] == 'http.request':
150
+ self.payload += self.event.get('body') or b''
143
151
  if length is None:
144
152
  r = self.payload
145
153
  self.payload = b''
@@ -149,16 +157,7 @@ async def translate_request(scope, receive, send):
149
157
  return r
150
158
 
151
159
  event = await receive()
152
- payload = b''
153
- if event['type'] == 'http.request':
154
- payload += event.get('body') or b''
155
- while event.get('more_body'):
156
- event = await receive()
157
- if event['type'] == 'http.request':
158
- payload += event.get('body') or b''
159
- elif event['type'] == 'websocket.connect':
160
- pass
161
- else:
160
+ if event['type'] not in ['http.request', 'websocket.connect']:
162
161
  return {}
163
162
 
164
163
  raw_uri = scope['path']
@@ -171,7 +170,7 @@ async def translate_request(scope, receive, send):
171
170
  else:
172
171
  raw_uri += '?' + query_string
173
172
  environ = {
174
- 'wsgi.input': AwaitablePayload(payload),
173
+ 'wsgi.input': AwaitablePayload(event),
175
174
  'wsgi.errors': sys.stderr,
176
175
  'wsgi.version': (1, 0),
177
176
  'wsgi.async': True,
@@ -462,7 +462,6 @@ class AsyncServer(base_server.BaseServer):
462
462
  'maxPayload': self.max_http_buffer_size,
463
463
  })
464
464
  await s.send(pkt)
465
- s.schedule_ping()
466
465
 
467
466
  ret = await self._trigger_event('connect', sid, environ,
468
467
  run_async=False)
@@ -471,6 +470,8 @@ class AsyncServer(base_server.BaseServer):
471
470
  self.logger.warning('Application rejected connection')
472
471
  return self._unauthorized(ret or None)
473
472
 
473
+ s.schedule_ping()
474
+
474
475
  if transport == 'websocket':
475
476
  ret = await s.handle_get_request(environ)
476
477
  if s.closed and sid in self.sockets:
@@ -128,10 +128,12 @@ class AsyncSocket(base_socket.BaseSocket):
128
128
  await self.queue.join()
129
129
 
130
130
  def schedule_ping(self):
131
- self.server.start_background_task(self._send_ping)
131
+ # only schedule a new ping if the previous ping wait cycle completed
132
+ if self.last_ping:
133
+ self.last_ping = None
134
+ self.server.start_background_task(self._send_ping)
132
135
 
133
136
  async def _send_ping(self):
134
- self.last_ping = None
135
137
  await asyncio.sleep(self.server.ping_interval)
136
138
  if not self.closing and not self.closed:
137
139
  self.last_ping = time.time()
@@ -1,3 +1,6 @@
1
+ import time
2
+
3
+
1
4
  class BaseSocket:
2
5
  upgrade_protocols = ['websocket']
3
6
 
@@ -5,7 +8,7 @@ class BaseSocket:
5
8
  self.server = server
6
9
  self.sid = sid
7
10
  self.queue = self.server.create_queue()
8
- self.last_ping = None
11
+ self.last_ping = time.time()
9
12
  self.connected = False
10
13
  self.upgrading = False
11
14
  self.upgraded = False
@@ -521,7 +521,7 @@ class Client(base_client.BaseClient):
521
521
 
522
522
  def _read_loop_websocket(self):
523
523
  """Read packets from the Engine.IO WebSocket connection."""
524
- while self.state == 'connected':
524
+ while self.state == 'connected' and self.write_loop_task:
525
525
  p = None
526
526
  try:
527
527
  p = self.ws.recv()
@@ -616,7 +616,6 @@ class Client(base_client.BaseClient):
616
616
  if r.status_code < 200 or r.status_code >= 300:
617
617
  self.logger.warning('Unexpected status code %s in server '
618
618
  'response, aborting', r.status_code)
619
- self.write_loop_task = None
620
619
  break
621
620
  else:
622
621
  # websocket
@@ -634,3 +633,4 @@ class Client(base_client.BaseClient):
634
633
  'WebSocket connection was closed, aborting')
635
634
  break
636
635
  self.logger.info('Exiting write loop task')
636
+ self.write_loop_task = None
@@ -401,7 +401,6 @@ class Server(base_server.BaseServer):
401
401
  'maxPayload': self.max_http_buffer_size,
402
402
  })
403
403
  s.send(pkt)
404
- s.schedule_ping()
405
404
 
406
405
  # NOTE: some sections below are marked as "no cover" to workaround
407
406
  # what seems to be a bug in the coverage package. All the lines below
@@ -413,6 +412,8 @@ class Server(base_server.BaseServer):
413
412
  self.logger.warning('Application rejected connection')
414
413
  return self._unauthorized(ret or None)
415
414
 
415
+ s.schedule_ping()
416
+
416
417
  if transport == 'websocket': # pragma: no cover
417
418
  ret = s.handle_get_request(environ, start_response)
418
419
  if s.closed and sid in self.sockets:
@@ -130,10 +130,12 @@ class Socket(base_socket.BaseSocket):
130
130
  self.queue.join()
131
131
 
132
132
  def schedule_ping(self):
133
- self.server.start_background_task(self._send_ping)
133
+ # only schedule a new ping if the previous ping wait cycle completed
134
+ if self.last_ping:
135
+ self.last_ping = None
136
+ self.server.start_background_task(self._send_ping)
134
137
 
135
138
  def _send_ping(self):
136
- self.last_ping = None
137
139
  self.server.sleep(self.server.ping_interval)
138
140
  if not self.closing and not self.closed:
139
141
  self.last_ping = time.time()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-engineio
3
- Version: 4.13.1
3
+ Version: 4.13.3
4
4
  Summary: Engine.IO server and client for Python
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  License: MIT
@@ -3,14 +3,6 @@ MANIFEST.in
3
3
  README.md
4
4
  pyproject.toml
5
5
  tox.ini
6
- docs/Makefile
7
- docs/api.rst
8
- docs/client.rst
9
- docs/conf.py
10
- docs/index.rst
11
- docs/intro.rst
12
- docs/make.bat
13
- docs/server.rst
14
6
  docs/_static/README.md
15
7
  src/engineio/__init__.py
16
8
  src/engineio/async_client.py
@@ -44,7 +36,6 @@ src/python_engineio.egg-info/dependency_links.txt
44
36
  src/python_engineio.egg-info/not-zip-safe
45
37
  src/python_engineio.egg-info/requires.txt
46
38
  src/python_engineio.egg-info/top_level.txt
47
- tests/__init__.py
48
39
  tests/async/__init__.py
49
40
  tests/async/index.html
50
41
  tests/async/test_aiohttp.py
@@ -95,14 +95,30 @@ class TestSocket:
95
95
  s = async_socket.AsyncSocket(mock_server, 'sid')
96
96
  s.send = mock.AsyncMock()
97
97
 
98
- async def schedule_ping():
98
+ async def schedule_ping_and_sleep():
99
99
  s.schedule_ping()
100
100
  await asyncio.sleep(0.05)
101
101
 
102
- await schedule_ping()
102
+ await schedule_ping_and_sleep()
103
103
  assert s.last_ping is not None
104
104
  assert s.send.await_args_list[0][0][0].encode() == '2'
105
105
 
106
+ async def test_schedule_ping_twice(self):
107
+ mock_server = self._get_mock_server()
108
+ mock_server.ping_interval = 0.1
109
+ s = async_socket.AsyncSocket(mock_server, 'sid')
110
+ s.send = mock.AsyncMock()
111
+
112
+ async def schedule_ping_and_sleep():
113
+ s.schedule_ping()
114
+ await asyncio.sleep(0.2)
115
+
116
+ s.schedule_ping()
117
+ assert s.last_ping is None
118
+ await schedule_ping_and_sleep()
119
+ assert s.last_ping is not None
120
+ assert s.send.await_count == 1
121
+
106
122
  async def test_schedule_ping_closed_socket(self):
107
123
  mock_server = self._get_mock_server()
108
124
  mock_server.ping_interval = 0.01
@@ -21,6 +21,7 @@ class TestSocket:
21
21
  mock_server.ping_interval_grace_period = 0.001
22
22
  mock_server.async_handlers = True
23
23
  mock_server.max_http_buffer_size = 128
24
+ mock_server.sleep = time.sleep
24
25
 
25
26
  try:
26
27
  import queue
@@ -100,10 +101,26 @@ class TestSocket:
100
101
  s = socket.Socket(mock_server, 'sid')
101
102
  s.send = mock.MagicMock()
102
103
  s.schedule_ping()
103
- time.sleep(0.05)
104
+ for _ in range(10):
105
+ time.sleep(0.05)
106
+ if s.last_ping is not None:
107
+ break
104
108
  assert s.last_ping is not None
105
109
  assert s.send.call_args_list[0][0][0].encode() == '2'
106
110
 
111
+ def test_schedule_ping_twice(self):
112
+ mock_server = self._get_mock_server()
113
+ mock_server.ping_interval = 0.1
114
+ s = socket.Socket(mock_server, 'sid')
115
+ s.send = mock.MagicMock()
116
+ s.schedule_ping()
117
+ assert s.last_ping is None
118
+ time.sleep(0.01)
119
+ s.schedule_ping()
120
+ time.sleep(0.2)
121
+ assert s.last_ping is not None
122
+ assert s.send.call_count == 1
123
+
107
124
  def test_schedule_ping_closed_socket(self):
108
125
  mock_server = self._get_mock_server()
109
126
  mock_server.ping_interval = 0.01
@@ -1,16 +1,3 @@
1
- [tox]
2
- envlist=flake8,py310,py311,py312,py313,py314,pypy3,docs
3
- skip_missing_interpreters=True
4
-
5
- [gh-actions]
6
- python =
7
- 3.10: py310
8
- 3.11: py311
9
- 3.12: py312
10
- 3.13: py313
11
- 3.14: py314
12
- pypy-3: pypy3
13
-
14
1
  [testenv]
15
2
  commands=
16
3
  pip install -e .
@@ -24,8 +11,6 @@ deps=
24
11
  requests
25
12
  websocket-client
26
13
 
27
- [testenv:pypy3]
28
-
29
14
  [testenv:flake8]
30
15
  deps=
31
16
  flake8
@@ -1,19 +0,0 @@
1
- # Minimal makefile for Sphinx documentation
2
- #
3
-
4
- # You can set these variables from the command line.
5
- SPHINXOPTS =
6
- SPHINXBUILD = sphinx-build
7
- SOURCEDIR = .
8
- BUILDDIR = _build
9
-
10
- # Put it first so that "make" without argument is like "make help".
11
- help:
12
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13
-
14
- .PHONY: help Makefile
15
-
16
- # Catch-all target: route all unknown targets to Sphinx using the new
17
- # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18
- %: Makefile
19
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -1,14 +0,0 @@
1
- API Reference
2
- =============
3
-
4
- .. toctree::
5
- :maxdepth: 2
6
-
7
- api_client
8
- api_async_client
9
- api_server
10
- api_async_server
11
- api_wsgiapp
12
- api_asgiapp
13
- api_middleware
14
-
@@ -1,211 +0,0 @@
1
- The Engine.IO Client
2
- ====================
3
-
4
- This package contains two Engine.IO clients:
5
-
6
- - The :func:`engineio.Client` class creates a client compatible with the
7
- standard Python library.
8
- - The :func:`engineio.AsyncClient` class creates a client compatible with
9
- the ``asyncio`` package.
10
-
11
- The methods in the two clients are the same, with the only difference that in
12
- the ``asyncio`` client most methods are implemented as coroutines.
13
-
14
- Installation
15
- ------------
16
-
17
- To install the standard Python client along with its dependencies, use the
18
- following command::
19
-
20
- pip install "python-engineio[client]"
21
-
22
- If instead you plan on using the ``asyncio`` client, then use this::
23
-
24
- pip install "python-engineio[asyncio_client]"
25
-
26
- Creating a Client Instance
27
- --------------------------
28
-
29
- To instantiate an Engine.IO client, simply create an instance of the
30
- appropriate client class::
31
-
32
- import engineio
33
-
34
- # standard Python
35
- eio = engineio.Client()
36
-
37
- # asyncio
38
- eio = engineio.AsyncClient()
39
-
40
- Defining Event Handlers
41
- -----------------------
42
-
43
- To responds to events triggered by the connection or the server, event Handler
44
- functions must be defined using the ``on`` decorator::
45
-
46
- @eio.on('connect')
47
- def on_connect():
48
- print('I'm connected!')
49
-
50
- @eio.on('message')
51
- def on_message(data):
52
- print('I received a message!')
53
-
54
- @eio.on('disconnect')
55
- def on_disconnect(reason):
56
- print('I'm disconnected! reason:', reason)
57
-
58
- For the ``asyncio`` server, event handlers can be regular functions as above,
59
- or can also be coroutines::
60
-
61
- @eio.on('message')
62
- async def on_message(data):
63
- print('I received a message!')
64
-
65
- The argument given to the ``on`` decorator is the event name. The events that
66
- are supported are ``connect``, ``message`` and ``disconnect``.
67
-
68
- The ``data`` argument passed to the ``'message'`` event handler contains
69
- application-specific data provided by the server with the event.
70
-
71
- The ``disconnect`` handler is invoked for client initiated disconnects, server
72
- initiated disconnects, or accidental disconnects, for example due to
73
- networking failures. The argument passed to this handler provides the
74
- disconnect reason. Example::
75
-
76
- @eio.on('disconnect')
77
- def on_disconnect(reason):
78
- if reason == eio.reason.CLIENT_DISCONNECT:
79
- print('client disconnection')
80
- elif reason == eio.reason.SERVER_DISCONNECT:
81
- print('the server kicked me out')
82
- else:
83
- print(f'disconnect reason: {reason}')
84
-
85
- Connecting to a Server
86
- ----------------------
87
-
88
- The connection to a server is established by calling the ``connect()``
89
- method::
90
-
91
- eio.connect('http://localhost:5000')
92
-
93
- In the case of the ``asyncio`` client, the method is a coroutine::
94
-
95
- await eio.connect('http://localhost:5000')
96
-
97
- Upon connection, the server assigns the client a unique session identifier.
98
- The applicaction can find this identifier in the ``sid`` attribute::
99
-
100
- print('my sid is', eio.sid)
101
-
102
- Sending Messages
103
- ----------------
104
-
105
- The client can send a message to the server using the ``send()`` method::
106
-
107
- eio.send({'foo': 'bar'})
108
-
109
- Or in the case of ``asyncio``, as a coroutine::
110
-
111
- await eio.send({'foo': 'bar'})
112
-
113
- The single argument provided to the method is the data that is passed on
114
- to the server. The data can be of type ``str``, ``bytes``, ``dict`` or
115
- ``list``. The data included inside dictionaries and lists is also
116
- constrained to these types.
117
-
118
- The ``send()`` method can be invoked inside an event handler as a response
119
- to a server event, or in any other part of the application, including in
120
- background tasks.
121
-
122
- Disconnecting from the Server
123
- -----------------------------
124
-
125
- At any time the client can request to be disconnected from the server by
126
- invoking the ``disconnect()`` method::
127
-
128
- eio.disconnect()
129
-
130
- For the ``asyncio`` client this is a coroutine::
131
-
132
- await eio.disconnect()
133
-
134
- Managing Background Tasks
135
- -------------------------
136
-
137
- When a client connection to the server is established, a few background
138
- tasks will be spawned to keep the connection alive and handle incoming
139
- events. The application running on the main thread is free to do any
140
- work, as this is not going to prevent the functioning of the Engine.IO
141
- client.
142
-
143
- If the application does not have anything to do in the main thread and
144
- just wants to wait until the connection ends, it can call the ``wait()``
145
- method::
146
-
147
- eio.wait()
148
-
149
- Or in the ``asyncio`` version::
150
-
151
- await eio.wait()
152
-
153
- For the convenience of the application, a helper function is
154
- provided to start a custom background task::
155
-
156
- def my_background_task(my_argument)
157
- # do some background work here!
158
- pass
159
-
160
- eio.start_background_task(my_background_task, 123)
161
-
162
- The arguments passed to this method are the background function and any
163
- positional or keyword arguments to invoke the function with.
164
-
165
- Here is the ``asyncio`` version::
166
-
167
- async def my_background_task(my_argument)
168
- # do some background work here!
169
- pass
170
-
171
- eio.start_background_task(my_background_task, 123)
172
-
173
- Note that this function is not a coroutine, since it does not wait for the
174
- background function to end, but the background function is.
175
-
176
- The ``sleep()`` method is a second convenience function that is provided for
177
- the benefit of applications working with background tasks of their own::
178
-
179
- eio.sleep(2)
180
-
181
- Or for ``asyncio``::
182
-
183
- await eio.sleep(2)
184
-
185
- The single argument passed to the method is the number of seconds to sleep
186
- for.
187
-
188
- Debugging and Troubleshooting
189
- -----------------------------
190
-
191
- To help you debug issues, the client can be configured to output logs to the
192
- terminal::
193
-
194
- import engineio
195
-
196
- # standard Python
197
- eio = engineio.Client(logger=True)
198
-
199
- # asyncio
200
- eio = engineio.AsyncClient(logger=True)
201
-
202
- The ``logger`` argument controls logging behavior:
203
-
204
- * ``True``: Enables log output to ``stderr`` at the ``INFO`` level.
205
- * ``False``: Enables log output to ``stderr`` at the ``ERROR`` level. This is
206
- the default.
207
- * A ``logging.Logger`` instance: Uses the provided logger without additional
208
- configuration.
209
-
210
- Logging can help identify the cause of connection problems, unexpected
211
- disconnections and other issues.