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-12-10 15:33:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-socketio (Old)
and /work/SRC/openSUSE:Factory/.python-python-socketio.new.1939 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-socketio"
Wed Dec 10 15:33:47 2025 rev:15 rq:1321922 version:5.15.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-python-socketio/python-python-socketio.changes
2025-10-31 16:29:46.022241642 +0100
+++
/work/SRC/openSUSE:Factory/.python-python-socketio.new.1939/python-python-socketio.changes
2025-12-10 15:34:30.768336427 +0100
@@ -1,0 +2,11 @@
+Wed Dec 3 21:26:55 UTC 2025 - Guang Yee <[email protected]>
+
+- Update 5.15.0
+ * Retry initial Redis connection #1534
+ * Correctly regenerate RabbitMQ binding after a connection failure #1516
+ * Support ext_type in the MsgPackPacket class #1521
+ * Support sending bytesarrays when using pub/sub managers
+ * Fix typos in documentation #1520
+ * Improvements to the logging documentation
+
+-------------------------------------------------------------------
Old:
----
python_socketio-5.14.3.tar.gz
New:
----
python_socketio-5.15.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-socketio.spec ++++++
--- /var/tmp/diff_new_pack.1jWF79/_old 2025-12-10 15:34:31.464365873 +0100
+++ /var/tmp/diff_new_pack.1jWF79/_new 2025-12-10 15:34:31.468366043 +0100
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-python-socketio
-Version: 5.14.3
+Version: 5.15.0
Release: 0
Summary: SocketIO server
License: MIT
@@ -26,13 +26,13 @@
Source:
https://github.com/miguelgrinberg/python-socketio/archive/v%{version}.tar.gz#/python_socketio-%{version}.tar.gz
BuildRequires: %{python_module bidict >= 0.21.0}
BuildRequires: %{python_module pip}
-BuildRequires: %{python_module python-engineio >= 4.8.0}
+BuildRequires: %{python_module python-engineio >= 4.11.0}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module wheel}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-bidict >= 0.21.0
-Requires: python-python-engineio >= 4.8.0
+Requires: python-python-engineio >= 4.11.0
Suggests: python-aiohttp >= 3.4
Suggests: python-requests >= 2.21.0
Suggests: python-websocket-client >= 0.54.0
++++++ python_socketio-5.14.3.tar.gz -> python_socketio-5.15.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/CHANGES.md
new/python-socketio-5.15.0/CHANGES.md
--- old/python-socketio-5.14.3/CHANGES.md 2025-10-29 10:42:20.000000000
+0100
+++ new/python-socketio-5.15.0/CHANGES.md 2025-11-22 19:38:45.000000000
+0100
@@ -1,5 +1,14 @@
# python-socketio change log
+**Release 5.15.0** - 2025-11-22
+
+- Retry initial Redis connection
[#1534](https://github.com/miguelgrinberg/python-socketio/issues/1536) ([commit
#1](https://github.com/miguelgrinberg/python-socketio/commit/1e903e173a2d7b04599c4f7f9630c1abbb531fad)
[commit
#2](https://github.com/miguelgrinberg/python-socketio/commit/5e898a9b93526e6e667767e54c60f4c84589989d))
+- Correctly regenerate RabbitMQ binding after a connection failure
[#1516](https://github.com/miguelgrinberg/python-socketio/issues/1516)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/c52e93b4a328d98a968bfbdec0cfd598b73ee913))
(thanks **Gritty_dev**!)
+- Support `ext_type` in the `MsgPackPacket` class
[#1521](https://github.com/miguelgrinberg/python-socketio/issues/1521)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/208925344a48485d2cd56e40eb74266c3bcb5311))
+- Support sending `bytesarray`s when using pub/sub managers
([commit](https://github.com/miguelgrinberg/python-socketio/commit/6c9b9974f72e2efdf62407ecab24ee6995448098))
+- Fix typos in documentation
[#1520](https://github.com/miguelgrinberg/python-socketio/issues/1520)
([commit](https://github.com/miguelgrinberg/python-socketio/commit/db3f1c2a0105c30cb833ddfca8f05fe4320468fd))
(thanks **Lê Nam Khánh**!)
+- Improvements to the logging documentation
([commit](https://github.com/miguelgrinberg/python-socketio/commit/b423d0e38eef559b7e81acb7e32059de305f982c))
+
**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))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/docs/client.rst
new/python-socketio-5.15.0/docs/client.rst
--- old/python-socketio-5.14.3/docs/client.rst 2025-10-29 10:42:20.000000000
+0100
+++ new/python-socketio-5.15.0/docs/client.rst 2025-11-22 19:38:45.000000000
+0100
@@ -198,9 +198,14 @@
The ``logger`` argument controls logging related to the Socket.IO protocol,
while ``engineio_logger`` controls logs that originate in the low-level
-Engine.IO transport. These arguments can be set to ``True`` to output logs to
-``stderr``, or to an object compatible with Python's ``logging`` package
-where the logs should be emitted to. A value of ``False`` disables logging.
+Engine.IO transport. The value given to these arguments controls logging
+behavior:
+
+* ``True``: Enables log output to ``stderr`` at the ``INFO`` level.
+* ``False``: Enables log output to ``stderr`` at the ``ERROR`` level. This is
+ the default.
+* A ``logging.Logger`` instance: Uses the provided logger without additional
+ configuration.
Logging can help identify the cause of connection problems, unexpected
disconnections and other issues.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/docs/server.rst
new/python-socketio-5.15.0/docs/server.rst
--- old/python-socketio-5.14.3/docs/server.rst 2025-10-29 10:42:20.000000000
+0100
+++ new/python-socketio-5.15.0/docs/server.rst 2025-11-22 19:38:45.000000000
+0100
@@ -657,9 +657,14 @@
The ``logger`` argument controls logging related to the Socket.IO protocol,
while ``engineio_logger`` controls logs that originate in the low-level
-Engine.IO transport. These arguments can be set to ``True`` to output logs to
-``stderr``, or to an object compatible with Python's ``logging`` package
-where the logs should be emitted to. A value of ``False`` disables logging.
+Engine.IO transport. The value given to these arguments controls logging
+behavior:
+
+* ``True``: Enables log output to ``stderr`` at the ``INFO`` level.
+* ``False``: Enables log output to ``stderr`` at the ``ERROR`` level. This is
+ the default.
+* A ``logging.Logger`` instance: Uses the provided logger without additional
+ configuration.
Logging can help identify the cause of connection problems, 400 responses,
bad performance and other issues.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/examples/server/wsgi/django_socketio/requirements.txt
new/python-socketio-5.15.0/examples/server/wsgi/django_socketio/requirements.txt
---
old/python-socketio-5.14.3/examples/server/wsgi/django_socketio/requirements.txt
2025-10-29 10:42:20.000000000 +0100
+++
new/python-socketio-5.15.0/examples/server/wsgi/django_socketio/requirements.txt
2025-11-22 19:38:45.000000000 +0100
@@ -1,6 +1,6 @@
asgiref==3.6.0
bidict==0.22.1
-Django==4.2.22
+Django==4.2.26
gunicorn==23.0.0
h11==0.16.0
python-engineio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/pyproject.toml
new/python-socketio-5.15.0/pyproject.toml
--- old/python-socketio-5.14.3/pyproject.toml 2025-10-29 10:42:20.000000000
+0100
+++ new/python-socketio-5.15.0/pyproject.toml 2025-11-22 19:38:45.000000000
+0100
@@ -1,6 +1,6 @@
[project]
name = "python-socketio"
-version = "5.14.3"
+version = "5.15.0"
license = {text = "MIT"}
authors = [
{ name = "Miguel Grinberg", email = "[email protected]" },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/src/socketio/async_aiopika_manager.py
new/python-socketio-5.15.0/src/socketio/async_aiopika_manager.py
--- old/python-socketio-5.14.3/src/socketio/async_aiopika_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/async_aiopika_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -101,26 +101,26 @@
raise asyncio.CancelledError()
async def _listen(self):
- async with (await self._connection()) as connection:
- channel = await self._channel(connection)
- await channel.set_qos(prefetch_count=1)
- exchange = await self._exchange(channel)
- queue = await self._queue(channel, exchange)
+ retry_sleep = 1
+ while True:
+ try:
+ async with (await self._connection()) as connection:
+ channel = await self._channel(connection)
+ await channel.set_qos(prefetch_count=1)
+ exchange = await self._exchange(channel)
+ queue = await self._queue(channel, exchange)
- retry_sleep = 1
- while True:
- try:
async with queue.iterator() as queue_iter:
async for message in queue_iter:
async with message.process():
yield message.body
retry_sleep = 1
- except aio_pika.AMQPException:
- self._get_logger().error(
- 'Cannot receive from rabbitmq... '
- 'retrying in {} secs'.format(retry_sleep))
- await asyncio.sleep(retry_sleep)
- retry_sleep = min(retry_sleep * 2, 60)
- except aio_pika.exceptions.ChannelInvalidStateError:
- # aio_pika raises this exception when the task is cancelled
- raise asyncio.CancelledError()
+ except aio_pika.AMQPException:
+ self._get_logger().error(
+ 'Cannot receive from rabbitmq... '
+ 'retrying in {} secs'.format(retry_sleep))
+ await asyncio.sleep(retry_sleep)
+ retry_sleep = min(retry_sleep * 2, 60)
+ except aio_pika.exceptions.ChannelInvalidStateError:
+ # aio_pika raises this exception when the task is cancelled
+ raise asyncio.CancelledError()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/src/socketio/async_client.py
new/python-socketio-5.15.0/src/socketio/async_client.py
--- old/python-socketio-5.14.3/src/socketio/async_client.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/async_client.py 2025-11-22
19:38:45.000000000 +0100
@@ -489,7 +489,7 @@
raise
return ret
- # or else, forward the event to a namepsace handler if one exists
+ # or else, forward the event to a namespace handler if one exists
handler, args = self._get_namespace_handler(namespace, args)
if handler:
return await handler.trigger_event(event, *args)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/src/socketio/async_redis_manager.py
new/python-socketio-5.15.0/src/socketio/async_redis_manager.py
--- old/python-socketio-5.14.3/src/socketio/async_redis_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/async_redis_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -61,7 +61,9 @@
super().__init__(channel=channel, write_only=write_only, logger=logger)
self.redis_url = url
self.redis_options = redis_options or {}
- self._redis_connect()
+ self.connected = False
+ self.redis = None
+ self.pubsub = None
def _get_redis_module_and_error(self):
parsed_url = urlparse(self.redis_url)
@@ -106,23 +108,23 @@
self.redis = module.Redis.from_url(self.redis_url,
**self.redis_options)
self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
+ self.connected = True
async def _publish(self, data): # pragma: no cover
- retry = True
_, error = self._get_redis_module_and_error()
- while True:
+ for retries_left in range(1, -1, -1): # 2 attempts
try:
- if not retry:
+ if not self.connected:
self._redis_connect()
return await self.redis.publish(
self.channel, json.dumps(data))
except error as exc:
- if retry:
+ if retries_left > 0:
self._get_logger().error(
'Cannot publish to redis... '
'retrying',
extra={"redis_exception": str(exc)})
- retry = False
+ self.connected = False
else:
self._get_logger().error(
'Cannot publish to redis... '
@@ -132,12 +134,12 @@
break
async def _redis_listen_with_retries(self): # pragma: no cover
- retry_sleep = 1
- connect = False
_, error = self._get_redis_module_and_error()
+ retry_sleep = 1
+ subscribed = False
while True:
try:
- if connect:
+ if not subscribed:
self._redis_connect()
await self.pubsub.subscribe(self.channel)
retry_sleep = 1
@@ -148,7 +150,7 @@
'retrying in '
f'{retry_sleep} secs',
extra={"redis_exception": str(exc)})
- connect = True
+ subscribed = False
await asyncio.sleep(retry_sleep)
retry_sleep *= 2
if retry_sleep > 60:
@@ -156,7 +158,6 @@
async def _listen(self): # pragma: no cover
channel = self.channel.encode('utf-8')
- await self.pubsub.subscribe(self.channel)
async for message in self._redis_listen_with_retries():
if message['channel'] == channel and \
message['type'] == 'message' and 'data' in message:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/src/socketio/kombu_manager.py
new/python-socketio-5.15.0/src/socketio/kombu_manager.py
--- old/python-socketio-5.14.3/src/socketio/kombu_manager.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/kombu_manager.py 2025-11-22
19:38:45.000000000 +0100
@@ -115,10 +115,10 @@
break
def _listen(self):
- reader_queue = self._queue()
retry_sleep = 1
while True:
try:
+ reader_queue = self._queue()
with self._connection() as connection:
with connection.SimpleQueue(reader_queue) as queue:
while True:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/src/socketio/msgpack_packet.py
new/python-socketio-5.15.0/src/socketio/msgpack_packet.py
--- old/python-socketio-5.14.3/src/socketio/msgpack_packet.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/msgpack_packet.py 2025-11-22
19:38:45.000000000 +0100
@@ -4,14 +4,40 @@
class MsgPackPacket(packet.Packet):
uses_binary_events = False
+ dumps_default = None
+ ext_hook = msgpack.ExtType
+
+ @classmethod
+ def configure(cls, dumps_default=None, ext_hook=msgpack.ExtType):
+ """Change the default options for msgpack encoding and decoding.
+
+ :param dumps_default: a function called for objects that cannot be
+ serialized by default msgpack. The function
+ receives one argument, the object to serialize.
+ It should return a serializable object or a
+ ``msgpack.ExtType`` instance.
+ :param ext_hook: a function called when a ``msgpack.ExtType`` object is
+ seen during decoding. The function receives two
+ arguments, the code and the data. It should return the
+ decoded object.
+ """
+ class CustomMsgPackPacket(MsgPackPacket):
+ dumps_default = None
+ ext_hook = None
+
+ CustomMsgPackPacket.dumps_default = dumps_default
+ CustomMsgPackPacket.ext_hook = ext_hook
+ return CustomMsgPackPacket
def encode(self):
"""Encode the packet for transmission."""
- return msgpack.dumps(self._to_dict())
+ return msgpack.dumps(self._to_dict(),
+ default=self.__class__.dumps_default)
def decode(self, encoded_packet):
"""Decode a transmitted package."""
- decoded = msgpack.loads(encoded_packet)
+ decoded = msgpack.loads(encoded_packet,
+ ext_hook=self.__class__.ext_hook)
self.packet_type = decoded['type']
self.data = decoded.get('data')
self.id = decoded.get('id')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/src/socketio/packet.py
new/python-socketio-5.15.0/src/socketio/packet.py
--- old/python-socketio-5.14.3/src/socketio/packet.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/packet.py 2025-11-22
19:38:45.000000000 +0100
@@ -154,7 +154,7 @@
@classmethod
def _deconstruct_binary_internal(cls, data, attachments):
- if isinstance(data, bytes):
+ if isinstance(data, (bytes, bytearray)):
attachments.append(data)
return {'_placeholder': True, 'num': len(attachments) - 1}
elif isinstance(data, list):
@@ -169,7 +169,7 @@
@classmethod
def data_is_binary(cls, data):
"""Check if the data contains binary components."""
- if isinstance(data, bytes):
+ if isinstance(data, (bytes, bytearray)):
return True
elif isinstance(data, list):
return functools.reduce(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/src/socketio/redis_manager.py
new/python-socketio-5.15.0/src/socketio/redis_manager.py
--- old/python-socketio-5.14.3/src/socketio/redis_manager.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/src/socketio/redis_manager.py 2025-11-22
19:38:45.000000000 +0100
@@ -83,7 +83,9 @@
super().__init__(channel=channel, write_only=write_only, logger=logger)
self.redis_url = url
self.redis_options = redis_options or {}
- self._redis_connect()
+ self.connected = False
+ self.redis = None
+ self.pubsub = None
def initialize(self): # pragma: no cover
super().initialize()
@@ -143,22 +145,22 @@
self.redis = module.Redis.from_url(self.redis_url,
**self.redis_options)
self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
+ self.connected = True
def _publish(self, data): # pragma: no cover
- retry = True
_, error = self._get_redis_module_and_error()
- while True:
+ for retries_left in range(1, -1, -1): # 2 attempts
try:
- if not retry:
+ if not self.connected:
self._redis_connect()
return self.redis.publish(self.channel, json.dumps(data))
except error as exc:
- if retry:
+ if retries_left > 0:
logger.error(
'Cannot publish to redis... retrying',
extra={"redis_exception": str(exc)}
)
- retry = False
+ self.connected = False
else:
logger.error(
'Cannot publish to redis... giving up',
@@ -167,12 +169,12 @@
break
def _redis_listen_with_retries(self): # pragma: no cover
- retry_sleep = 1
- connect = False
_, error = self._get_redis_module_and_error()
+ retry_sleep = 1
+ subscribed = False
while True:
try:
- if connect:
+ if not subscribed:
self._redis_connect()
self.pubsub.subscribe(self.channel)
retry_sleep = 1
@@ -181,7 +183,7 @@
logger.error('Cannot receive from redis... '
f'retrying in {retry_sleep} secs',
extra={"redis_exception": str(exc)})
- connect = True
+ subscribed = False
time.sleep(retry_sleep)
retry_sleep *= 2
if retry_sleep > 60:
@@ -189,7 +191,6 @@
def _listen(self): # pragma: no cover
channel = self.channel.encode('utf-8')
- self.pubsub.subscribe(self.channel)
for message in self._redis_listen_with_retries():
if message['channel'] == channel and \
message['type'] == 'message' and 'data' in message:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/tests/async/test_client.py
new/python-socketio-5.15.0/tests/async/test_client.py
--- old/python-socketio-5.14.3/tests/async/test_client.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/async/test_client.py 2025-11-22
19:38:45.000000000 +0100
@@ -1,5 +1,6 @@
import asyncio
from unittest import mock
+from datetime import datetime, timezone, timedelta
import pytest
@@ -8,6 +9,7 @@
from engineio import exceptions as engineio_exceptions
from socketio import exceptions
from socketio import packet
+from socketio.msgpack_packet import MsgPackPacket
class TestAsyncClient:
@@ -1242,3 +1244,21 @@
assert c.sid is None
assert not c.connected
c.start_background_task.assert_not_called()
+
+ def test_serializer_args_with_msgpack(self):
+ def default(o):
+ if isinstance(o, datetime):
+ return o.isoformat()
+ raise TypeError("Unknown type")
+
+ data = {"current": datetime.now(timezone(timedelta(0)))}
+ c = async_client.AsyncClient(
+ serializer=MsgPackPacket.configure(dumps_default=default))
+ p = c.packet_class(data=data)
+ p2 = c.packet_class(encoded_packet=p.encode())
+
+ assert p.data != p2.data
+ assert isinstance(p2.data, dict)
+ assert "current" in p2.data
+ assert isinstance(p2.data["current"], str)
+ assert default(data["current"]) == p2.data["current"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/tests/async/test_pubsub_manager.py
new/python-socketio-5.15.0/tests/async/test_pubsub_manager.py
--- old/python-socketio-5.14.3/tests/async/test_pubsub_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/async/test_pubsub_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -97,6 +97,36 @@
}
)
+ async def test_emit_bytearray(self):
+ await self.pm.emit('foo', bytearray(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': bytearray(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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/tests/async/test_redis_manager.py
new/python-socketio-5.15.0/tests/async/test_redis_manager.py
--- old/python-socketio-5.14.3/tests/async/test_redis_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/async/test_redis_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -12,7 +12,7 @@
async_redis_manager.aioredis = None
with pytest.raises(RuntimeError):
- AsyncRedisManager('redis://')
+ AsyncRedisManager('redis://')._redis_connect()
assert AsyncRedisManager('unix:///var/sock/redis.sock') is not None
async_redis_manager.aioredis = saved_redis
@@ -22,7 +22,7 @@
async_redis_manager.aiovalkey = None
with pytest.raises(RuntimeError):
- AsyncRedisManager('valkey://')
+ AsyncRedisManager('valkey://')._redis_connect()
assert AsyncRedisManager('unix:///var/sock/redis.sock') is not None
async_redis_manager.aiovalkey = saved_valkey
@@ -34,18 +34,18 @@
async_redis_manager.aiovalkey = None
with pytest.raises(RuntimeError):
- AsyncRedisManager('redis://')
+ AsyncRedisManager('redis://')._redis_connect()
with pytest.raises(RuntimeError):
- AsyncRedisManager('valkey://')
+ AsyncRedisManager('valkey://')._redis_connect()
with pytest.raises(RuntimeError):
- AsyncRedisManager('unix:///var/sock/redis.sock')
+ AsyncRedisManager('unix:///var/sock/redis.sock')._redis_connect()
async_redis_manager.aioredis = saved_redis
async_redis_manager.aiovalkey = saved_valkey
def test_bad_url(self):
with pytest.raises(ValueError):
- AsyncRedisManager('http://localhost:6379')
+ AsyncRedisManager('http://localhost:6379')._redis_connect()
def test_redis_connect(self):
urls = [
@@ -72,6 +72,8 @@
]
for url in urls:
c = AsyncRedisManager(url)
+ assert c.redis is None
+ c._redis_connect()
assert isinstance(c.redis, redis.asyncio.Redis)
def test_valkey_connect(self):
@@ -102,6 +104,8 @@
]
for url in urls:
c = AsyncRedisManager(url)
+ assert c.redis is None
+ c._redis_connect()
assert isinstance(c.redis, valkey.asyncio.Valkey)
async_redis_manager.aioredis = saved_redis
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/tests/async/test_server.py
new/python-socketio-5.15.0/tests/async/test_server.py
--- old/python-socketio-5.14.3/tests/async/test_server.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/async/test_server.py 2025-11-22
19:38:45.000000000 +0100
@@ -1,6 +1,7 @@
import asyncio
import logging
from unittest import mock
+from datetime import datetime, timezone, timedelta
from engineio import json
from engineio import packet as eio_packet
@@ -11,6 +12,7 @@
from socketio import exceptions
from socketio import namespace
from socketio import packet
+from socketio.msgpack_packet import MsgPackPacket
@mock.patch('socketio.server.engineio.AsyncServer', **{
@@ -1089,3 +1091,21 @@
s = async_server.AsyncServer()
await s.sleep(1.23)
s.eio.sleep.assert_awaited_once_with(1.23)
+
+ def test_serializer_args_with_msgpack(self, eio):
+ def default(o):
+ if isinstance(o, datetime):
+ return o.isoformat()
+ raise TypeError("Unknown type")
+
+ data = {"current": datetime.now(timezone(timedelta(0)))}
+ s = async_server.AsyncServer(
+ serializer=MsgPackPacket.configure(dumps_default=default))
+ p = s.packet_class(data=data)
+ p2 = s.packet_class(encoded_packet=p.encode())
+
+ assert p.data != p2.data
+ assert isinstance(p2.data, dict)
+ assert "current" in p2.data
+ assert isinstance(p2.data["current"], str)
+ assert default(data["current"]) == p2.data["current"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/tests/common/test_client.py
new/python-socketio-5.15.0/tests/common/test_client.py
--- old/python-socketio-5.14.3/tests/common/test_client.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_client.py 2025-11-22
19:38:45.000000000 +0100
@@ -1,6 +1,7 @@
import logging
import time
from unittest import mock
+from datetime import datetime, timezone, timedelta
from engineio import exceptions as engineio_exceptions
from engineio import json
@@ -13,6 +14,7 @@
from socketio import msgpack_packet
from socketio import namespace
from socketio import packet
+from socketio.msgpack_packet import MsgPackPacket
class TestClient:
@@ -1386,3 +1388,21 @@
assert c.sid is None
assert not c.connected
c.start_background_task.assert_not_called()
+
+ def test_serializer_args_with_msgpack(self):
+ def default(o):
+ if isinstance(o, datetime):
+ return o.isoformat()
+ raise TypeError("Unknown type")
+
+ data = {"current": datetime.now(timezone(timedelta(0)))}
+ c = client.Client(
+ serializer=MsgPackPacket.configure(dumps_default=default))
+ p = c.packet_class(data=data)
+ p2 = c.packet_class(encoded_packet=p.encode())
+
+ assert p.data != p2.data
+ assert isinstance(p2.data, dict)
+ assert "current" in p2.data
+ assert isinstance(p2.data["current"], str)
+ assert default(data["current"]) == p2.data["current"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/tests/common/test_msgpack_packet.py
new/python-socketio-5.15.0/tests/common/test_msgpack_packet.py
--- old/python-socketio-5.14.3/tests/common/test_msgpack_packet.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_msgpack_packet.py
2025-11-22 19:38:45.000000000 +0100
@@ -1,3 +1,8 @@
+from datetime import datetime, timedelta, timezone
+
+import pytest
+import msgpack
+
from socketio import msgpack_packet
from socketio import packet
@@ -32,3 +37,102 @@
assert p.packet_type == packet.ACK
p2 = msgpack_packet.MsgPackPacket(encoded_packet=p.encode())
assert p2.data == {'foo': b'bar'}
+
+ def test_encode_with_dumps_default(self):
+ def default(obj):
+ if isinstance(obj, datetime):
+ return obj.isoformat()
+ raise TypeError('Unknown type')
+
+ data = {
+ 'current': datetime.now(tz=timezone(timedelta(0))),
+ 'key': 'value',
+ }
+ p = msgpack_packet.MsgPackPacket.configure(dumps_default=default)(
+ data=data)
+ p2 = msgpack_packet.MsgPackPacket(encoded_packet=p.encode())
+ assert p.packet_type == p2.packet_type
+ assert p.id == p2.id
+ assert p.namespace == p2.namespace
+ assert p.data != p2.data
+
+ assert isinstance(p2.data, dict)
+ assert 'current' in p2.data
+ assert isinstance(p2.data['current'], str)
+ assert default(data['current']) == p2.data['current']
+
+ data.pop('current')
+ p2_data_without_current = p2.data.copy()
+ p2_data_without_current.pop('current')
+ assert data == p2_data_without_current
+
+ def test_encode_without_dumps_default(self):
+ data = {
+ 'current': datetime.now(tz=timezone(timedelta(0))),
+ 'key': 'value',
+ }
+ p_without_default = msgpack_packet.MsgPackPacket(data=data)
+ with pytest.raises(TypeError):
+ p_without_default.encode()
+
+ def test_encode_decode_with_ext_hook(self):
+ class Custom:
+ def __init__(self, value):
+ self.value = value
+
+ def __eq__(self, value: object) -> bool:
+ return isinstance(value, Custom) and self.value == value.value
+
+ def default(obj):
+ if isinstance(obj, Custom):
+ return msgpack.ExtType(1, obj.value)
+ raise TypeError('Unknown type')
+
+ def ext_hook(code, data):
+ if code == 1:
+ return Custom(data)
+ raise TypeError('Unknown ext type')
+
+ data = {'custom': Custom(b'custom_data'), 'key': 'value'}
+ p = msgpack_packet.MsgPackPacket.configure(dumps_default=default)(
+ data=data)
+ p2 = msgpack_packet.MsgPackPacket.configure(ext_hook=ext_hook)(
+ encoded_packet=p.encode()
+ )
+ assert p.packet_type == p2.packet_type
+ assert p.id == p2.id
+ assert p.data == p2.data
+ assert p.namespace == p2.namespace
+
+ def test_encode_decode_without_ext_hook(self):
+ class Custom:
+ def __init__(self, value):
+ self.value = value
+
+ def __eq__(self, value: object) -> bool:
+ return isinstance(value, Custom) and self.value == value.value
+
+ def default(obj):
+ if isinstance(obj, Custom):
+ return msgpack.ExtType(1, obj.value)
+ raise TypeError('Unknown type')
+
+ data = {'custom': Custom(b'custom_data'), 'key': 'value'}
+ p = msgpack_packet.MsgPackPacket.configure(dumps_default=default)(
+ data=data)
+ p2 = msgpack_packet.MsgPackPacket(encoded_packet=p.encode())
+ assert p.packet_type == p2.packet_type
+ assert p.id == p2.id
+ assert p.namespace == p2.namespace
+ assert p.data != p2.data
+
+ assert isinstance(p2.data, dict)
+ assert 'custom' in p2.data
+ assert isinstance(p2.data['custom'], msgpack.ExtType)
+ assert p2.data['custom'].code == 1
+ assert p2.data['custom'].data == b'custom_data'
+
+ data.pop('custom')
+ p2_data_without_custom = p2.data.copy()
+ p2_data_without_custom.pop('custom')
+ assert data == p2_data_without_custom
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/tests/common/test_packet.py
new/python-socketio-5.15.0/tests/common/test_packet.py
--- old/python-socketio-5.14.3/tests/common/test_packet.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_packet.py 2025-11-22
19:38:45.000000000 +0100
@@ -279,11 +279,15 @@
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([bytearray(b'foo')])
assert pkt.data_is_binary(['foo', b'bar'])
+ assert pkt.data_is_binary(['foo', bytearray(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': bytearray(b'foo')})
assert pkt.data_is_binary({'a': 'foo', 'b': b'bar'})
+ assert pkt.data_is_binary({'a': 'foo', 'b': bytearray(b'bar')})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/tests/common/test_pubsub_manager.py
new/python-socketio-5.15.0/tests/common/test_pubsub_manager.py
--- old/python-socketio-5.14.3/tests/common/test_pubsub_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_pubsub_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -109,6 +109,36 @@
}
)
+ def test_emit_bytearray(self):
+ self.pm.emit('foo', bytearray(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': bytearray(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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-socketio-5.14.3/tests/common/test_redis_manager.py
new/python-socketio-5.15.0/tests/common/test_redis_manager.py
--- old/python-socketio-5.14.3/tests/common/test_redis_manager.py
2025-10-29 10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_redis_manager.py
2025-11-22 19:38:45.000000000 +0100
@@ -12,7 +12,7 @@
redis_manager.redis = None
with pytest.raises(RuntimeError):
- RedisManager('redis://')
+ RedisManager('redis://')._redis_connect()
assert RedisManager('unix:///var/sock/redis.sock') is not None
redis_manager.redis = saved_redis
@@ -22,7 +22,7 @@
redis_manager.valkey = None
with pytest.raises(RuntimeError):
- RedisManager('valkey://')
+ RedisManager('valkey://')._redis_connect()
assert RedisManager('unix:///var/sock/redis.sock') is not None
redis_manager.valkey = saved_valkey
@@ -34,18 +34,18 @@
redis_manager.valkey = None
with pytest.raises(RuntimeError):
- RedisManager('redis://')
+ RedisManager('redis://')._redis_connect()
with pytest.raises(RuntimeError):
- RedisManager('valkey://')
+ RedisManager('valkey://')._redis_connect()
with pytest.raises(RuntimeError):
- RedisManager('unix:///var/sock/redis.sock')
+ RedisManager('unix:///var/sock/redis.sock')._redis_connect()
redis_manager.redis = saved_redis
redis_manager.valkey = saved_valkey
def test_bad_url(self):
with pytest.raises(ValueError):
- RedisManager('http://localhost:6379')
+ RedisManager('http://localhost:6379')._redis_connect()
def test_redis_connect(self):
urls = [
@@ -72,6 +72,8 @@
]
for url in urls:
c = RedisManager(url)
+ assert c.redis is None
+ c._redis_connect()
assert isinstance(c.redis, redis.Redis)
def test_valkey_connect(self):
@@ -102,6 +104,8 @@
]
for url in urls:
c = RedisManager(url)
+ assert c.redis is None
+ c._redis_connect()
assert isinstance(c.redis, valkey.Valkey)
redis_manager.redis = saved_redis
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-socketio-5.14.3/tests/common/test_server.py
new/python-socketio-5.15.0/tests/common/test_server.py
--- old/python-socketio-5.14.3/tests/common/test_server.py 2025-10-29
10:42:20.000000000 +0100
+++ new/python-socketio-5.15.0/tests/common/test_server.py 2025-11-22
19:38:45.000000000 +0100
@@ -1,5 +1,6 @@
import logging
from unittest import mock
+from datetime import datetime, timezone, timedelta
from engineio import json
from engineio import packet as eio_packet
@@ -10,6 +11,7 @@
from socketio import namespace
from socketio import packet
from socketio import server
+from socketio.msgpack_packet import MsgPackPacket
@mock.patch('socketio.server.engineio.Server', **{
@@ -1032,3 +1034,21 @@
s = server.Server()
s.sleep(1.23)
s.eio.sleep.assert_called_once_with(1.23)
+
+ def test_serializer_args_with_msgpack(self, eio):
+ def default(o):
+ if isinstance(o, datetime):
+ return o.isoformat()
+ raise TypeError("Unknown type")
+
+ data = {"current": datetime.now(timezone(timedelta(0)))}
+ s = server.Server(
+ serializer=MsgPackPacket.configure(dumps_default=default))
+ p = s.packet_class(data=data)
+ p2 = s.packet_class(encoded_packet=p.encode())
+
+ assert p.data != p2.data
+ assert isinstance(p2.data, dict)
+ assert "current" in p2.data
+ assert isinstance(p2.data["current"], str)
+ assert default(data["current"]) == p2.data["current"]