python-socketio
Advanced tools
+8
-3
@@ -201,6 +201,11 @@ The Socket.IO Clients | ||
| 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 | ||
@@ -207,0 +212,0 @@ disconnections and other issues. |
+8
-3
@@ -660,6 +660,11 @@ The Socket.IO Server | ||
| 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, | ||
@@ -666,0 +671,0 @@ bad performance and other issues. |
+1
-1
| Metadata-Version: 2.4 | ||
| Name: python-socketio | ||
| Version: 5.14.3 | ||
| Version: 5.15.0 | ||
| Summary: Socket.IO server and client for Python | ||
@@ -5,0 +5,0 @@ Author-email: Miguel Grinberg <miguel.grinberg@gmail.com> |
+1
-1
| [project] | ||
| name = "python-socketio" | ||
| version = "5.14.3" | ||
| version = "5.15.0" | ||
| license = {text = "MIT"} | ||
@@ -5,0 +5,0 @@ authors = [ |
| Metadata-Version: 2.4 | ||
| Name: python-socketio | ||
| Version: 5.14.3 | ||
| Version: 5.15.0 | ||
| Summary: Socket.IO server and client for Python | ||
@@ -5,0 +5,0 @@ Author-email: Miguel Grinberg <miguel.grinberg@gmail.com> |
@@ -104,11 +104,11 @@ import asyncio | ||
| 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: | ||
@@ -119,10 +119,10 @@ async for message in queue_iter: | ||
| 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() |
@@ -492,3 +492,3 @@ import asyncio | ||
| # 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) | ||
@@ -495,0 +495,0 @@ if handler: |
@@ -64,3 +64,5 @@ import asyncio | ||
| self.redis_options = redis_options or {} | ||
| self._redis_connect() | ||
| self.connected = False | ||
| self.redis = None | ||
| self.pubsub = None | ||
@@ -110,9 +112,9 @@ def _get_redis_module_and_error(self): | ||
| 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() | ||
@@ -122,3 +124,3 @@ return await self.redis.publish( | ||
| except error as exc: | ||
| if retry: | ||
| if retries_left > 0: | ||
| self._get_logger().error( | ||
@@ -128,3 +130,3 @@ 'Cannot publish to redis... ' | ||
| extra={"redis_exception": str(exc)}) | ||
| retry = False | ||
| self.connected = False | ||
| else: | ||
@@ -139,8 +141,8 @@ self._get_logger().error( | ||
| async def _redis_listen_with_retries(self): # pragma: no cover | ||
| _, error = self._get_redis_module_and_error() | ||
| retry_sleep = 1 | ||
| connect = False | ||
| _, error = self._get_redis_module_and_error() | ||
| subscribed = False | ||
| while True: | ||
| try: | ||
| if connect: | ||
| if not subscribed: | ||
| self._redis_connect() | ||
@@ -156,3 +158,3 @@ await self.pubsub.subscribe(self.channel) | ||
| extra={"redis_exception": str(exc)}) | ||
| connect = True | ||
| subscribed = False | ||
| await asyncio.sleep(retry_sleep) | ||
@@ -165,3 +167,2 @@ retry_sleep *= 2 | ||
| channel = self.channel.encode('utf-8') | ||
| await self.pubsub.subscribe(self.channel) | ||
| async for message in self._redis_listen_with_retries(): | ||
@@ -168,0 +169,0 @@ if message['channel'] == channel and \ |
@@ -118,6 +118,6 @@ import time | ||
| def _listen(self): | ||
| reader_queue = self._queue() | ||
| retry_sleep = 1 | ||
| while True: | ||
| try: | ||
| reader_queue = self._queue() | ||
| with self._connection() as connection: | ||
@@ -124,0 +124,0 @@ with connection.SimpleQueue(reader_queue) as queue: |
@@ -7,10 +7,36 @@ import msgpack | ||
| 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'] | ||
@@ -17,0 +43,0 @@ self.data = decoded.get('data') |
@@ -157,3 +157,3 @@ import functools | ||
| def _deconstruct_binary_internal(cls, data, attachments): | ||
| if isinstance(data, bytes): | ||
| if isinstance(data, (bytes, bytearray)): | ||
| attachments.append(data) | ||
@@ -173,3 +173,3 @@ return {'_placeholder': True, 'num': len(attachments) - 1} | ||
| """Check if the data contains binary components.""" | ||
| if isinstance(data, bytes): | ||
| if isinstance(data, (bytes, bytearray)): | ||
| return True | ||
@@ -176,0 +176,0 @@ elif isinstance(data, list): |
@@ -86,3 +86,5 @@ import logging | ||
| self.redis_options = redis_options or {} | ||
| self._redis_connect() | ||
| self.connected = False | ||
| self.redis = None | ||
| self.pubsub = None | ||
@@ -147,13 +149,13 @@ def initialize(self): # pragma: no cover | ||
| 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( | ||
@@ -163,3 +165,3 @@ 'Cannot publish to redis... retrying', | ||
| ) | ||
| retry = False | ||
| self.connected = False | ||
| else: | ||
@@ -173,8 +175,8 @@ logger.error( | ||
| def _redis_listen_with_retries(self): # pragma: no cover | ||
| _, error = self._get_redis_module_and_error() | ||
| retry_sleep = 1 | ||
| connect = False | ||
| _, error = self._get_redis_module_and_error() | ||
| subscribed = False | ||
| while True: | ||
| try: | ||
| if connect: | ||
| if not subscribed: | ||
| self._redis_connect() | ||
@@ -188,3 +190,3 @@ self.pubsub.subscribe(self.channel) | ||
| extra={"redis_exception": str(exc)}) | ||
| connect = True | ||
| subscribed = False | ||
| time.sleep(retry_sleep) | ||
@@ -197,3 +199,2 @@ retry_sleep *= 2 | ||
| channel = self.channel.encode('utf-8') | ||
| self.pubsub.subscribe(self.channel) | ||
| for message in self._redis_listen_with_retries(): | ||
@@ -200,0 +201,0 @@ if message['channel'] == channel and \ |
| import asyncio | ||
| from unittest import mock | ||
| from datetime import datetime, timezone, timedelta | ||
@@ -11,2 +12,3 @@ import pytest | ||
| from socketio import packet | ||
| from socketio.msgpack_packet import MsgPackPacket | ||
@@ -1246,1 +1248,19 @@ | ||
| 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"] |
@@ -100,2 +100,32 @@ import asyncio | ||
| 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): | ||
@@ -102,0 +132,0 @@ sid = 'room-mate' |
@@ -15,3 +15,3 @@ import pytest | ||
| with pytest.raises(RuntimeError): | ||
| AsyncRedisManager('redis://') | ||
| AsyncRedisManager('redis://')._redis_connect() | ||
| assert AsyncRedisManager('unix:///var/sock/redis.sock') is not None | ||
@@ -26,3 +26,3 @@ | ||
| with pytest.raises(RuntimeError): | ||
| AsyncRedisManager('valkey://') | ||
| AsyncRedisManager('valkey://')._redis_connect() | ||
| assert AsyncRedisManager('unix:///var/sock/redis.sock') is not None | ||
@@ -39,7 +39,7 @@ | ||
| 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() | ||
@@ -51,3 +51,3 @@ async_redis_manager.aioredis = saved_redis | ||
| with pytest.raises(ValueError): | ||
| AsyncRedisManager('http://localhost:6379') | ||
| AsyncRedisManager('http://localhost:6379')._redis_connect() | ||
@@ -79,2 +79,4 @@ def test_redis_connect(self): | ||
| c = AsyncRedisManager(url) | ||
| assert c.redis is None | ||
| c._redis_connect() | ||
| assert isinstance(c.redis, redis.asyncio.Redis) | ||
@@ -110,4 +112,6 @@ | ||
| c = AsyncRedisManager(url) | ||
| assert c.redis is None | ||
| c._redis_connect() | ||
| assert isinstance(c.redis, valkey.asyncio.Valkey) | ||
| async_redis_manager.aioredis = saved_redis |
| import asyncio | ||
| import logging | ||
| from unittest import mock | ||
| from datetime import datetime, timezone, timedelta | ||
@@ -14,2 +15,3 @@ from engineio import json | ||
| from socketio import packet | ||
| from socketio.msgpack_packet import MsgPackPacket | ||
@@ -1093,1 +1095,19 @@ | ||
| 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"] |
| import logging | ||
| import time | ||
| from unittest import mock | ||
| from datetime import datetime, timezone, timedelta | ||
@@ -16,2 +17,3 @@ from engineio import exceptions as engineio_exceptions | ||
| from socketio import packet | ||
| from socketio.msgpack_packet import MsgPackPacket | ||
@@ -1390,1 +1392,19 @@ | ||
| 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"] |
@@ -0,1 +1,6 @@ | ||
| from datetime import datetime, timedelta, timezone | ||
| import pytest | ||
| import msgpack | ||
| from socketio import msgpack_packet | ||
@@ -35,1 +40,100 @@ from socketio import packet | ||
| 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 |
@@ -282,3 +282,5 @@ import pytest | ||
| 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')]) | ||
@@ -290,2 +292,4 @@ def test_data_is_binary_dict(self): | ||
| 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')}) |
@@ -112,2 +112,32 @@ import functools | ||
| 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): | ||
@@ -114,0 +144,0 @@ sid = "ferris" |
@@ -15,3 +15,3 @@ import pytest | ||
| with pytest.raises(RuntimeError): | ||
| RedisManager('redis://') | ||
| RedisManager('redis://')._redis_connect() | ||
| assert RedisManager('unix:///var/sock/redis.sock') is not None | ||
@@ -26,3 +26,3 @@ | ||
| with pytest.raises(RuntimeError): | ||
| RedisManager('valkey://') | ||
| RedisManager('valkey://')._redis_connect() | ||
| assert RedisManager('unix:///var/sock/redis.sock') is not None | ||
@@ -39,7 +39,7 @@ | ||
| 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() | ||
@@ -51,3 +51,3 @@ redis_manager.redis = saved_redis | ||
| with pytest.raises(ValueError): | ||
| RedisManager('http://localhost:6379') | ||
| RedisManager('http://localhost:6379')._redis_connect() | ||
@@ -79,2 +79,4 @@ def test_redis_connect(self): | ||
| c = RedisManager(url) | ||
| assert c.redis is None | ||
| c._redis_connect() | ||
| assert isinstance(c.redis, redis.Redis) | ||
@@ -110,2 +112,4 @@ | ||
| c = RedisManager(url) | ||
| assert c.redis is None | ||
| c._redis_connect() | ||
| assert isinstance(c.redis, valkey.Valkey) | ||
@@ -112,0 +116,0 @@ |
| import logging | ||
| from unittest import mock | ||
| from datetime import datetime, timezone, timedelta | ||
@@ -13,2 +14,3 @@ from engineio import json | ||
| from socketio import server | ||
| from socketio.msgpack_packet import MsgPackPacket | ||
@@ -1036,1 +1038,19 @@ | ||
| 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"] |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
761446
1.49%14616
1.73%