python-engineio 4.12.3__tar.gz → 4.13.1__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 (75) hide show
  1. {python_engineio-4.12.3/src/python_engineio.egg-info → python_engineio-4.13.1}/PKG-INFO +6 -3
  2. python_engineio-4.13.1/docs/api.rst +14 -0
  3. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/client.rst +7 -3
  4. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/conf.py +2 -11
  5. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/index.rst +1 -1
  6. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/server.rst +7 -3
  7. {python_engineio-4.12.3 → python_engineio-4.13.1}/pyproject.toml +12 -4
  8. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_client.py +11 -8
  9. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/aiohttp.py +9 -16
  10. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/asgi.py +3 -3
  11. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/tornado.py +2 -1
  12. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_server.py +8 -6
  13. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/client.py +9 -5
  14. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/server.py +4 -3
  15. {python_engineio-4.12.3 → python_engineio-4.13.1/src/python_engineio.egg-info}/PKG-INFO +6 -3
  16. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/python_engineio.egg-info/requires.txt +5 -1
  17. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_aiohttp.py +19 -6
  18. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_client.py +17 -1
  19. {python_engineio-4.12.3 → python_engineio-4.13.1}/tox.ini +3 -3
  20. python_engineio-4.12.3/docs/api.rst +0 -33
  21. {python_engineio-4.12.3 → python_engineio-4.13.1}/LICENSE +0 -0
  22. {python_engineio-4.12.3 → python_engineio-4.13.1}/MANIFEST.in +0 -0
  23. {python_engineio-4.12.3 → python_engineio-4.13.1}/README.md +0 -0
  24. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/Makefile +0 -0
  25. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/_static/README.md +0 -0
  26. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/intro.rst +0 -0
  27. {python_engineio-4.12.3 → python_engineio-4.13.1}/docs/make.bat +0 -0
  28. {python_engineio-4.12.3 → python_engineio-4.13.1}/setup.cfg +0 -0
  29. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/__init__.py +0 -0
  30. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/__init__.py +0 -0
  31. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/_websocket_wsgi.py +0 -0
  32. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/eventlet.py +0 -0
  33. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/gevent.py +0 -0
  34. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/gevent_uwsgi.py +0 -0
  35. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/sanic.py +0 -0
  36. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_drivers/threading.py +0 -0
  37. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/async_socket.py +0 -0
  38. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/base_client.py +0 -0
  39. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/base_server.py +0 -0
  40. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/base_socket.py +0 -0
  41. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/exceptions.py +0 -0
  42. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/json.py +0 -0
  43. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/middleware.py +0 -0
  44. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/packet.py +0 -0
  45. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/payload.py +0 -0
  46. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/socket.py +0 -0
  47. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/engineio/static_files.py +0 -0
  48. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/python_engineio.egg-info/SOURCES.txt +0 -0
  49. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/python_engineio.egg-info/dependency_links.txt +0 -0
  50. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/python_engineio.egg-info/not-zip-safe +0 -0
  51. {python_engineio-4.12.3 → python_engineio-4.13.1}/src/python_engineio.egg-info/top_level.txt +0 -0
  52. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/__init__.py +0 -0
  53. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/__init__.py +0 -0
  54. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/index.html +0 -0
  55. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_asgi.py +0 -0
  56. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_sanic.py +0 -0
  57. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_server.py +0 -0
  58. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_socket.py +0 -0
  59. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/async/test_tornado.py +0 -0
  60. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/__init__.py +0 -0
  61. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/index.html +0 -0
  62. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_client.py +0 -0
  63. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_middleware.py +0 -0
  64. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_packet.py +0 -0
  65. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_payload.py +0 -0
  66. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_server.py +0 -0
  67. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/common/test_socket.py +0 -0
  68. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/README.md +0 -0
  69. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/binary_b64_packet.py +0 -0
  70. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/binary_packet.py +0 -0
  71. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/json_packet.py +0 -0
  72. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/payload.py +0 -0
  73. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/run.sh +0 -0
  74. {python_engineio-4.12.3 → python_engineio-4.13.1}/tests/performance/server_receive.py +0 -0
  75. {python_engineio-4.12.3 → python_engineio-4.13.1}/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.12.3
3
+ Version: 4.13.1
4
4
  Summary: Engine.IO server and client for Python
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  License: MIT
@@ -10,7 +10,7 @@ Classifier: Environment :: Web Environment
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Operating System :: OS Independent
13
- Requires-Python: >=3.6
13
+ Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: simple-websocket>=0.10.0
@@ -18,9 +18,12 @@ Provides-Extra: client
18
18
  Requires-Dist: requests>=2.21.0; extra == "client"
19
19
  Requires-Dist: websocket-client>=0.54.0; extra == "client"
20
20
  Provides-Extra: asyncio-client
21
- Requires-Dist: aiohttp>=3.4; extra == "asyncio-client"
21
+ Requires-Dist: aiohttp>=3.11; extra == "asyncio-client"
22
+ Provides-Extra: dev
23
+ Requires-Dist: tox; extra == "dev"
22
24
  Provides-Extra: docs
23
25
  Requires-Dist: sphinx; extra == "docs"
26
+ Requires-Dist: furo; extra == "docs"
24
27
  Dynamic: license-file
25
28
 
26
29
  python-engineio
@@ -0,0 +1,14 @@
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
+
@@ -199,9 +199,13 @@ terminal::
199
199
  # asyncio
200
200
  eio = engineio.AsyncClient(logger=True)
201
201
 
202
- The ``logger`` argument can be set to ``True`` to output logs to ``stderr``, or
203
- to an object compatible with Python's ``logging`` package where the logs should
204
- be emitted to. A value of ``False`` disables logging.
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.
205
209
 
206
210
  Logging can help identify the cause of connection problems, unexpected
207
211
  disconnections and other issues.
@@ -67,29 +67,20 @@ language = None
67
67
  # This pattern also affects html_static_path and html_extra_path.
68
68
  exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
69
69
 
70
- # The name of the Pygments (syntax highlighting) style to use.
71
- pygments_style = None
72
-
73
70
 
74
71
  # -- Options for HTML output -------------------------------------------------
75
72
 
76
73
  # The theme to use for HTML and HTML Help pages. See the documentation for
77
74
  # a list of builtin themes.
78
75
  #
79
- html_theme = 'alabaster'
76
+ html_theme = 'furo'
77
+ html_title = 'python-engineio'
80
78
 
81
79
  # Theme options are theme-specific and customize the look and feel of a theme
82
80
  # further. For a list of options available for each theme, see the
83
81
  # documentation.
84
82
  #
85
83
  html_theme_options = {
86
- 'github_user': 'miguelgrinberg',
87
- 'github_repo': 'python-engineio',
88
- 'github_banner': True,
89
- 'github_button': True,
90
- 'github_type': 'star',
91
- 'fixed_sidebar': True,
92
-
93
84
  }
94
85
 
95
86
  # Add any paths that contain custom static files (such as style sheets) here,
@@ -11,7 +11,7 @@ standalone or integrated with a variety of Python web frameworks and
11
11
  applications.
12
12
 
13
13
  .. toctree::
14
- :maxdepth: 3
14
+ :maxdepth: 2
15
15
 
16
16
  intro
17
17
  client
@@ -350,9 +350,13 @@ terminal::
350
350
  # asyncio
351
351
  eio = engineio.AsyncServer(logger=True)
352
352
 
353
- The ``logger`` argument can be set to ``True`` to output logs to ``stderr``, or
354
- to an object compatible with Python's ``logging`` package where the logs should
355
- be emitted to. A value of ``False`` disables logging.
353
+ The ``logger`` argument controls logging behavior:
354
+
355
+ * ``True``: Enables log output to ``stderr`` at the ``INFO`` level.
356
+ * ``False``: Enables log output to ``stderr`` at the ``ERROR`` level. This is
357
+ the default.
358
+ * A ``logging.Logger`` instance: Uses the provided logger without additional
359
+ configuration.
356
360
 
357
361
  Logging can help identify the cause of connection problems, 400 responses,
358
362
  bad performance and other issues.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-engineio"
3
- version = "4.12.3"
3
+ version = "4.13.1"
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"
@@ -10,7 +10,7 @@ classifiers = [
10
10
  "Programming Language :: Python :: 3",
11
11
  "Operating System :: OS Independent",
12
12
  ]
13
- requires-python = ">=3.6"
13
+ requires-python = ">=3.8"
14
14
  dependencies = ["simple-websocket >= 0.10.0"]
15
15
 
16
16
  [project.readme]
@@ -26,8 +26,16 @@ client = [
26
26
  "requests >= 2.21.0",
27
27
  "websocket-client >= 0.54.0",
28
28
  ]
29
- asyncio_client = ["aiohttp >= 3.4"]
30
- docs = ["sphinx"]
29
+ asyncio_client = [
30
+ "aiohttp >= 3.11"
31
+ ]
32
+ dev = [
33
+ "tox",
34
+ ]
35
+ docs = [
36
+ "sphinx",
37
+ "furo",
38
+ ]
31
39
 
32
40
  [tool.setuptools]
33
41
  zip-safe = false
@@ -1,4 +1,6 @@
1
1
  import asyncio
2
+ from http.cookies import SimpleCookie
3
+ import inspect
2
4
  import signal
3
5
  import ssl
4
6
  import threading
@@ -53,10 +55,11 @@ class AsyncClient(base_client.BaseClient):
53
55
  use. To disable logging set to ``False``. The default is
54
56
  ``False``. Note that fatal errors are logged even when
55
57
  ``logger`` is ``False``.
56
- :param json: An alternative json module to use for encoding and decoding
58
+ :param json: An alternative JSON module to use for encoding and decoding
57
59
  packets. Custom json modules must have ``dumps`` and ``loads``
58
60
  functions that are compatible with the standard library
59
- versions.
61
+ versions. This is a process-wide setting, all instantiated
62
+ servers and clients must use the same JSON module.
60
63
  :param request_timeout: A timeout in seconds for requests. The default is
61
64
  5 seconds.
62
65
  :param http_session: an initialized ``aiohttp.ClientSession`` object to be
@@ -319,16 +322,16 @@ class AsyncClient(base_client.BaseClient):
319
322
 
320
323
  # extract any new cookies passed in a header so that they can also be
321
324
  # sent the the WebSocket route
322
- cookies = {}
323
325
  for header, value in headers.items():
324
326
  if header.lower() == 'cookie':
325
- cookies = dict(
326
- [cookie.split('=', 1) for cookie in value.split('; ')])
327
+ ck = SimpleCookie(headers[header])
328
+ self.http.cookie_jar.update_cookies(
329
+ {k: m.value for k, m in ck.items()})
327
330
  del headers[header]
328
331
  break
329
- self.http.cookie_jar.update_cookies(cookies)
330
332
 
331
- extra_options = {'timeout': self.request_timeout}
333
+ extra_options = {
334
+ 'timeout': aiohttp.ClientWSTimeout(ws_close=self.request_timeout)}
332
335
  if not self.ssl_verify:
333
336
  ssl_context = ssl.create_default_context()
334
337
  ssl_context.check_hostname = False
@@ -468,7 +471,7 @@ class AsyncClient(base_client.BaseClient):
468
471
  run_async = kwargs.pop('run_async', False)
469
472
  ret = None
470
473
  if event in self.handlers:
471
- if asyncio.iscoroutinefunction(self.handlers[event]) is True:
474
+ if inspect.iscoroutinefunction(self.handlers[event]) is True:
472
475
  if run_async:
473
476
  task = self.start_background_task(self.handlers[event],
474
477
  *args)
@@ -1,6 +1,5 @@
1
- import asyncio
1
+ import inspect
2
2
  import sys
3
- from urllib.parse import urlsplit
4
3
 
5
4
  from aiohttp.web import Response, WebSocketResponse
6
5
 
@@ -22,12 +21,8 @@ def translate_request(request):
22
21
  """This function takes the arguments passed to the request handler and
23
22
  uses them to generate a WSGI compatible environ dictionary.
24
23
  """
25
- message = request._message
26
- payload = request._payload
27
-
28
- uri_parts = urlsplit(message.path)
29
24
  environ = {
30
- 'wsgi.input': payload,
25
+ 'wsgi.input': request.content,
31
26
  'wsgi.errors': sys.stderr,
32
27
  'wsgi.version': (1, 0),
33
28
  'wsgi.async': True,
@@ -35,10 +30,10 @@ def translate_request(request):
35
30
  'wsgi.multiprocess': False,
36
31
  'wsgi.run_once': False,
37
32
  'SERVER_SOFTWARE': 'aiohttp',
38
- 'REQUEST_METHOD': message.method,
39
- 'QUERY_STRING': uri_parts.query or '',
40
- 'RAW_URI': message.path,
41
- 'SERVER_PROTOCOL': 'HTTP/%s.%s' % message.version,
33
+ 'REQUEST_METHOD': request.method,
34
+ 'QUERY_STRING': request.query_string or '',
35
+ 'RAW_URI': request.path_qs,
36
+ 'SERVER_PROTOCOL': f'HTTP/{request.version[0]}.{request.version[1]}',
42
37
  'REMOTE_ADDR': '127.0.0.1',
43
38
  'REMOTE_PORT': '0',
44
39
  'SERVER_NAME': 'aiohttp',
@@ -46,7 +41,7 @@ def translate_request(request):
46
41
  'aiohttp.request': request
47
42
  }
48
43
 
49
- for hdr_name, hdr_value in message.headers.items():
44
+ for hdr_name, hdr_value in request.headers.items():
50
45
  hdr_name = hdr_name.upper()
51
46
  if hdr_name == 'CONTENT-TYPE':
52
47
  environ['CONTENT_TYPE'] = hdr_value
@@ -63,9 +58,7 @@ def translate_request(request):
63
58
 
64
59
  environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')
65
60
 
66
- path_info = uri_parts.path
67
-
68
- environ['PATH_INFO'] = path_info
61
+ environ['PATH_INFO'] = request.path
69
62
  environ['SCRIPT_NAME'] = ''
70
63
 
71
64
  return environ
@@ -105,7 +98,7 @@ class WebSocket: # pragma: no cover
105
98
  f = self._sock.send_bytes
106
99
  else:
107
100
  f = self._sock.send_str
108
- if asyncio.iscoroutinefunction(f):
101
+ if inspect.iscoroutinefunction(f):
109
102
  await f(message)
110
103
  else:
111
104
  f(message)
@@ -1,6 +1,6 @@
1
+ import inspect
1
2
  import os
2
3
  import sys
3
- import asyncio
4
4
 
5
5
  from engineio.static_files import get_static_file
6
6
 
@@ -102,7 +102,7 @@ class ASGIApp:
102
102
  if self.on_startup:
103
103
  try:
104
104
  await self.on_startup() \
105
- if asyncio.iscoroutinefunction(self.on_startup) \
105
+ if inspect.iscoroutinefunction(self.on_startup) \
106
106
  else self.on_startup()
107
107
  except:
108
108
  await send({'type': 'lifespan.startup.failed'})
@@ -112,7 +112,7 @@ class ASGIApp:
112
112
  if self.on_shutdown:
113
113
  try:
114
114
  await self.on_shutdown() \
115
- if asyncio.iscoroutinefunction(self.on_shutdown) \
115
+ if inspect.iscoroutinefunction(self.on_shutdown) \
116
116
  else self.on_shutdown()
117
117
  except:
118
118
  await send({'type': 'lifespan.shutdown.failed'})
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import inspect
2
3
  import sys
3
4
  from urllib.parse import urlsplit
4
5
  from .. import exceptions
@@ -24,7 +25,7 @@ def get_tornado_handler(engineio_server):
24
25
  async def get(self, *args, **kwargs):
25
26
  if self.request.headers.get('Upgrade', '').lower() == 'websocket':
26
27
  ret = super().get(*args, **kwargs)
27
- if asyncio.iscoroutine(ret):
28
+ if inspect.iscoroutine(ret):
28
29
  await ret
29
30
  else:
30
31
  await engineio_server.handle_request(self)
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import inspect
2
3
  import urllib
3
4
 
4
5
  from . import base_server
@@ -63,10 +64,11 @@ class AsyncServer(base_server.BaseServer):
63
64
  :param logger: To enable logging set to ``True`` or pass a logger object to
64
65
  use. To disable logging set to ``False``. Note that fatal
65
66
  errors are logged even when ``logger`` is ``False``.
66
- :param json: An alternative json module to use for encoding and decoding
67
- packets. Custom json modules must have ``dumps`` and ``loads``
67
+ :param json: An alternative JSON module to use for encoding and decoding
68
+ packets. Custom JSON modules must have ``dumps`` and ``loads``
68
69
  functions that are compatible with the standard library
69
- versions.
70
+ versions. This is a process-wide setting, all instantiated
71
+ servers and clients must use the same JSON module.
70
72
  :param async_handlers: If set to ``True``, run message event handlers in
71
73
  non-blocking threads. To run handlers synchronously,
72
74
  set to ``False``. The default is ``True``.
@@ -212,7 +214,7 @@ class AsyncServer(base_server.BaseServer):
212
214
  Note: this method is a coroutine.
213
215
  """
214
216
  translate_request = self._async['translate_request']
215
- if asyncio.iscoroutinefunction(translate_request):
217
+ if inspect.iscoroutinefunction(translate_request):
216
218
  environ = await translate_request(*args, **kwargs)
217
219
  else:
218
220
  environ = translate_request(*args, **kwargs)
@@ -427,7 +429,7 @@ class AsyncServer(base_server.BaseServer):
427
429
  async def _make_response(self, response_dict, environ):
428
430
  cors_headers = self._cors_headers(environ)
429
431
  make_response = self._async['make_response']
430
- if asyncio.iscoroutinefunction(make_response):
432
+ if inspect.iscoroutinefunction(make_response):
431
433
  response = await make_response(
432
434
  response_dict['status'],
433
435
  response_dict['headers'] + cors_headers,
@@ -502,7 +504,7 @@ class AsyncServer(base_server.BaseServer):
502
504
  run_async = kwargs.pop('run_async', False)
503
505
  ret = None
504
506
  if event in self.handlers:
505
- if asyncio.iscoroutinefunction(self.handlers[event]):
507
+ if inspect.iscoroutinefunction(self.handlers[event]):
506
508
  async def run_async_handler():
507
509
  try:
508
510
  try:
@@ -1,11 +1,12 @@
1
1
  from base64 import b64encode
2
- from engineio.json import JSONDecodeError
2
+ from http.cookies import SimpleCookie
3
3
  import logging
4
4
  import queue
5
5
  import ssl
6
6
  import threading
7
7
  import time
8
8
  import urllib
9
+ from engineio.json import JSONDecodeError
9
10
 
10
11
  try:
11
12
  import requests
@@ -33,10 +34,11 @@ class Client(base_client.BaseClient):
33
34
  use. To disable logging set to ``False``. The default is
34
35
  ``False``. Note that fatal errors are logged even when
35
36
  ``logger`` is ``False``.
36
- :param json: An alternative json module to use for encoding and decoding
37
+ :param json: An alternative JSON module to use for encoding and decoding
37
38
  packets. Custom json modules must have ``dumps`` and ``loads``
38
39
  functions that are compatible with the standard library
39
- versions.
40
+ versions. This is a process-wide setting, all instantiated
41
+ servers and clients must use the same JSON module.
40
42
  :param request_timeout: A timeout in seconds for requests. The default is
41
43
  5 seconds.
42
44
  :param http_session: an initialized ``requests.Session`` object to be used
@@ -268,8 +270,10 @@ class Client(base_client.BaseClient):
268
270
  extra_options = {}
269
271
  if self.http:
270
272
  # cookies
271
- cookies = '; '.join([f"{cookie.name}={cookie.value}"
272
- for cookie in self.http.cookies])
273
+ ck = SimpleCookie()
274
+ for cookie in self.http.cookies:
275
+ ck[cookie.name] = cookie.value
276
+ cookies = ck.output(header='', sep=';').strip()
273
277
  for header, value in headers.items():
274
278
  if header.lower() == 'cookie':
275
279
  if cookies:
@@ -63,10 +63,11 @@ class Server(base_server.BaseServer):
63
63
  use. To disable logging set to ``False``. The default is
64
64
  ``False``. Note that fatal errors are logged even when
65
65
  ``logger`` is ``False``.
66
- :param json: An alternative json module to use for encoding and decoding
67
- packets. Custom json modules must have ``dumps`` and ``loads``
66
+ :param json: An alternative JSON module to use for encoding and decoding
67
+ packets. Custom JSON modules must have ``dumps`` and ``loads``
68
68
  functions that are compatible with the standard library
69
- versions.
69
+ versions. This is a process-wide setting, all instantiated
70
+ servers and clients must use the same JSON module.
70
71
  :param async_handlers: If set to ``True``, run message event handlers in
71
72
  non-blocking threads. To run handlers synchronously,
72
73
  set to ``False``. The default is ``True``.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-engineio
3
- Version: 4.12.3
3
+ Version: 4.13.1
4
4
  Summary: Engine.IO server and client for Python
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  License: MIT
@@ -10,7 +10,7 @@ Classifier: Environment :: Web Environment
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Operating System :: OS Independent
13
- Requires-Python: >=3.6
13
+ Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: simple-websocket>=0.10.0
@@ -18,9 +18,12 @@ Provides-Extra: client
18
18
  Requires-Dist: requests>=2.21.0; extra == "client"
19
19
  Requires-Dist: websocket-client>=0.54.0; extra == "client"
20
20
  Provides-Extra: asyncio-client
21
- Requires-Dist: aiohttp>=3.4; extra == "asyncio-client"
21
+ Requires-Dist: aiohttp>=3.11; extra == "asyncio-client"
22
+ Provides-Extra: dev
23
+ Requires-Dist: tox; extra == "dev"
22
24
  Provides-Extra: docs
23
25
  Requires-Dist: sphinx; extra == "docs"
26
+ Requires-Dist: furo; extra == "docs"
24
27
  Dynamic: license-file
25
28
 
26
29
  python-engineio
@@ -1,11 +1,15 @@
1
1
  simple-websocket>=0.10.0
2
2
 
3
3
  [asyncio_client]
4
- aiohttp>=3.4
4
+ aiohttp>=3.11
5
5
 
6
6
  [client]
7
7
  requests>=2.21.0
8
8
  websocket-client>=0.54.0
9
9
 
10
+ [dev]
11
+ tox
12
+
10
13
  [docs]
11
14
  sphinx
15
+ furo
@@ -1,5 +1,8 @@
1
1
  from unittest import mock
2
2
 
3
+ import yarl
4
+ from aiohttp.web import Request
5
+
3
6
  from engineio.async_drivers import aiohttp as async_aiohttp
4
7
 
5
8
 
@@ -12,18 +15,28 @@ class TestAiohttp:
12
15
  app.router.add_post.assert_any_call('/foo', mock_server.handle_request)
13
16
 
14
17
  def test_translate_request(self):
15
- request = mock.MagicMock()
16
- request._message.method = 'PUT'
17
- request._message.path = '/foo/bar?baz=1'
18
- request._message.version = (1, 1)
19
- request._message.headers = {
18
+ message = mock.MagicMock()
19
+ message.url = yarl.URL('https://example.com/foo/bar?baz=1')
20
+ message.method = 'PUT'
21
+ message.path = '/foo/bar?baz=1'
22
+ message.version = (1, 1)
23
+ message.headers = {
20
24
  'a': 'b',
21
25
  'c-c': 'd',
22
26
  'c_c': 'e',
23
27
  'content-type': 'application/json',
24
28
  'content-length': 123,
25
29
  }
26
- request._payload = b'hello world'
30
+
31
+ request = Request(
32
+ message,
33
+ payload=b'hello world',
34
+ protocol=mock.MagicMock(),
35
+ payload_writer=mock.MagicMock(),
36
+ task=mock.MagicMock(),
37
+ loop=mock.MagicMock(),
38
+ )
39
+
27
40
  environ = async_aiohttp.translate_request(request)
28
41
  expected_environ = {
29
42
  'REQUEST_METHOD': 'PUT',
@@ -24,6 +24,10 @@ class TestAsyncClient:
24
24
  client.queue.put = mock.AsyncMock()
25
25
  client.queue.join = mock.AsyncMock()
26
26
 
27
+ @staticmethod
28
+ def mock_ws_timeout(ws_close):
29
+ return ws_close
30
+
27
31
  async def test_is_asyncio_based(self):
28
32
  c = async_client.AsyncClient()
29
33
  assert c.is_asyncio_based()
@@ -500,6 +504,8 @@ class TestAsyncClient:
500
504
  assert c in base_client.connected_clients
501
505
 
502
506
  @mock.patch('engineio.client.time.time', return_value=123.456)
507
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
508
+ new=mock_ws_timeout)
503
509
  async def test_websocket_connection_failed(self, _time):
504
510
  c = async_client.AsyncClient()
505
511
  c.http = mock.MagicMock(closed=False)
@@ -541,6 +547,8 @@ class TestAsyncClient:
541
547
  )
542
548
 
543
549
  @mock.patch('engineio.client.time.time', return_value=123.456)
550
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
551
+ new=mock_ws_timeout)
544
552
  async def test_websocket_upgrade_failed(self, _time):
545
553
  c = async_client.AsyncClient()
546
554
  c.http = mock.MagicMock(closed=False)
@@ -568,6 +576,8 @@ class TestAsyncClient:
568
576
  await c.connect('http://foo', transports=['websocket'])
569
577
 
570
578
  @mock.patch('engineio.client.time.time', return_value=123.456)
579
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
580
+ new=mock_ws_timeout)
571
581
  async def test_websocket_connection_successful(self, _time):
572
582
  c = async_client.AsyncClient()
573
583
  c.http = mock.MagicMock(closed=False)
@@ -649,6 +659,8 @@ class TestAsyncClient:
649
659
  assert kwargs['ssl'].verify_mode == ssl.CERT_NONE
650
660
 
651
661
  @mock.patch('engineio.client.time.time', return_value=123.456)
662
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
663
+ new=mock_ws_timeout)
652
664
  async def test_websocket_connection_with_cookies(self, _time):
653
665
  c = async_client.AsyncClient()
654
666
  c.http = mock.MagicMock(closed=False)
@@ -682,6 +694,8 @@ class TestAsyncClient:
682
694
  )
683
695
 
684
696
  @mock.patch('engineio.client.time.time', return_value=123.456)
697
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
698
+ new=mock_ws_timeout)
685
699
  async def test_websocket_connection_with_cookie_header(self, _time):
686
700
  c = async_client.AsyncClient()
687
701
  c.http = mock.MagicMock(closed=False)
@@ -714,10 +728,12 @@ class TestAsyncClient:
714
728
  timeout=5,
715
729
  )
716
730
  c.http.cookie_jar.update_cookies.assert_called_once_with(
717
- {'key': 'value', 'key2': 'value2', 'key3': '"value3="'}
731
+ {'key': 'value', 'key2': 'value2', 'key3': 'value3='}
718
732
  )
719
733
 
720
734
  @mock.patch('engineio.client.time.time', return_value=123.456)
735
+ @mock.patch('engineio.async_client.aiohttp.ClientWSTimeout',
736
+ new=mock_ws_timeout)
721
737
  async def test_websocket_connection_with_cookies_and_headers(self, _time):
722
738
  c = async_client.AsyncClient()
723
739
  c.http = mock.MagicMock(closed=False)
@@ -1,15 +1,14 @@
1
1
  [tox]
2
- envlist=flake8,py38,py39,py310,py311,py312,py313,pypy3,docs
2
+ envlist=flake8,py310,py311,py312,py313,py314,pypy3,docs
3
3
  skip_missing_interpreters=True
4
4
 
5
5
  [gh-actions]
6
6
  python =
7
- 3.8: py38
8
- 3.9: py39
9
7
  3.10: py310
10
8
  3.11: py311
11
9
  3.12: py312
12
10
  3.13: py313
11
+ 3.14: py314
13
12
  pypy-3: pypy3
14
13
 
15
14
  [testenv]
@@ -37,6 +36,7 @@ commands=
37
36
  changedir=docs
38
37
  deps=
39
38
  sphinx
39
+ furo
40
40
  allowlist_externals=
41
41
  make
42
42
  commands=
@@ -1,33 +0,0 @@
1
- API Reference
2
- =============
3
-
4
- .. toctree::
5
- :maxdepth: 3
6
-
7
- .. module:: engineio
8
-
9
- .. autoclass:: Client
10
- :members:
11
- :inherited-members:
12
-
13
- .. autoclass:: AsyncClient
14
- :members:
15
- :inherited-members:
16
-
17
- .. autoclass:: Server
18
- :members:
19
- :inherited-members:
20
-
21
- .. autoclass:: AsyncServer
22
- :members:
23
- :inherited-members:
24
-
25
- .. autoclass:: WSGIApp
26
- :members:
27
-
28
- .. autoclass:: ASGIApp
29
- :members:
30
-
31
- .. autoclass:: Middleware
32
- :members:
33
-