Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-python-socketio for
openSUSE:Factory checked in at 2025-10-31 16:28:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-socketio (Old)
and /work/SRC/openSUSE:Factory/.python-python-socketio.new.1980 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-socketio"
Fri Oct 31 16:28:39 2025 rev:14 rq:1314808 version:5.14.3
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-python-socketio/python-python-socketio.changes
2025-10-08 18:18:44.977053691 +0200
+++
/work/SRC/openSUSE:Factory/.python-python-socketio.new.1980/python-python-socketio.changes
2025-10-31 16:29:46.022241642 +0100
@@ -1,0 +2,14 @@
+Fri Oct 31 11:40:10 UTC 2025 - Dirk Müller <[email protected]>
+
+- update to 5.14.3:
+ * Support Python's native `ConnectionRefusedError` exception to
+ reject a connection #1515 (commit)
+ * Push binary data to the aiopika client manager #1514 (commit)
+ * Restore binary message support in message queue setups #1509
+ (commit)
+ * Fix formatting of client connection error #1507 (commit)
+ * Add 3.14 and pypy-3.11 CI tasks (commit)
+ * Improve documentation of the `BaseManager.get_participants()`
+ method (commit)
+
+-------------------------------------------------------------------
Old:
----
python_socketio-5.14.1.tar.gz
New:
----
python_socketio-5.14.3.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-socketio.spec ++++++
--- /var/tmp/diff_new_pack.TMYpdo/_old 2025-10-31 16:29:46.590265777 +0100
+++ /var/tmp/diff_new_pack.TMYpdo/_new 2025-10-31 16:29:46.594265947 +0100
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-python-socketio
-Version: 5.14.1
+Version: 5.14.3
Release: 0
Summary: SocketIO server
License: MIT
++++++ python_socketio-5.14.1.tar.gz -> python_socketio-5.14.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/.github/workflows/tests.yml
new/python-socketio-5.14.3/.github/workflows/tests.yml
--- old/python-socketio-5.14.1/.github/workflows/tests.yml 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/.github/workflows/tests.yml 2025-10-29
10:42:20.000000000 +0100
@@ -16,11 +16,7 @@
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
- python: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.10']
- exclude:
- # pypy3 currently fails to run on Windows
- - os: windows-latest
- python: pypy-3.10
+ python: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14',
'pypy-3.11']
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/CHANGES.md
new/python-socketio-5.14.3/CHANGES.md
--- old/python-socketio-5.14.1/CHANGES.md 2025-10-02 20:41:26.000000000
+0200
+++ new/python-socketio-5.14.3/CHANGES.md 2025-10-29 10:42:20.000000000
+0100
@@ -1,5 +1,17 @@
# python-socketio change log
+**Release 5.14.3** - 2025-10-29
+
+- Support Python's native `ConnectionRefusedError` exception to reject a
connection
[#1515](https://github.com/miguelgrinberg/python-socketio/issues/1515)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/f3b18bde3f16437b223491d4c3e440ea37105fe3))
+- Push binary data to the aiopika client manager
[#1514](https://github.com/miguelgrinberg/python-socketio/issues/1514)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/194e1b7f277b5f72e1de78d3f614e7b8b6c788ac))
+
+**Release 5.14.2** - 2025-10-15
+
+- Restore binary message support in message queue setups
[#1509](https://github.com/miguelgrinberg/python-socketio/issues/1509)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/bab4a10f48aaae11d7f832ebe5c30ad3f85d31b3))
+- Fix formatting of client connection error
[#1507](https://github.com/miguelgrinberg/python-socketio/issues/1507)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/f298c9b54d76ab09ff72935937e1b9575bc45ffd))
+- Add 3.14 and pypy-3.11 CI tasks
([commit](https://github.com/miguelgrinberg/python-socketio/commit/1f4cd3b025c294f25208ec3c05b5f8df6209e403))
+- Improve documentation of the `BaseManager.get_participants()` method
([commit](https://github.com/miguelgrinberg/python-socketio/commit/33722a0d96036f005188b07b8b46a5ef091fe65f))
+
**Release 5.14.1** - 2025-10-02
- Restore support for `rediss://` URLs, and add support for `valkeys://` as
well
([commit](https://github.com/miguelgrinberg/python-socketio/commit/6e2d0de12bb4e4a99fdfc30bed0706ded620822c))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/docs/server.rst
new/python-socketio-5.14.3/docs/server.rst
--- old/python-socketio-5.14.1/docs/server.rst 2025-10-02 20:41:26.000000000
+0200
+++ new/python-socketio-5.14.3/docs/server.rst 2025-10-29 10:42:20.000000000
+0100
@@ -246,6 +246,8 @@
and all of its arguments will be sent to the client with the rejection
message::
+ from socketio.exceptions import ConnectionRefusedError
+
@sio.event
def connect(sid, environ, auth):
raise ConnectionRefusedError('authentication failed')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/pyproject.toml
new/python-socketio-5.14.3/pyproject.toml
--- old/python-socketio-5.14.1/pyproject.toml 2025-10-02 20:41:26.000000000
+0200
+++ new/python-socketio-5.14.3/pyproject.toml 2025-10-29 10:42:20.000000000
+0100
@@ -1,6 +1,6 @@
[project]
name = "python-socketio"
-version = "5.14.1"
+version = "5.14.3"
license = {text = "MIT"}
authors = [
{ name = "Miguel Grinberg", email = "[email protected]" },
@@ -34,6 +34,9 @@
asyncio_client = [
"aiohttp >= 3.4",
]
+dev = [
+ "tox",
+]
docs = [
"sphinx",
]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.1/src/socketio/async_aiopika_manager.py
new/python-socketio-5.14.3/src/socketio/async_aiopika_manager.py
--- old/python-socketio-5.14.1/src/socketio/async_aiopika_manager.py
2025-10-02 20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/async_aiopika_manager.py
2025-10-29 10:42:20.000000000 +0100
@@ -82,7 +82,7 @@
try:
await self.publisher_exchange.publish(
aio_pika.Message(
- body=json.dumps(data),
+ body=json.dumps(data).encode(),
delivery_mode=aio_pika.DeliveryMode.PERSISTENT
), routing_key='*',
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/async_client.py
new/python-socketio-5.14.3/src/socketio/async_client.py
--- old/python-socketio-5.14.1/src/socketio/async_client.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/async_client.py 2025-10-29
10:42:20.000000000 +0100
@@ -175,8 +175,8 @@
if set(self.namespaces) != set(self.connection_namespaces):
await self.disconnect()
raise exceptions.ConnectionError(
- 'One or more namespaces failed to connect'
- ', '.join(self.failed_namespaces))
+ 'One or more namespaces failed to connect: '
+ + ', '.join(self.failed_namespaces))
self.connected = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.1/src/socketio/async_pubsub_manager.py
new/python-socketio-5.14.3/src/socketio/async_pubsub_manager.py
--- old/python-socketio-5.14.1/src/socketio/async_pubsub_manager.py
2025-10-02 20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/async_pubsub_manager.py
2025-10-29 10:42:20.000000000 +0100
@@ -1,10 +1,12 @@
import asyncio
+import base64
from functools import partial
import uuid
from engineio import json
from .async_manager import AsyncManager
+from .packet import Packet
class AsyncPubSubManager(AsyncManager):
@@ -64,8 +66,12 @@
callback = (room, namespace, id)
else:
callback = None
+ binary = Packet.data_is_binary(data)
+ if binary:
+ data, attachments = Packet.deconstruct_binary(data)
+ data = [data, *[base64.b64encode(a).decode() for a in attachments]]
message = {'method': 'emit', 'event': event, 'data': data,
- 'namespace': namespace, 'room': room,
+ 'binary': binary, 'namespace': namespace, 'room': room,
'skip_sid': skip_sid, 'callback': callback,
'host_id': self.host_id}
await self._handle_emit(message) # handle in this host
@@ -145,7 +151,11 @@
*remote_callback)
else:
callback = None
- await super().emit(message['event'], message['data'],
+ data = message['data']
+ if message.get('binary'):
+ attachments = [base64.b64decode(a) for a in data[1:]]
+ data = Packet.reconstruct_binary(data[0], attachments)
+ await super().emit(message['event'], data,
namespace=message.get('namespace'),
room=message.get('room'),
skip_sid=message.get('skip_sid'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/async_server.py
new/python-socketio-5.14.3/src/socketio/async_server.py
--- old/python-socketio-5.14.1/src/socketio/async_server.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/async_server.py 2025-10-29
10:42:20.000000000 +0100
@@ -561,6 +561,9 @@
except exceptions.ConnectionRefusedError as exc:
fail_reason = exc.error_args
success = False
+ except ConnectionRefusedError:
+ fail_reason = {"message": "Connection refused by server"}
+ success = False
if success is False:
if self.always_connect:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/base_manager.py
new/python-socketio-5.14.3/src/socketio/base_manager.py
--- old/python-socketio-5.14.1/src/socketio/base_manager.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/base_manager.py 2025-10-29
10:42:20.000000000 +0100
@@ -29,7 +29,13 @@
return self.rooms.keys()
def get_participants(self, namespace, room):
- """Return an iterable with the active participants in a room."""
+ """Return an iterable with the active participants in a room.
+
+ Note that in a multi-server scenario this method only returns the
+ participants connect to the server in which the method is called. There
+ is currently no functionality to assemble a complete list of users
+ across multiple servers.
+ """
ns = self.rooms.get(namespace, {})
if hasattr(room, '__len__') and not isinstance(room, str):
participants = ns[room[0]]._fwdm.copy() if room[0] in ns else {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/client.py
new/python-socketio-5.14.3/src/socketio/client.py
--- old/python-socketio-5.14.1/src/socketio/client.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/client.py 2025-10-29
10:42:20.000000000 +0100
@@ -169,7 +169,7 @@
self.disconnect()
raise exceptions.ConnectionError(
'One or more namespaces failed to connect: '
- ', '.join(self.failed_namespaces))
+ + ', '.join(self.failed_namespaces))
self.connected = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/packet.py
new/python-socketio-5.14.3/src/socketio/packet.py
--- old/python-socketio-5.14.1/src/socketio/packet.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/packet.py 2025-10-29
10:42:20.000000000 +0100
@@ -29,7 +29,7 @@
self.namespace = namespace
self.id = id
if self.uses_binary_events and \
- (binary or (binary is None and self._data_is_binary(
+ (binary or (binary is None and self.data_is_binary(
self.data))):
if self.packet_type == EVENT:
self.packet_type = BINARY_EVENT
@@ -51,7 +51,7 @@
"""
encoded_packet = str(self.packet_type)
if self.packet_type == BINARY_EVENT or self.packet_type == BINARY_ACK:
- data, attachments = self._deconstruct_binary(self.data)
+ data, attachments = self.deconstruct_binary(self.data)
encoded_packet += str(len(attachments)) + '-'
else:
data = self.data
@@ -119,61 +119,65 @@
raise ValueError('Unexpected binary attachment')
self.attachments.append(attachment)
if self.attachment_count == len(self.attachments):
- self.reconstruct_binary(self.attachments)
+ self.data = self.reconstruct_binary(self.data, self.attachments)
return True
return False
- def reconstruct_binary(self, attachments):
+ @classmethod
+ def reconstruct_binary(cls, data, attachments):
"""Reconstruct a decoded packet using the given list of binary
attachments.
"""
- self.data = self._reconstruct_binary_internal(self.data,
- self.attachments)
+ return cls._reconstruct_binary_internal(data, attachments)
- def _reconstruct_binary_internal(self, data, attachments):
+ @classmethod
+ def _reconstruct_binary_internal(cls, data, attachments):
if isinstance(data, list):
- return [self._reconstruct_binary_internal(item, attachments)
+ return [cls._reconstruct_binary_internal(item, attachments)
for item in data]
elif isinstance(data, dict):
if data.get('_placeholder') and 'num' in data:
return attachments[data['num']]
else:
- return {key: self._reconstruct_binary_internal(value,
- attachments)
+ return {key: cls._reconstruct_binary_internal(value,
+ attachments)
for key, value in data.items()}
else:
return data
- def _deconstruct_binary(self, data):
+ @classmethod
+ def deconstruct_binary(cls, data):
"""Extract binary components in the packet."""
attachments = []
- data = self._deconstruct_binary_internal(data, attachments)
+ data = cls._deconstruct_binary_internal(data, attachments)
return data, attachments
- def _deconstruct_binary_internal(self, data, attachments):
+ @classmethod
+ def _deconstruct_binary_internal(cls, data, attachments):
if isinstance(data, bytes):
attachments.append(data)
return {'_placeholder': True, 'num': len(attachments) - 1}
elif isinstance(data, list):
- return [self._deconstruct_binary_internal(item, attachments)
+ return [cls._deconstruct_binary_internal(item, attachments)
for item in data]
elif isinstance(data, dict):
- return {key: self._deconstruct_binary_internal(value, attachments)
+ return {key: cls._deconstruct_binary_internal(value, attachments)
for key, value in data.items()}
else:
return data
- def _data_is_binary(self, data):
+ @classmethod
+ def data_is_binary(cls, data):
"""Check if the data contains binary components."""
if isinstance(data, bytes):
return True
elif isinstance(data, list):
return functools.reduce(
- lambda a, b: a or b, [self._data_is_binary(item)
+ lambda a, b: a or b, [cls.data_is_binary(item)
for item in data], False)
elif isinstance(data, dict):
return functools.reduce(
- lambda a, b: a or b, [self._data_is_binary(item)
+ lambda a, b: a or b, [cls.data_is_binary(item)
for item in data.values()],
False)
else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.1/src/socketio/pubsub_manager.py
new/python-socketio-5.14.3/src/socketio/pubsub_manager.py
--- old/python-socketio-5.14.1/src/socketio/pubsub_manager.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/pubsub_manager.py 2025-10-29
10:42:20.000000000 +0100
@@ -1,9 +1,11 @@
+import base64
from functools import partial
import uuid
from engineio import json
from .manager import Manager
+from .packet import Packet
class PubSubManager(Manager):
@@ -61,8 +63,12 @@
callback = (room, namespace, id)
else:
callback = None
+ binary = Packet.data_is_binary(data)
+ if binary:
+ data, attachments = Packet.deconstruct_binary(data)
+ data = [data, *[base64.b64encode(a).decode() for a in attachments]]
message = {'method': 'emit', 'event': event, 'data': data,
- 'namespace': namespace, 'room': room,
+ 'binary': binary, 'namespace': namespace, 'room': room,
'skip_sid': skip_sid, 'callback': callback,
'host_id': self.host_id}
self._handle_emit(message) # handle in this host
@@ -141,7 +147,11 @@
*remote_callback)
else:
callback = None
- super().emit(message['event'], message['data'],
+ data = message['data']
+ if message.get('binary'):
+ attachments = [base64.b64decode(a) for a in data[1:]]
+ data = Packet.reconstruct_binary(data[0], attachments)
+ super().emit(message['event'], data,
namespace=message.get('namespace'),
room=message.get('room'),
skip_sid=message.get('skip_sid'), callback=callback)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/src/socketio/server.py
new/python-socketio-5.14.3/src/socketio/server.py
--- old/python-socketio-5.14.1/src/socketio/server.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/src/socketio/server.py 2025-10-29
10:42:20.000000000 +0100
@@ -543,6 +543,9 @@
except exceptions.ConnectionRefusedError as exc:
fail_reason = exc.error_args
success = False
+ except ConnectionRefusedError:
+ fail_reason = {"message": "Connection refused by server"}
+ success = False
if success is False:
if self.always_connect:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tests/async/test_client.py
new/python-socketio-5.14.3/tests/async/test_client.py
--- old/python-socketio-5.14.1/tests/async/test_client.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/async/test_client.py 2025-10-29
10:42:20.000000000 +0100
@@ -203,6 +203,60 @@
assert c.connected is True
assert c.namespaces == {'/bar': '123', '/foo': '456'}
+ async def test_connect_wait_one_namespaces_error(self):
+ c = async_client.AsyncClient()
+ c.eio.connect = mock.AsyncMock()
+ c._connect_event = mock.MagicMock()
+
+ async def mock_connect():
+ if c.failed_namespaces == []:
+ c.failed_namespaces = ['/foo']
+ return True
+ return False
+
+ c._connect_event.wait = mock_connect
+ with pytest.raises(exceptions.ConnectionError,
+ match='failed to connect: /foo'):
+ await c.connect(
+ 'url',
+ namespaces=['/foo'],
+ wait=True,
+ wait_timeout=0.01,
+ )
+ assert c.connected is False
+ assert c.namespaces == {}
+ assert c.failed_namespaces == ['/foo']
+
+ async def test_connect_wait_three_namespaces_error(self):
+ c = async_client.AsyncClient()
+ c.eio.connect = mock.AsyncMock()
+ c._connect_event = mock.MagicMock()
+
+ async def mock_connect():
+ if c.namespaces == {}:
+ c.namespaces = {'/bar': '123'}
+ return True
+ elif c.namespaces == {'/bar': '123'} and c.failed_namespaces == []:
+ c.failed_namespaces = ['/baz']
+ return True
+ elif c.failed_namespaces == ['/baz']:
+ c.failed_namespaces = ['/baz', '/foo']
+ return True
+ return False
+
+ c._connect_event.wait = mock_connect
+ with pytest.raises(exceptions.ConnectionError,
+ match='failed to connect: /baz, /foo'):
+ await c.connect(
+ 'url',
+ namespaces=['/foo', '/bar', '/baz'],
+ wait=True,
+ wait_timeout=0.01,
+ )
+ assert c.connected is False
+ assert c.namespaces == {'/bar': '123'}
+ assert c.failed_namespaces == ['/baz', '/foo']
+
async def test_connect_timeout(self):
c = async_client.AsyncClient()
c.eio.connect = mock.AsyncMock()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.1/tests/async/test_pubsub_manager.py
new/python-socketio-5.14.3/tests/async/test_pubsub_manager.py
--- old/python-socketio-5.14.1/tests/async/test_pubsub_manager.py
2025-10-02 20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/async/test_pubsub_manager.py
2025-10-29 10:42:20.000000000 +0100
@@ -57,6 +57,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': None,
@@ -66,6 +67,36 @@
}
)
+ async def test_emit_binary(self):
+ await self.pm.emit('foo', b'bar')
+ self.pm._publish.assert_awaited_once_with(
+ {
+ 'method': 'emit',
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'_placeholder': True, 'num': 0}, 'YmFy'],
+ 'namespace': '/',
+ 'room': None,
+ 'skip_sid': None,
+ 'callback': None,
+ 'host_id': '123456',
+ }
+ )
+ await self.pm.emit('foo', {'foo': b'bar'})
+ self.pm._publish.assert_awaited_with(
+ {
+ 'method': 'emit',
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'foo': {'_placeholder': True, 'num': 0}}, 'YmFy'],
+ 'namespace': '/',
+ 'room': None,
+ 'skip_sid': None,
+ 'callback': None,
+ 'host_id': '123456',
+ }
+ )
+
async def test_emit_with_to(self):
sid = 'room-mate'
await self.pm.emit('foo', 'bar', to=sid)
@@ -73,6 +104,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': sid,
@@ -88,6 +120,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/baz',
'room': None,
@@ -103,6 +136,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': 'baz',
@@ -118,6 +152,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': None,
@@ -136,6 +171,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': 'baz',
@@ -238,6 +274,37 @@
namespace=None,
room=None,
skip_sid=None,
+ callback=None,
+ )
+
+ async def test_handle_emit_binary(self):
+ with mock.patch.object(
+ async_manager.AsyncManager, 'emit'
+ ) as super_emit:
+ await self.pm._handle_emit({
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'_placeholder': True, 'num': 0}, 'YmFy'],
+ })
+ super_emit.assert_awaited_once_with(
+ 'foo',
+ b'bar',
+ namespace=None,
+ room=None,
+ skip_sid=None,
+ callback=None,
+ )
+ await self.pm._handle_emit({
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'foo': {'_placeholder': True, 'num': 0}}, 'YmFy'],
+ })
+ super_emit.assert_awaited_with(
+ 'foo',
+ {'foo': b'bar'},
+ namespace=None,
+ room=None,
+ skip_sid=None,
callback=None,
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tests/async/test_server.py
new/python-socketio-5.14.3/tests/async/test_server.py
--- old/python-socketio-5.14.1/tests/async/test_server.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/async/test_server.py 2025-10-29
10:42:20.000000000 +0100
@@ -482,6 +482,21 @@
'123', '4{"message":"fail_reason"}')
assert s.environ == {'123': 'environ'}
+ async def test_handle_connect_rejected_with_python_exception(self, eio):
+ eio.return_value.send = mock.AsyncMock()
+ s = async_server.AsyncServer()
+ handler = mock.MagicMock(
+ side_effect=ConnectionRefusedError()
+ )
+ s.on('connect', handler)
+ await s._handle_eio_connect('123', 'environ')
+ await s._handle_eio_message('123', '0')
+ assert not s.manager.is_connected('1', '/')
+ handler.assert_called_once_with('1', 'environ')
+ s.eio.send.assert_awaited_once_with(
+ '123', '4{"message":"Connection refused by server"}')
+ assert s.environ == {'123': 'environ'}
+
async def test_handle_connect_rejected_with_empty_exception(self, eio):
eio.return_value.send = mock.AsyncMock()
s = async_server.AsyncServer()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tests/common/test_client.py
new/python-socketio-5.14.3/tests/common/test_client.py
--- old/python-socketio-5.14.1/tests/common/test_client.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/common/test_client.py 2025-10-29
10:42:20.000000000 +0100
@@ -350,6 +350,62 @@
assert c.connected is True
assert c.namespaces == {'/bar': '123', '/foo': '456'}
+ def test_connect_wait_one_namespaces_error(self):
+ c = client.Client()
+ c.eio.connect = mock.MagicMock()
+ c._connect_event = mock.MagicMock()
+
+ def mock_connect(timeout):
+ assert timeout == 0.01
+ if c.failed_namespaces == []:
+ c.failed_namespaces = ['/foo']
+ return True
+ return False
+
+ c._connect_event.wait = mock_connect
+ with pytest.raises(exceptions.ConnectionError,
+ match='failed to connect: /foo'):
+ c.connect(
+ 'url',
+ namespaces=['/foo'],
+ wait=True,
+ wait_timeout=0.01,
+ )
+ assert c.connected is False
+ assert c.namespaces == {}
+ assert c.failed_namespaces == ['/foo']
+
+ def test_connect_wait_three_namespaces_error(self):
+ c = client.Client()
+ c.eio.connect = mock.MagicMock()
+ c._connect_event = mock.MagicMock()
+
+ def mock_connect(timeout):
+ assert timeout == 0.01
+ if c.namespaces == {}:
+ c.namespaces = {'/bar': '123'}
+ return True
+ elif c.namespaces == {'/bar': '123'} and c.failed_namespaces == []:
+ c.failed_namespaces = ['/baz']
+ return True
+ elif c.failed_namespaces == ['/baz']:
+ c.failed_namespaces = ['/baz', '/foo']
+ return True
+ return False
+
+ c._connect_event.wait = mock_connect
+ with pytest.raises(exceptions.ConnectionError,
+ match='failed to connect: /baz, /foo'):
+ c.connect(
+ 'url',
+ namespaces=['/foo', '/bar', '/baz'],
+ wait=True,
+ wait_timeout=0.01,
+ )
+ assert c.connected is False
+ assert c.namespaces == {'/bar': '123'}
+ assert c.failed_namespaces == ['/baz', '/foo']
+
def test_connect_timeout(self):
c = client.Client()
c.eio.connect = mock.MagicMock()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tests/common/test_packet.py
new/python-socketio-5.14.3/tests/common/test_packet.py
--- old/python-socketio-5.14.1/tests/common/test_packet.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/common/test_packet.py 2025-10-29
10:42:20.000000000 +0100
@@ -266,16 +266,24 @@
assert pkt.data["a"] == "0123456789-"
assert pkt.attachment_count == 0
+ def test_deconstruct_binary(self):
+ datas = [b'foo', [b'foo', b'bar'], ['foo', b'bar'], {'foo': b'bar'},
+ {'foo': 'bar', 'baz': b'qux'}, {'foo': [b'bar']}]
+ for data in datas:
+ bdata, attachments = packet.Packet.deconstruct_binary(data)
+ rdata = packet.Packet.reconstruct_binary(bdata, attachments)
+ assert data == rdata
+
def test_data_is_binary_list(self):
pkt = packet.Packet()
- assert not pkt._data_is_binary(['foo'])
- assert not pkt._data_is_binary([])
- assert pkt._data_is_binary([b'foo'])
- assert pkt._data_is_binary(['foo', b'bar'])
+ assert not pkt.data_is_binary(['foo'])
+ assert not pkt.data_is_binary([])
+ assert pkt.data_is_binary([b'foo'])
+ assert pkt.data_is_binary(['foo', b'bar'])
def test_data_is_binary_dict(self):
pkt = packet.Packet()
- assert not pkt._data_is_binary({'a': 'foo'})
- assert not pkt._data_is_binary({})
- assert pkt._data_is_binary({'a': b'foo'})
- assert pkt._data_is_binary({'a': 'foo', 'b': b'bar'})
+ assert not pkt.data_is_binary({'a': 'foo'})
+ assert not pkt.data_is_binary({})
+ assert pkt.data_is_binary({'a': b'foo'})
+ assert pkt.data_is_binary({'a': 'foo', 'b': b'bar'})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.1/tests/common/test_pubsub_manager.py
new/python-socketio-5.14.3/tests/common/test_pubsub_manager.py
--- old/python-socketio-5.14.1/tests/common/test_pubsub_manager.py
2025-10-02 20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/common/test_pubsub_manager.py
2025-10-29 10:42:20.000000000 +0100
@@ -69,6 +69,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': None,
@@ -78,6 +79,36 @@
}
)
+ def test_emit_binary(self):
+ self.pm.emit('foo', b'bar')
+ self.pm._publish.assert_called_once_with(
+ {
+ 'method': 'emit',
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'_placeholder': True, 'num': 0}, 'YmFy'],
+ 'namespace': '/',
+ 'room': None,
+ 'skip_sid': None,
+ 'callback': None,
+ 'host_id': '123456',
+ }
+ )
+ self.pm.emit('foo', {'foo': b'bar'})
+ self.pm._publish.assert_called_with(
+ {
+ 'method': 'emit',
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'foo': {'_placeholder': True, 'num': 0}}, 'YmFy'],
+ 'namespace': '/',
+ 'room': None,
+ 'skip_sid': None,
+ 'callback': None,
+ 'host_id': '123456',
+ }
+ )
+
def test_emit_with_to(self):
sid = "ferris"
self.pm.emit('foo', 'bar', to=sid)
@@ -85,6 +116,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': sid,
@@ -100,6 +132,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/baz',
'room': None,
@@ -115,6 +148,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': 'baz',
@@ -130,6 +164,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': None,
@@ -148,6 +183,7 @@
{
'method': 'emit',
'event': 'foo',
+ 'binary': False,
'data': 'bar',
'namespace': '/',
'room': 'baz',
@@ -247,6 +283,35 @@
namespace=None,
room=None,
skip_sid=None,
+ callback=None,
+ )
+
+ def test_handle_emit_binary(self):
+ with mock.patch.object(manager.Manager, 'emit') as super_emit:
+ self.pm._handle_emit({
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'_placeholder': True, 'num': 0}, 'YmFy'],
+ })
+ super_emit.assert_called_once_with(
+ 'foo',
+ b'bar',
+ namespace=None,
+ room=None,
+ skip_sid=None,
+ callback=None,
+ )
+ self.pm._handle_emit({
+ 'event': 'foo',
+ 'binary': True,
+ 'data': [{'foo': {'_placeholder': True, 'num': 0}}, 'YmFy'],
+ })
+ super_emit.assert_called_with(
+ 'foo',
+ {'foo': b'bar'},
+ namespace=None,
+ room=None,
+ skip_sid=None,
callback=None,
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tests/common/test_server.py
new/python-socketio-5.14.3/tests/common/test_server.py
--- old/python-socketio-5.14.1/tests/common/test_server.py 2025-10-02
20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tests/common/test_server.py 2025-10-29
10:42:20.000000000 +0100
@@ -462,6 +462,20 @@
s.eio.send.assert_called_once_with('123', '4{"message":"fail_reason"}')
assert s.environ == {'123': 'environ'}
+ def test_handle_connect_rejected_with_python_exception(self, eio):
+ s = server.Server()
+ handler = mock.MagicMock(
+ side_effect=ConnectionRefusedError()
+ )
+ s.on('connect', handler)
+ s._handle_eio_connect('123', 'environ')
+ s._handle_eio_message('123', '0')
+ assert not s.manager.is_connected('1', '/')
+ handler.assert_called_once_with('1', 'environ')
+ s.eio.send.assert_called_once_with(
+ '123', '4{"message":"Connection refused by server"}')
+ assert s.environ == {'123': 'environ'}
+
def test_handle_connect_rejected_with_empty_exception(self, eio):
s = server.Server()
handler = mock.MagicMock(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.1/tox.ini
new/python-socketio-5.14.3/tox.ini
--- old/python-socketio-5.14.1/tox.ini 2025-10-02 20:41:26.000000000 +0200
+++ new/python-socketio-5.14.3/tox.ini 2025-10-29 10:42:20.000000000 +0100
@@ -1,5 +1,5 @@
[tox]
-envlist=flake8,py{38,39,310,311,312,313},docs
+envlist=flake8,py{38,39,310,311,312,313,314},docs
skip_missing_interpreters=True
[gh-actions]
@@ -10,6 +10,7 @@
3.11: py311
3.12: py312
3.13: py313
+ 3.14: py314
pypy-3: pypy3
[testenv]