Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-hiredis for openSUSE:Factory checked in at 2025-10-01 19:07:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-hiredis (Old) and /work/SRC/openSUSE:Factory/.python-hiredis.new.11973 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-hiredis" Wed Oct 1 19:07:05 2025 rev:13 rq:1308428 version:3.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-hiredis/python-hiredis.changes 2024-01-05 22:59:23.809853110 +0100 +++ /work/SRC/openSUSE:Factory/.python-hiredis.new.11973/python-hiredis.changes 2025-10-01 19:07:07.219943895 +0200 @@ -1,0 +2,11 @@ +Wed Oct 1 11:06:20 UTC 2025 - Markéta Machová <[email protected]> + +- update to 3.2.1 + * BREAKING: Return Redis sets as Python lists + * Update Python 3.13 compatibility + * Fix memory leaks and segfaults in RESP3 map parsing + * Update hiredis to 1.3.0 +- Drop 159-sdsalloc-to-alloc.patch, merged upstream +- Run tests + +------------------------------------------------------------------- Old: ---- 159-sdsalloc-to-alloc.patch hiredis-2.3.2.tar.gz New: ---- hiredis-3.2.1.tar.gz ----------(Old B)---------- Old: * Update hiredis to 1.3.0 - Drop 159-sdsalloc-to-alloc.patch, merged upstream - Run tests ----------(Old E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-hiredis.spec ++++++ --- /var/tmp/diff_new_pack.Ciq2a6/_old 2025-10-01 19:07:07.975975560 +0200 +++ /var/tmp/diff_new_pack.Ciq2a6/_new 2025-10-01 19:07:07.979975727 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-hiredis # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-hiredis -Version: 2.3.2 +Version: 3.2.1 Release: 0 Summary: Python wrapper for hiredis License: BSD-3-Clause @@ -27,18 +27,16 @@ # PATCH-FIX-UPSTREAM drop-vendor-sources.patch gh#redis/hiredis-py#90 [email protected] # Allow to use platform hiredis libs on build Patch0: drop-vendor-sources.patch -# PATCH-FIX-UPSTREAM 159-sdsalloc-to-alloc.patch gh#redis/hiredis-py#158 [email protected] -# Don't use sdsalloc, we actually don't need it -Patch1: 159-sdsalloc-to-alloc.patch # PATCH-FIX-UPSTREAM 161-use-system-hiredis.patch gh#redis/hiredis-py#158 [email protected] # use system hiredis instead Patch2: 161-use-system-hiredis.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module pip} +BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} BuildRequires: fdupes -BuildRequires: hiredis-devel >= 1.0.0 +BuildRequires: hiredis-devel >= 1.3.0 BuildRequires: python-rpm-macros %python_subpackages @@ -58,9 +56,8 @@ %pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitearch} -# %%check -# export PYTHONPATH=%%{buildroot}%%{$python_sitearch} -# %%python_exec test.py +%check +%pytest_arch %files %{python_files} %license LICENSE ++++++ hiredis-2.3.2.tar.gz -> hiredis-3.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/PKG-INFO new/hiredis-3.2.1/PKG-INFO --- old/hiredis-2.3.2/PKG-INFO 2023-12-17 13:26:35.074601400 +0100 +++ new/hiredis-3.2.1/PKG-INFO 2025-05-23 12:50:15.495752000 +0200 @@ -1,31 +1,31 @@ Metadata-Version: 2.1 Name: hiredis -Version: 2.3.2 +Version: 3.2.1 Summary: Python wrapper for hiredis Home-page: https://github.com/redis/hiredis-py Author: Jan-Erik Rediger, Pieter Noordhuis Author-email: [email protected], [email protected] -License: BSD +License: MIT Project-URL: Changes, https://github.com/redis/hiredis-py/releases Project-URL: Issue tracker, https://github.com/redis/hiredis-py/issues Keywords: Redis Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License +Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: MacOS Classifier: Operating System :: POSIX Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development -Requires-Python: >=3.7 +Requires-Python: >=3.8 Description-Content-Type: text/markdown License-File: LICENSE @@ -33,6 +33,7 @@ [](https://github.com/redis/hiredis-py/actions/workflows/integration.yaml) [](https://opensource.org/licenses/BSD-3-Clause) +[](https://pypi.org/project/hiredis/) Python extension that wraps protocol parsing code in [hiredis][hiredis]. It primarily speeds up parsing of multi bulk replies. @@ -65,14 +66,14 @@ Building this repository requires a recursive checkout of submodules, and building hiredis. The following example shows how to clone, compile, and run tests. Please note - you will need the gcc installed. ```bash -git clone --recursse-submodules https://github.com/redis/hiredis-py +git clone --recurse-submodules https://github.com/redis/hiredis-py python setup.py build_ext --inplace python -m pytest ``` ### Requirements -hiredis-py requires **Python 3.7+**. +hiredis-py requires **Python 3.8+**. Make sure Python development headers are available when installing hiredis-py. On Ubuntu/Debian systems, install them with `apt-get install python3-dev`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/README.md new/hiredis-3.2.1/README.md --- old/hiredis-2.3.2/README.md 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/README.md 2025-05-23 12:50:12.000000000 +0200 @@ -2,6 +2,7 @@ [](https://github.com/redis/hiredis-py/actions/workflows/integration.yaml) [](https://opensource.org/licenses/BSD-3-Clause) +[](https://pypi.org/project/hiredis/) Python extension that wraps protocol parsing code in [hiredis][hiredis]. It primarily speeds up parsing of multi bulk replies. @@ -34,14 +35,14 @@ Building this repository requires a recursive checkout of submodules, and building hiredis. The following example shows how to clone, compile, and run tests. Please note - you will need the gcc installed. ```bash -git clone --recursse-submodules https://github.com/redis/hiredis-py +git clone --recurse-submodules https://github.com/redis/hiredis-py python setup.py build_ext --inplace python -m pytest ``` ### Requirements -hiredis-py requires **Python 3.7+**. +hiredis-py requires **Python 3.8+**. Make sure Python development headers are available when installing hiredis-py. On Ubuntu/Debian systems, install them with `apt-get install python3-dev`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/hiredis/__init__.py new/hiredis-3.2.1/hiredis/__init__.py --- old/hiredis-2.3.2/hiredis/__init__.py 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/hiredis/__init__.py 2025-05-23 12:50:12.000000000 +0200 @@ -1,4 +1,4 @@ -from hiredis.hiredis import Reader, HiredisError, pack_command, ProtocolError, ReplyError +from hiredis.hiredis import Reader, HiredisError, pack_command, ProtocolError, ReplyError, PushNotification from hiredis.version import __version__ __all__ = [ @@ -6,5 +6,6 @@ "HiredisError", "pack_command", "ProtocolError", + "PushNotification", "ReplyError", "__version__"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/hiredis/hiredis.pyi new/hiredis-3.2.1/hiredis/hiredis.pyi --- old/hiredis-2.3.2/hiredis/hiredis.pyi 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/hiredis/hiredis.pyi 2025-05-23 12:50:12.000000000 +0200 @@ -13,6 +13,10 @@ ... +class PushNotification(list): + ... + + class Reader: def __init__( self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/hiredis/version.py new/hiredis-3.2.1/hiredis/version.py --- old/hiredis-2.3.2/hiredis/version.py 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/hiredis/version.py 2025-05-23 12:50:12.000000000 +0200 @@ -1 +1 @@ -__version__ = "2.3.2" +__version__ = "3.2.1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/hiredis.egg-info/PKG-INFO new/hiredis-3.2.1/hiredis.egg-info/PKG-INFO --- old/hiredis-2.3.2/hiredis.egg-info/PKG-INFO 2023-12-17 13:26:34.000000000 +0100 +++ new/hiredis-3.2.1/hiredis.egg-info/PKG-INFO 2025-05-23 12:50:15.000000000 +0200 @@ -1,31 +1,31 @@ Metadata-Version: 2.1 Name: hiredis -Version: 2.3.2 +Version: 3.2.1 Summary: Python wrapper for hiredis Home-page: https://github.com/redis/hiredis-py Author: Jan-Erik Rediger, Pieter Noordhuis Author-email: [email protected], [email protected] -License: BSD +License: MIT Project-URL: Changes, https://github.com/redis/hiredis-py/releases Project-URL: Issue tracker, https://github.com/redis/hiredis-py/issues Keywords: Redis Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License +Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: MacOS Classifier: Operating System :: POSIX Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development -Requires-Python: >=3.7 +Requires-Python: >=3.8 Description-Content-Type: text/markdown License-File: LICENSE @@ -33,6 +33,7 @@ [](https://github.com/redis/hiredis-py/actions/workflows/integration.yaml) [](https://opensource.org/licenses/BSD-3-Clause) +[](https://pypi.org/project/hiredis/) Python extension that wraps protocol parsing code in [hiredis][hiredis]. It primarily speeds up parsing of multi bulk replies. @@ -65,14 +66,14 @@ Building this repository requires a recursive checkout of submodules, and building hiredis. The following example shows how to clone, compile, and run tests. Please note - you will need the gcc installed. ```bash -git clone --recursse-submodules https://github.com/redis/hiredis-py +git clone --recurse-submodules https://github.com/redis/hiredis-py python setup.py build_ext --inplace python -m pytest ``` ### Requirements -hiredis-py requires **Python 3.7+**. +hiredis-py requires **Python 3.8+**. Make sure Python development headers are available when installing hiredis-py. On Ubuntu/Debian systems, install them with `apt-get install python3-dev`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/setup.py new/hiredis-3.2.1/setup.py --- old/hiredis-2.3.2/setup.py 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/setup.py 2025-05-23 12:50:12.000000000 +0200 @@ -60,11 +60,11 @@ author="Jan-Erik Rediger, Pieter Noordhuis", author_email="[email protected], [email protected]", keywords=["Redis"], - license="BSD", + license="MIT", packages=["hiredis"], package_data={"hiredis": ["hiredis.pyi", "py.typed"]}, ext_modules=[ext], - python_requires=">=3.7", + python_requires=">=3.8", project_urls={ "Changes": "https://github.com/redis/hiredis-py/releases", "Issue tracker": "https://github.com/redis/hiredis-py/issues", @@ -72,18 +72,18 @@ classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', + 'License :: OSI Approved :: MIT License', 'Operating System :: MacOS', 'Operating System :: POSIX', 'Programming Language :: C', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Software Development', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/src/hiredis.c new/hiredis-3.2.1/src/hiredis.c --- old/hiredis-2.3.2/src/hiredis.c 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/src/hiredis.c 2025-05-23 12:50:12.000000000 +0200 @@ -59,6 +59,11 @@ return NULL; } + PushNotificationType.tp_base = &PyList_Type; + if (PyType_Ready(&PushNotificationType) < 0) { + return NULL; + } + mod_hiredis = PyModule_Create(&hiredis_ModuleDef); /* Setup custom exceptions */ @@ -79,5 +84,8 @@ Py_INCREF(&hiredis_ReaderType); PyModule_AddObject(mod_hiredis, "Reader", (PyObject *)&hiredis_ReaderType); + Py_INCREF(&PushNotificationType); + PyModule_AddObject(mod_hiredis, "PushNotification", (PyObject *)&PushNotificationType); + return mod_hiredis; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/src/pack.c new/hiredis-3.2.1/src/pack.c --- old/hiredis-2.3.2/src/pack.c 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/src/pack.c 2025-05-23 12:50:12.000000000 +0200 @@ -16,7 +16,7 @@ extern sds sdsnewlen(const void *init, size_t initlen); #endif -#include <hiredis/sdsalloc.h> +#include <hiredis/alloc.h> PyObject * pack_command(PyObject *cmd) @@ -32,7 +32,7 @@ } Py_ssize_t tokens_number = PyTuple_Size(cmd); - sds *tokens = s_malloc(sizeof(sds) * tokens_number); + sds *tokens = hi_malloc(sizeof(sds) * tokens_number); if (tokens == NULL) { return PyErr_NoMemory(); @@ -118,4 +118,4 @@ sdsfreesplitres(tokens, tokens_number); hi_free(lengths); return result; -} \ No newline at end of file +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/src/reader.c new/hiredis-3.2.1/src/reader.c --- old/hiredis-2.3.2/src/reader.c 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/src/reader.c 2025-05-23 12:50:12.000000000 +0200 @@ -1,6 +1,7 @@ #include "reader.h" #include <assert.h> +#include <Python.h> static void Reader_dealloc(hiredis_ReaderObject *self); static int Reader_traverse(hiredis_ReaderObject *self, visitproc visit, void *arg); @@ -14,6 +15,10 @@ static PyObject *Reader_has_data(hiredis_ReaderObject *self); static PyObject *Reader_set_encoding(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds); +static int PushNotificationType_init(PushNotificationObject *self, PyObject *args, PyObject *kwds); +/* Create a new instance of PushNotificationType with preallocated number of elements */ +static PyObject* PushNotificationType_New(Py_ssize_t size); + static PyMethodDef hiredis_ReaderMethods[] = { {"feed", (PyCFunction)Reader_feed, METH_VARARGS, NULL }, {"gets", (PyCFunction)Reader_gets, METH_VARARGS, NULL }, @@ -66,6 +71,16 @@ Reader_new, /*tp_new */ }; +PyTypeObject PushNotificationType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = MOD_HIREDIS ".PushNotification", + .tp_basicsize = sizeof(PushNotificationObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "Redis PUSH notification type", + .tp_init = (initproc) PushNotificationType_init, +}; + static void *tryParentize(const redisReadTask *task, PyObject *obj) { PyObject *parent; if (task && task->parent) { @@ -74,20 +89,23 @@ case REDIS_REPLY_MAP: if (task->idx % 2 == 0) { /* Set a temporary item to save the object as a key. */ - PyDict_SetItem(parent, obj, Py_None); + int res = PyDict_SetItem(parent, obj, Py_None); + Py_DECREF(obj); + + if (res == -1) { + return NULL; + } } else { /* Pop the temporary item and set proper key and value. */ PyObject *last_item = PyObject_CallMethod(parent, "popitem", NULL); PyObject *last_key = PyTuple_GetItem(last_item, 0); PyDict_SetItem(parent, last_key, obj); + Py_DECREF(last_item); + Py_DECREF(obj); } break; - case REDIS_REPLY_SET: - assert(PyAnySet_CheckExact(parent)); - PySet_Add(parent, obj); - break; default: - assert(PyList_CheckExact(parent)); + assert(PyList_Check(parent)); PyList_SET_ITEM(parent, task->idx, obj); } } @@ -162,8 +180,8 @@ case REDIS_REPLY_MAP: obj = PyDict_New(); break; - case REDIS_REPLY_SET: - obj = PySet_New(NULL); + case REDIS_REPLY_PUSH: + obj = PushNotificationType_New(elements); break; default: obj = PyList_New(elements); @@ -199,6 +217,41 @@ Py_XDECREF(obj); } +static int PushNotificationType_init(PushNotificationObject *self, PyObject *args, PyObject *kwds) { + return PyList_Type.tp_init((PyObject *)self, args, kwds); +} + +static PyObject* PushNotificationType_New(Py_ssize_t size) { + /* Check for negative size */ + if (size < 0) { + PyErr_SetString(PyExc_SystemError, "negative list size"); + return NULL; + } + + /* Check for potential overflow */ + if ((size_t)size > PY_SSIZE_T_MAX / sizeof(PyObject*)) { + return PyErr_NoMemory(); + } + +#ifdef PYPY_VERSION + PyObject* obj = PyObject_CallObject((PyObject *) &PushNotificationType, NULL); +#else + PyObject* obj = PyType_GenericNew(&PushNotificationType, NULL, NULL); +#endif + if (obj == NULL) { + return NULL; + } + + int res = PyList_SetSlice(obj, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, PyList_New(size)); + + if (res == -1) { + Py_DECREF(obj); + return NULL; + } + + return obj; +} + redisReplyObjectFunctions hiredis_ObjectFunctions = { createStringObject, // void *(*createString)(const redisReadTask*, char*, size_t); createArrayObject, // void *(*createArray)(const redisReadTask*, size_t); @@ -366,8 +419,6 @@ static PyObject *Reader_gets(hiredis_ReaderObject *self, PyObject *args) { PyObject *obj; - PyObject *err; - char *errstr; self->shouldDecode = 1; if (!PyArg_ParseTuple(args, "|i", &self->shouldDecode)) { @@ -375,9 +426,17 @@ } if (redisReaderGetReply(self->reader, (void**)&obj) == REDIS_ERR) { - errstr = redisReaderGetError(self->reader); - /* protocolErrorClass might be a callable. call it, then use it's type */ - err = createError(self->protocolErrorClass, errstr, strlen(errstr)); + PyObject *err = NULL; + char *errstr = NULL; + + // Checking if there is no error during the call to redisReaderGetReply + // to avoid getting a SystemError. + if (PyErr_Occurred() == NULL) { + errstr = redisReaderGetError(self->reader); + /* protocolErrorClass might be a callable. call it, then use it's type */ + err = createError(self->protocolErrorClass, errstr, strlen(errstr)); + } + if (err != NULL) { obj = PyObject_Type(err); PyErr_SetString(obj, errstr); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/src/reader.h new/hiredis-3.2.1/src/reader.h --- old/hiredis-2.3.2/src/reader.h 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/src/reader.h 2025-05-23 12:50:12.000000000 +0200 @@ -23,7 +23,12 @@ } error; } hiredis_ReaderObject; +typedef struct { + PyListObject list; +} PushNotificationObject; + extern PyTypeObject hiredis_ReaderType; +extern PyTypeObject PushNotificationType; extern redisReplyObjectFunctions hiredis_ObjectFunctions; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/tests/test_reader.py new/hiredis-3.2.1/tests/test_reader.py --- old/hiredis-2.3.2/tests/test_reader.py 2023-12-17 13:26:29.000000000 +0100 +++ new/hiredis-3.2.1/tests/test_reader.py 2025-05-23 12:50:12.000000000 +0200 @@ -136,15 +136,60 @@ def test_set(reader): reader.feed(b"~3\r\n+tangerine\r\n_\r\n,10.5\r\n") - assert {b"tangerine", None, 10.5} == reader.gets() + assert [b"tangerine", None, 10.5] == reader.gets() + +def test_set_with_nested_dict(reader): + reader.feed(b"~2\r\n+tangerine\r\n%1\r\n+a\r\n:1\r\n") + assert [b"tangerine", {b"a": 1}] == reader.gets() def test_dict(reader): reader.feed(b"%2\r\n+radius\r\n,4.5\r\n+diameter\r\n:9\r\n") assert {b"radius": 4.5, b"diameter": 9} == reader.gets() -def test_vector(reader): [email protected]_memory("50 KB") +def test_dict_memory_leaks(reader): + data = ( + b"%5\r\n" + b"+radius\r\n,4.5\r\n" + b"+diameter\r\n:9\r\n" + b"+nested_map\r\n" + b"%2\r\n" + b"+key1\r\n+value1\r\n" + b"+key2\r\n:42\r\n" + b"+nested_array\r\n" + b"*2\r\n" + b"+item1\r\n" + b"+item2\r\n" + b"+nested_set\r\n" + b"~2\r\n" + b"+element1\r\n" + b"+element2\r\n" + ) + for i in range(10000): + reader.feed(data) + res = reader.gets() + assert { + b"radius": 4.5, + b"diameter": 9, + b"nested_map": {b"key1": b"value1", b"key2": 42}, + b"nested_array": [b"item1", b"item2"], + b"nested_set": [b"element1", b"element2"], + } == res + +def test_dict_with_unhashable_key(reader): + reader.feed( + b"%1\r\n" + b"%1\r\n+key1\r\n+value1\r\n" + b":9\r\n" + ) + with pytest.raises(TypeError): + reader.gets() + +def test_vector(reader): reader.feed(b">4\r\n+pubsub\r\n+message\r\n+channel\r\n+message\r\n") - assert [b"pubsub", b"message", b"channel", b"message"] == reader.gets() + result = reader.gets() + assert isinstance(result, hiredis.PushNotification) + assert [b"pubsub", b"message", b"channel", b"message"] == result def test_verbatim_string(reader): value = b"text" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/async.c new/hiredis-3.2.1/vendor/hiredis/async.c --- old/hiredis-2.3.2/vendor/hiredis/async.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/async.c 2025-05-23 12:50:14.000000000 +0200 @@ -478,7 +478,7 @@ /* Match reply with the expected format of a pushed message. * The type and number of elements (3 to 4) are specified at: - * https://redis.io/topics/pubsub#format-of-pushed-messages */ + * https://redis.io/docs/latest/develop/interact/pubsub/#format-of-pushed-messages */ if ((reply->type == REDIS_REPLY_ARRAY && !(c->flags & REDIS_SUPPORTS_PUSH) && reply->elements >= 3) || reply->type == REDIS_REPLY_PUSH) { assert(reply->element[0]->type == REDIS_REPLY_STRING); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/hiredis.c new/hiredis-3.2.1/vendor/hiredis/hiredis.c --- old/hiredis-2.3.2/vendor/hiredis/hiredis.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/hiredis.c 2025-05-23 12:50:14.000000000 +0200 @@ -102,6 +102,7 @@ break; /* Nothing to free */ case REDIS_REPLY_ARRAY: case REDIS_REPLY_MAP: + case REDIS_REPLY_ATTR: case REDIS_REPLY_SET: case REDIS_REPLY_PUSH: if (r->element != NULL) { @@ -160,6 +161,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; @@ -192,6 +194,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; @@ -212,6 +215,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; @@ -249,6 +253,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; @@ -267,6 +272,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; @@ -287,6 +293,7 @@ parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY || parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_ATTR || parent->type == REDIS_REPLY_SET || parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/hiredis.h new/hiredis-3.2.1/vendor/hiredis/hiredis.h --- old/hiredis-2.3.2/vendor/hiredis/hiredis.h 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/hiredis.h 2025-05-23 12:50:14.000000000 +0200 @@ -46,9 +46,9 @@ #include "alloc.h" /* for allocation wrappers */ #define HIREDIS_MAJOR 1 -#define HIREDIS_MINOR 2 +#define HIREDIS_MINOR 3 #define HIREDIS_PATCH 0 -#define HIREDIS_SONAME 1.1.0 +#define HIREDIS_SONAME 1.3.0 /* Connection type can be blocking or non-blocking and is set in the * least significant bit of the flags field in redisContext. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/net.c new/hiredis-3.2.1/vendor/hiredis/net.c --- old/hiredis-2.3.2/vendor/hiredis/net.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/net.c 2025-05-23 12:50:14.000000000 +0200 @@ -41,6 +41,7 @@ #include <stdio.h> #include <limits.h> #include <stdlib.h> +#include <time.h> #include "net.h" #include "sds.h" @@ -172,6 +173,10 @@ int val = 1; redisFD fd = c->fd; + /* TCP_KEEPALIVE makes no sense with AF_UNIX connections */ + if (c->connection_type == REDIS_CONN_UNIX) + return REDIS_ERR; + #ifndef _WIN32 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); @@ -271,37 +276,54 @@ return REDIS_OK; } +static long redisPollMillis(void) { +#ifndef _MSC_VER + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return (now.tv_sec * 1000) + now.tv_nsec / 1000000; +#else + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10; +#endif +} + static int redisContextWaitReady(redisContext *c, long msec) { - struct pollfd wfd[1]; + struct pollfd wfd; + long end; + int res; - wfd[0].fd = c->fd; - wfd[0].events = POLLOUT; + if (errno != EINPROGRESS) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + redisNetClose(c); + return REDIS_ERR; + } - if (errno == EINPROGRESS) { - int res; + wfd.fd = c->fd; + wfd.events = POLLOUT; + end = msec >= 0 ? redisPollMillis() + msec : 0; - if ((res = poll(wfd, 1, msec)) == -1) { + while ((res = poll(&wfd, 1, msec)) <= 0) { + if (res < 0 && errno != EINTR) { __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); redisNetClose(c); return REDIS_ERR; - } else if (res == 0) { + } else if (res == 0 || (msec >= 0 && redisPollMillis() >= end)) { errno = ETIMEDOUT; - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL); redisNetClose(c); return REDIS_ERR; + } else { + /* res < 0 && errno == EINTR, try again */ } + } - if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) { - redisCheckSocketError(c); - return REDIS_ERR; - } - - return REDIS_OK; + if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) { + redisCheckSocketError(c); + return REDIS_ERR; } - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisNetClose(c); - return REDIS_ERR; + return REDIS_OK; } int redisCheckConnectDone(redisContext *c, int *completed) { @@ -646,7 +668,7 @@ sa->sun_family = AF_UNIX; strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { - if (errno == EINPROGRESS && !blocking) { + if ((errno == EAGAIN || errno == EINPROGRESS) && !blocking) { /* This is ok. */ } else { if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/read.c new/hiredis-3.2.1/vendor/hiredis/read.c --- old/hiredis-2.3.2/vendor/hiredis/read.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/read.c 2025-05-23 12:50:14.000000000 +0200 @@ -250,6 +250,7 @@ prv = r->task[r->ridx-1]; assert(prv->type == REDIS_REPLY_ARRAY || prv->type == REDIS_REPLY_MAP || + prv->type == REDIS_REPLY_ATTR || prv->type == REDIS_REPLY_SET || prv->type == REDIS_REPLY_PUSH); if (cur->idx == prv->elements-1) { @@ -534,7 +535,7 @@ moveToNextTask(r); } else { - if (cur->type == REDIS_REPLY_MAP) elements *= 2; + if (cur->type == REDIS_REPLY_MAP || cur->type == REDIS_REPLY_ATTR) elements *= 2; if (r->fn && r->fn->createArray) obj = r->fn->createArray(cur,elements); @@ -602,6 +603,9 @@ case '%': cur->type = REDIS_REPLY_MAP; break; + case '|': + cur->type = REDIS_REPLY_ATTR; + break; case '~': cur->type = REDIS_REPLY_SET; break; @@ -642,6 +646,7 @@ return processBulkItem(r); case REDIS_REPLY_ARRAY: case REDIS_REPLY_MAP: + case REDIS_REPLY_ATTR: case REDIS_REPLY_SET: case REDIS_REPLY_PUSH: return processAggregateItem(r); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/sds.c new/hiredis-3.2.1/vendor/hiredis/sds.c --- old/hiredis-2.3.2/vendor/hiredis/sds.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/sds.c 2025-05-23 12:50:14.000000000 +0200 @@ -692,10 +692,10 @@ * Output will be just "Hello World". */ sds sdstrim(sds s, const char *cset) { - char *start, *end, *sp, *ep; + char *end, *sp, *ep; size_t len; - sp = start = s; + sp = s; ep = end = s+sdslen(s)-1; while(sp <= end && strchr(cset, *sp)) sp++; while(ep > sp && strchr(cset, *ep)) ep--; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/ssl.c new/hiredis-3.2.1/vendor/hiredis/ssl.c --- old/hiredis-2.3.2/vendor/hiredis/ssl.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/ssl.c 2025-05-23 12:50:14.000000000 +0200 @@ -34,6 +34,9 @@ #include "async.h" #include "net.h" +#include <openssl/ssl.h> +#include <openssl/err.h> + #include <assert.h> #include <errno.h> #include <string.h> @@ -52,9 +55,6 @@ #include <pthread.h> #endif -#include <openssl/ssl.h> -#include <openssl/err.h> - #include "win32.h" #include "async_private.h" #include "hiredis_ssl.h" @@ -167,8 +167,8 @@ int redisInitOpenSSL(void) { - SSL_library_init(); #ifdef HIREDIS_USE_CRYPTO_LOCKS + SSL_library_init(); initOpensslLocks(); #endif @@ -364,7 +364,6 @@ return REDIS_ERR; } - c->funcs = &redisContextSSLFuncs; rssl->ssl = ssl; SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); @@ -372,15 +371,19 @@ SSL_set_connect_state(rssl->ssl); ERR_clear_error(); + int rv = SSL_connect(rssl->ssl); if (rv == 1) { + c->funcs = &redisContextSSLFuncs; c->privctx = rssl; return REDIS_OK; } rv = SSL_get_error(rssl->ssl, rv); if (((c->flags & REDIS_BLOCK) == 0) && - (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) { + (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) + { + c->funcs = &redisContextSSLFuncs; c->privctx = rssl; return REDIS_OK; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-2.3.2/vendor/hiredis/test.c new/hiredis-3.2.1/vendor/hiredis/test.c --- old/hiredis-2.3.2/vendor/hiredis/test.c 2023-12-17 13:26:31.000000000 +0100 +++ new/hiredis-3.2.1/vendor/hiredis/test.c 2025-05-23 12:50:14.000000000 +0200 @@ -104,6 +104,13 @@ #define assert(e) (void)(e) #endif +#define redisTestPanic(msg) \ + do { \ + fprintf(stderr, "PANIC: %s (In function \"%s\", file \"%s\", line %d)\n", \ + msg, __func__, __FILE__, __LINE__); \ + exit(1); \ + } while (1) + /* Helper to extract Redis version information. Aborts on any failure. */ #define REDIS_VERSION_FIELD "redis_version:" void get_redis_version(redisContext *c, int *majorptr, int *minorptr) { @@ -149,7 +156,7 @@ assert(reply != NULL); freeReplyObject(reply); - /* Make sure the DB is emtpy */ + /* Make sure the DB is empty */ reply = redisCommand(c,"DBSIZE"); assert(reply != NULL); if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { @@ -232,7 +239,7 @@ c = redisConnectFd(fd); } } else { - assert(NULL); + redisTestPanic("Unknown connection type!"); } if (c == NULL) { @@ -424,6 +431,24 @@ redisFree(c); } +static void test_unix_keepalive(struct config cfg) { + redisContext *c; + redisReply *r; + + c = do_connect(cfg); + + test("Setting TCP_KEEPALIVE on a unix socket returns an error: "); + test_cond(redisEnableKeepAlive(c) == REDIS_ERR && c->err == 0); + + test("Setting TCP_KEEPALIVE on a unix socket doesn't break the connection: "); + r = redisCommand(c, "PING"); + test_cond(r != NULL && r->type == REDIS_REPLY_STATUS && r->len == 4 && + !memcmp(r->str, "PONG", 4)); + freeReplyObject(r); + + redisFree(c); +} + static void test_reply_reader(void) { redisReader *reader; void *reply, *root; @@ -770,6 +795,26 @@ freeReplyObject(reply); redisReaderFree(reader); + test("Can parse RESP3 attribute: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "|2\r\n+foo\r\n:123\r\n+bar\r\n#t\r\n",26); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_ATTR && + ((redisReply*)reply)->elements == 4 && + ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STATUS && + ((redisReply*)reply)->element[0]->len == 3 && + !strcmp(((redisReply*)reply)->element[0]->str,"foo") && + ((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->element[1]->integer == 123 && + ((redisReply*)reply)->element[2]->type == REDIS_REPLY_STATUS && + ((redisReply*)reply)->element[2]->len == 3 && + !strcmp(((redisReply*)reply)->element[2]->str,"bar") && + ((redisReply*)reply)->element[3]->type == REDIS_REPLY_BOOL && + ((redisReply*)reply)->element[3]->integer); + freeReplyObject(reply); + redisReaderFree(reader); + test("Can parse RESP3 set: "); reader = redisReaderCreate(); redisReaderFeed(reader, "~5\r\n+orange\r\n$5\r\napple\r\n#f\r\n:100\r\n:999\r\n",40); @@ -1231,15 +1276,13 @@ redisContext *c; redisReply *reply; ssize_t s; - const char *sleep_cmd = "DEBUG SLEEP 3\r\n"; - struct timeval tv; + const char *sleep_cmd = "DEBUG SLEEP 1\r\n"; + struct timeval tv = {.tv_sec = 0, .tv_usec = 10000}; c = do_connect(config); test("Successfully completes a command when the timeout is not exceeded: "); reply = redisCommand(c,"SET foo fast"); freeReplyObject(reply); - tv.tv_sec = 0; - tv.tv_usec = 10000; redisSetTimeout(c, tv); reply = redisCommand(c, "GET foo"); test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); @@ -1257,8 +1300,6 @@ sdsfree(c->obuf); c->obuf = sdsempty(); - tv.tv_sec = 0; - tv.tv_usec = 10000; redisSetTimeout(c, tv); reply = redisCommand(c, "GET foo"); #ifndef _WIN32 @@ -1271,7 +1312,7 @@ freeReplyObject(reply); // wait for the DEBUG SLEEP to complete so that Redis server is unblocked for the following tests - millisleep(3000); + millisleep(1100); } else { test_skipped(); } @@ -1340,7 +1381,7 @@ } static void test_invalid_timeout_errors(struct config config) { - redisContext *c; + redisContext *c = NULL; test("Set error when an invalid timeout usec value is used during connect: "); @@ -1352,10 +1393,10 @@ } else if(config.type == CONN_UNIX) { c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout); } else { - assert(NULL); + redisTestPanic("Unknown connection type!"); } - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); + test_cond(c != NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); redisFree(c); test("Set error when an invalid timeout sec value is used during connect: "); @@ -1368,10 +1409,10 @@ } else if(config.type == CONN_UNIX) { c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout); } else { - assert(NULL); + redisTestPanic("Unknown connection type!"); } - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); + test_cond(c != NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); redisFree(c); } @@ -2253,7 +2294,7 @@ */ test("Ping/Pong from onConnected callback (Issue #931): "); c = do_aconnect(config, ASTEST_ISSUE_931_PING); - /* connect callback issues ping, reponse callback destroys context */ + /* connect callback issues ping, response callback destroys context */ while(astest.ac) redisPollTick(c, 0.1); assert(astest.connected == 0); @@ -2356,6 +2397,7 @@ test_blocking_connection_timeouts(cfg); test_blocking_io_errors(cfg); test_invalid_timeout_errors(cfg); + test_unix_keepalive(cfg); if (throughput) test_throughput(cfg); } else { test_skipped();
