Skip to content

Commit bef209c

Browse files
author
Mikhail Koviazin
authored
Merge pull request #16 from valkey-io/mkmkme/fix-return-value
reader: partly restore the previous behaviour
2 parents 2578712 + a9c45d6 commit bef209c

File tree

4 files changed

+72
-125
lines changed

4 files changed

+72
-125
lines changed

libvalkey/libvalkey.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Reader:
2121
encoding: Optional[str] = ...,
2222
errors: Optional[str] = ...,
2323
notEnoughData: Any = ...,
24-
listOnly: bool = ...,
24+
convertSetsToLists: bool = ...,
2525
) -> None: ...
2626

2727
def feed(

src/reader.c

Lines changed: 54 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ static PyObject *Reader_getmaxbuf(libvalkey_ReaderObject *self);
1414
static PyObject *Reader_len(libvalkey_ReaderObject *self);
1515
static PyObject *Reader_has_data(libvalkey_ReaderObject *self);
1616
static PyObject *Reader_set_encoding(libvalkey_ReaderObject *self, PyObject *args, PyObject *kwds);
17-
static PyObject *Reader_listOnly(PyObject *self, void *closure);
17+
static PyObject *Reader_convertSetsToLists(PyObject *self, void *closure);
1818

1919
static PyMethodDef libvalkey_ReaderMethods[] = {
2020
{"feed", (PyCFunction)Reader_feed, METH_VARARGS, NULL },
@@ -28,7 +28,7 @@ static PyMethodDef libvalkey_ReaderMethods[] = {
2828
};
2929

3030
static PyGetSetDef libvalkey_ReaderGetSet[] = {
31-
{"listOnly", (getter)Reader_listOnly, NULL, NULL, NULL},
31+
{"convertSetsToLists", (getter)Reader_convertSetsToLists, NULL, NULL, NULL},
3232
{NULL} /* Sentinel */
3333
};
3434

@@ -73,88 +73,46 @@ PyTypeObject libvalkey_ReaderType = {
7373
Reader_new, /*tp_new */
7474
};
7575

76-
static int tryParentize_impl(const valkeyReadTask *task, PyObject *obj,
77-
PyObject *parent, libvalkey_ReaderObject *self) {
78-
switch (task->parent->type) {
79-
case VALKEY_REPLY_MAP:
80-
if (task->idx % 2 == 0) {
81-
/* Save the object as a key. */
82-
self->pendingObject = obj;
83-
} else {
84-
if (self->pendingObject == NULL) {
85-
Py_DECREF(obj);
86-
return -1;
87-
}
88-
if (PyDict_SetItem(parent, self->pendingObject, obj) < 0) {
89-
Py_DECREF(obj);
90-
Py_DECREF(self->pendingObject);
76+
static void *tryParentize(const valkeyReadTask *task, PyObject *obj) {
77+
libvalkey_ReaderObject *self = (libvalkey_ReaderObject*)task->privdata;
78+
if (task && task->parent) {
79+
PyObject *parent = (PyObject*)task->parent->obj;
80+
switch (task->parent->type) {
81+
case VALKEY_REPLY_MAP:
82+
if (task->idx % 2 == 0) {
83+
/* Save the object as a key. */
84+
self->pendingObject = obj;
85+
} else {
86+
if (self->pendingObject == NULL) {
87+
Py_DECREF(obj);
88+
return NULL;
89+
}
90+
if (PyDict_SetItem(parent, self->pendingObject, obj) < 0) {
91+
Py_DECREF(obj);
92+
Py_DECREF(self->pendingObject);
93+
self->pendingObject = NULL;
94+
return NULL;
95+
}
9196
self->pendingObject = NULL;
92-
return -1;
9397
}
94-
self->pendingObject = NULL;
95-
}
96-
break;
97-
case VALKEY_REPLY_SET:
98-
assert(PyAnySet_CheckExact(parent));
99-
if (PySet_Add(parent, obj) < 0) {
100-
Py_DECREF(obj);
101-
return -1;
102-
}
103-
break;
104-
default:
105-
assert(PyList_CheckExact(parent));
106-
if (PyList_SetItem(parent, task->idx, obj) < 0) {
107-
Py_DECREF(obj);
108-
return -1;
109-
}
110-
}
111-
return 0;
112-
}
113-
114-
static int tryParentize_ListOnly(const valkeyReadTask *task, PyObject *obj,
115-
PyObject *parent, libvalkey_ReaderObject *self) {
116-
switch (task->parent->type) {
117-
case VALKEY_REPLY_MAP:
118-
if (task->idx % 2 == 0) {
119-
/* Set a temporary item to save the object as a key. */
120-
self->pendingObject = PyTuple_New(2);
121-
if (self->pendingObject == NULL) {
122-
Py_DECREF(obj);
123-
return -1;
98+
break;
99+
case VALKEY_REPLY_SET:
100+
if (!self->convertSetsToLists) {
101+
assert(PyAnySet_CheckExact(parent));
102+
if (PySet_Add(parent, obj) < 0) {
103+
Py_DECREF(obj);
104+
return NULL;
105+
}
106+
break;
124107
}
125-
PyTuple_SET_ITEM(self->pendingObject, 0, obj);
126-
} else {
127-
if (self->pendingObject == NULL) {
108+
/* fallthrough */
109+
default:
110+
assert(PyList_CheckExact(parent));
111+
if (PyList_SetItem(parent, task->idx, obj) < 0) {
128112
Py_DECREF(obj);
129-
return -1;
113+
return NULL;
130114
}
131-
PyTuple_SET_ITEM(self->pendingObject, 1, obj);
132-
int res = PyList_Append(parent, self->pendingObject);
133-
Py_DECREF(self->pendingObject);
134-
self->pendingObject = NULL;
135-
if (res < 0)
136-
return -1;
137-
}
138-
break;
139-
default:
140-
assert(PyList_CheckExact(parent));
141-
if (PyList_SetItem(parent, task->idx, obj) < 0) {
142-
Py_DECREF(obj);
143-
return -1;
144-
}
145-
}
146-
return 0;
147-
}
148-
149-
static void *tryParentize(const valkeyReadTask *task, PyObject *obj) {
150-
libvalkey_ReaderObject *self = (libvalkey_ReaderObject*)task->privdata;
151-
if (task && task->parent) {
152-
PyObject *parent = (PyObject*)task->parent->obj;
153-
int res = self->listOnly
154-
? tryParentize_ListOnly(task, obj, parent, self)
155-
: tryParentize_impl(task, obj, parent, self);
156-
if (res < 0)
157-
return NULL;
115+
}
158116
}
159117
return obj;
160118
}
@@ -224,22 +182,18 @@ static void *createStringObject(const valkeyReadTask *task, char *str, size_t le
224182
static void *createArrayObject(const valkeyReadTask *task, size_t elements) {
225183
libvalkey_ReaderObject *self = (libvalkey_ReaderObject*)task->privdata;
226184
PyObject *obj;
227-
if (self->listOnly) {
228-
/* For map, don't preallocate listand use append later. */
229-
if (task->type == VALKEY_REPLY_MAP)
230-
elements = 0;
231-
obj = PyList_New(elements);
232-
} else {
233-
switch (task->type) {
234-
case VALKEY_REPLY_MAP:
235-
obj = PyDict_New();
236-
break;
237-
case VALKEY_REPLY_SET:
185+
switch (task->type) {
186+
case VALKEY_REPLY_MAP:
187+
obj = PyDict_New();
188+
break;
189+
case VALKEY_REPLY_SET:
190+
if (!self->convertSetsToLists) {
238191
obj = PySet_New(NULL);
239192
break;
240-
default:
241-
obj = PyList_New(elements);
242-
}
193+
}
194+
/* fallthrough */
195+
default:
196+
obj = PyList_New(elements);
243197
}
244198
return tryParentize(task, obj);
245199
}
@@ -357,18 +311,18 @@ static int Reader_init(libvalkey_ReaderObject *self, PyObject *args, PyObject *k
357311
"encoding",
358312
"errors",
359313
"notEnoughData",
360-
"listOnly",
314+
"convertSetsToLists",
361315
NULL,
362316
};
363317
PyObject *protocolErrorClass = NULL;
364318
PyObject *replyErrorClass = NULL;
365319
PyObject *notEnoughData = NULL;
366320
char *encoding = NULL;
367321
char *errors = NULL;
368-
int listOnly = 0;
322+
int convertSetsToLists = 0;
369323

370324
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOzzOp", kwlist,
371-
&protocolErrorClass, &replyErrorClass, &encoding, &errors, &notEnoughData, &listOnly))
325+
&protocolErrorClass, &replyErrorClass, &encoding, &errors, &notEnoughData, &convertSetsToLists))
372326
return -1;
373327

374328
if (protocolErrorClass)
@@ -386,7 +340,7 @@ static int Reader_init(libvalkey_ReaderObject *self, PyObject *args, PyObject *k
386340
Py_INCREF(self->notEnoughDataObject);
387341
}
388342

389-
self->listOnly = listOnly;
343+
self->convertSetsToLists = convertSetsToLists;
390344

391345
return _Reader_set_encoding(self, encoding, errors);
392346
}
@@ -406,7 +360,7 @@ static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
406360
self->protocolErrorClass = LIBVALKEY_STATE->VkErr_ProtocolError;
407361
self->replyErrorClass = LIBVALKEY_STATE->VkErr_ReplyError;
408362
self->pendingObject = NULL;
409-
self->listOnly = 0;
363+
self->convertSetsToLists = 0;
410364
Py_INCREF(self->protocolErrorClass);
411365
Py_INCREF(self->replyErrorClass);
412366
Py_INCREF(self->notEnoughDataObject);
@@ -549,9 +503,9 @@ static PyObject *Reader_set_encoding(libvalkey_ReaderObject *self, PyObject *arg
549503

550504
}
551505

552-
static PyObject *Reader_listOnly(PyObject *obj, void *closure) {
506+
static PyObject *Reader_convertSetsToLists(PyObject *obj, void *closure) {
553507
libvalkey_ReaderObject *self = (libvalkey_ReaderObject*)obj;
554-
PyObject *result = PyBool_FromLong(self->listOnly);
508+
PyObject *result = PyBool_FromLong(self->convertSetsToLists);
555509
Py_INCREF(result);
556510
return result;
557511
}

src/reader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ typedef struct {
1313
PyObject *protocolErrorClass;
1414
PyObject *replyErrorClass;
1515
PyObject *notEnoughDataObject;
16-
int listOnly;
16+
int convertSetsToLists;
1717

1818
PyObject *pendingObject;
1919

tests/test_reader.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
@pytest.fixture(params=[True, False])
55
def reader(request):
6-
return libvalkey.Reader(listOnly=request.param)
6+
return libvalkey.Reader(convertSetsToLists=request.param)
77

88
# def reply():
99
# return reader.gets()
@@ -138,55 +138,48 @@ def test_set(reader):
138138
reader.feed(b"~3\r\n+tangerine\r\n_\r\n,10.5\r\n")
139139
expected = (
140140
[b"tangerine", None, 10.5]
141-
if reader.listOnly
141+
if reader.convertSetsToLists
142142
else {b"tangerine", None, 10.5}
143143
)
144144
assert expected == reader.gets()
145145

146146
def test_dict(reader):
147147
reader.feed(b"%2\r\n+radius\r\n,4.5\r\n+diameter\r\n:9\r\n")
148-
expected = (
149-
[(b"radius", 4.5), (b"diameter", 9)]
150-
if reader.listOnly
151-
else {b"radius": 4.5, b"diameter": 9}
152-
)
153-
assert expected == reader.gets()
148+
assert {b"radius": 4.5, b"diameter": 9} == reader.gets()
154149

155150
def test_set_with_nested_dict(reader):
156151
reader.feed(b"~2\r\n+tangerine\r\n%1\r\n+a\r\n:1\r\n")
157-
if reader.listOnly:
158-
assert [b"tangerine", [(b"a", 1)]] == reader.gets()
152+
if reader.convertSetsToLists:
153+
assert [b"tangerine", {b"a": 1}] == reader.gets()
159154
else:
160155
with pytest.raises(TypeError):
161156
reader.gets()
162157

163158
def test_dict_with_nested_set(reader):
164159
reader.feed(b"%1\r\n+a\r\n~2\r\n:1\r\n:2\r\n")
165-
expected = (
166-
[(b"a", [1, 2])]
167-
if reader.listOnly
168-
else {b"a": {1, 2}}
169-
)
160+
expected: dict[bytes, set[int] | list[int]] = {b"a": {1, 2}}
161+
if reader.convertSetsToLists:
162+
expected[b"a"] = list(expected[b"a"])
170163
assert expected == reader.gets()
171164

172165
def test_map_inside_list(reader):
173166
reader.feed(b"*1\r\n%1\r\n+a\r\n:1\r\n")
174-
expected = (
175-
[[(b"a", 1)]]
176-
if reader.listOnly
177-
else [{b"a": 1}]
178-
)
179-
assert expected == reader.gets()
167+
assert [{b"a": 1}] == reader.gets()
180168

181169
def test_map_inside_set(reader):
182170
reader.feed(b"~1\r\n%1\r\n+a\r\n:1\r\n")
183-
if reader.listOnly:
184-
assert [[(b"a", 1)]] == reader.gets()
171+
if reader.convertSetsToLists:
172+
assert [{b"a": 1}] == reader.gets()
185173
else:
186174
# Map inside set is not allowed in Python
187175
with pytest.raises(TypeError):
188176
reader.gets()
189177

178+
def test_set_as_map_key(reader):
179+
reader.feed(b"%1\r\n~1\r\n:1\r\n:2\r\n")
180+
with pytest.raises(TypeError):
181+
reader.gets()
182+
190183
def test_vector(reader):
191184
reader.feed(b">4\r\n+pubsub\r\n+message\r\n+channel\r\n+message\r\n")
192185
assert [b"pubsub", b"message", b"channel", b"message"] == reader.gets()

0 commit comments

Comments
 (0)