Skip to content

Commit 01add3c

Browse files
authored
Implement garbage collection support in Reader (#162) (#163)
1 parent 8adb1b3 commit 01add3c

File tree

3 files changed

+32
-5
lines changed

3 files changed

+32
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
* Implement pack_command that serializes redis-py command to the RESP bytes object.
2+
* Implement garbage collection support in Reader (#162)
23

34
### 2.1.1 (2023-10-01)
45

src/reader.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <assert.h>
44

55
static void Reader_dealloc(hiredis_ReaderObject *self);
6+
static int Reader_traverse(hiredis_ReaderObject *self, visitproc visit, void *arg);
67
static int Reader_init(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds);
78
static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
89
static PyObject *Reader_feed(hiredis_ReaderObject *self, PyObject *args);
@@ -44,9 +45,9 @@ PyTypeObject hiredis_ReaderType = {
4445
0, /*tp_getattro*/
4546
0, /*tp_setattro*/
4647
0, /*tp_as_buffer*/
47-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
48+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
4849
"Hiredis protocol reader", /*tp_doc */
49-
0, /*tp_traverse */
50+
(traverseproc)Reader_traverse,/*tp_traverse */
5051
0, /*tp_clear */
5152
0, /*tp_richcompare */
5253
0, /*tp_weaklistoffset */
@@ -209,16 +210,24 @@ redisReplyObjectFunctions hiredis_ObjectFunctions = {
209210
};
210211

211212
static void Reader_dealloc(hiredis_ReaderObject *self) {
213+
PyObject_GC_UnTrack(self);
212214
// we don't need to free self->encoding as the buffer is managed by Python
213215
// https://docs.python.org/3/c-api/arg.html#strings-and-buffers
214216
redisReaderFree(self->reader);
215-
Py_XDECREF(self->protocolErrorClass);
216-
Py_XDECREF(self->replyErrorClass);
217-
Py_XDECREF(self->notEnoughDataObject);
217+
Py_CLEAR(self->protocolErrorClass);
218+
Py_CLEAR(self->replyErrorClass);
219+
Py_CLEAR(self->notEnoughDataObject);
218220

219221
((PyObject *)self)->ob_type->tp_free((PyObject*)self);
220222
}
221223

224+
static int Reader_traverse(hiredis_ReaderObject *self, visitproc visit, void *arg) {
225+
Py_VISIT(self->protocolErrorClass);
226+
Py_VISIT(self->replyErrorClass);
227+
Py_VISIT(self->notEnoughDataObject);
228+
return 0;
229+
}
230+
222231
static int _Reader_set_exception(PyObject **target, PyObject *value) {
223232
int callable;
224233
callable = PyCallable_Check(value);

tests/test_gc.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import gc
2+
3+
import hiredis
4+
5+
6+
def test_reader_gc():
7+
class A:
8+
def __init__(self):
9+
self.reader = hiredis.Reader(replyError=self.reply_error)
10+
11+
def reply_error(self, error):
12+
return Exception()
13+
14+
A()
15+
gc.collect()
16+
17+
assert not any(isinstance(o, A) for o in gc.get_objects()), "Referent was not collected"

0 commit comments

Comments
 (0)