PATCH: cyclic GC traversal for psycopg types
"James Henstridge" <[email protected]>
| Newsgroups | gmane.comp.python.db.psycopg.devel |
|---|---|
| Message-ID | <[email protected]> |
Attached is a patch to add cyclic GC traversal support to the extension types that Psycopg defines. I have not implemented the tp_clear method for any of the types so there can be cases where this won't result in cycles being broken, but it should at least make them visible to the GC (and related debugging tools). If we find cases where objects get involved in unbreakable cycles, we can look at adding tp_clear implementations (which probably also involves more extensive checking of the reference handling in said classes). James. _______________________________________________ Psycopg mailing list Psycopg-IAPFreCvJWPBWskQ1e/[email protected] http://lists.initd.org/mailman/listinfo/psycopg
psycopg-garbage-collection.patch
(text/x-diff, 31.3 KB)
# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: [email protected] # target_branch: bzr+ssh://initd.org/bazaar/psycopg/psycopg2/trunk/ # testament_sha1: 1cade3b30d2e65fa05459031cc13634ead57fb66 # timestamp: 2008-07-21 17:04:07 +0800 # base_revision_id: [email protected] # # Begin patch === modified file 'ChangeLog' --- ChangeLog 2008-05-19 08:14:17 +0000 +++ ChangeLog 2008-07-18 09:42:31 +0000 @@ -1,3 +1,46 @@ +2008-07-18 James Henstridge <[email protected]> + + * psycopg/adapter_qstring.c (qstring_traverse): add cyclic GC + traversal for quoted string adapters. + + * psycopg/adapter_pboolean.c (pboolean_traverse): add cyclic GC + traversal for boolean adapters. + + * psycopg/adapter_mxdatetime.c (mxdatetime_traverse): add cyclic + GC traversal for mxdatetime adapters. + + * psycopg/adapter_datetime.c (pydatetime_traverse): add cyclic GC + traversal for datetime adapters. + +2008-07-01 James Henstridge <[email protected]> + + * psycopg/adapter_binary.c (binary_traverse): add cyclic GC + traversal for binary adapters. + + * psycopg/adapter_asis.c (asis_traverse): add cyclic GC traversal + for AsIs adapters. + + * psycopg/adapter_list.c (list_traverse): add cyclic GC traversal + for list adapters. + +2008-05-28 James Henstridge <[email protected]> + + * psycopg/cursor_type.c (cursor_setup): incref before setting + attributes, to make things GC-safe. + + * psycopg/cursor_int.c (curs_reset): make clearing of description + and casts attributes GC-safe. + + * psycopg/typecast.c (typecast_traverse): implement cyclic GC + traversal for typecasters. + + * psycopg/connection_type.c: + * psycopg/cursor_type.c: add support for cyclic GC traversal (no + support for clearing). + + * psycopg/python.h: add definitions for Py_CLEAR() and Py_VISIT() + for compatibility with old versions of Python. + 2008-05-19 Federico Di Gregorio <[email protected]> * psycopg/cursor_type.c: fixed memory leak in .executemany(); on === modified file 'psycopg/adapter_asis.c' --- psycopg/adapter_asis.c 2008-01-13 16:05:59 +0000 +++ psycopg/adapter_asis.c 2008-07-21 05:41:54 +0000 @@ -96,8 +96,8 @@ self, ((PyObject *)self)->ob_refcnt ); + Py_INCREF(obj); self->wrapped = obj; - Py_INCREF(self->wrapped); Dprintf("asis_setup: good asis object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -106,12 +106,19 @@ return 0; } +static int +asis_traverse(asisObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->wrapped); + return 0; +} + static void asis_dealloc(PyObject* obj) { asisObject *self = (asisObject *)obj; - Py_XDECREF(self->wrapped); + Py_CLEAR(self->wrapped); Dprintf("asis_dealloc: deleted asis object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -141,7 +148,7 @@ static void asis_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -183,10 +190,10 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ asisType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + (traverseproc)asis_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_binary.c' --- psycopg/adapter_binary.c 2008-05-05 02:37:24 +0000 +++ psycopg/adapter_binary.c 2008-07-21 05:41:54 +0000 @@ -257,8 +257,8 @@ self->buffer = NULL; self->conn = NULL; + Py_INCREF(str); self->wrapped = str; - Py_INCREF(self->wrapped); Dprintf("binary_setup: good binary object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -266,14 +266,25 @@ return 0; } +static int +binary_traverse(PyObject *obj, visitproc visit, void *arg) +{ + binaryObject *self = (binaryObject *)obj; + + Py_VISIT(self->wrapped); + Py_VISIT(self->buffer); + Py_VISIT(self->conn); + return 0; +} + static void binary_dealloc(PyObject* obj) { binaryObject *self = (binaryObject *)obj; - Py_XDECREF(self->wrapped); - Py_XDECREF(self->buffer); - Py_XDECREF(self->conn); + Py_CLEAR(self->wrapped); + Py_CLEAR(self->buffer); + Py_CLEAR(self->conn); Dprintf("binary_dealloc: deleted binary object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -303,7 +314,7 @@ static void binary_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -341,11 +352,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ binaryType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + binary_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_datetime.c' --- psycopg/adapter_datetime.c 2008-05-05 02:37:24 +0000 +++ psycopg/adapter_datetime.c 2008-07-21 05:41:54 +0000 @@ -131,8 +131,8 @@ self, ((PyObject *)self)->ob_refcnt); self->type = type; + Py_INCREF(obj); self->wrapped = obj; - Py_INCREF(self->wrapped); Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -140,12 +140,21 @@ return 0; } +static int +pydatetime_traverse(PyObject *obj, visitproc visit, void *arg) +{ + pydatetimeObject *self = (pydatetimeObject *)obj; + + Py_VISIT(self->wrapped); + return 0; +} + static void pydatetime_dealloc(PyObject* obj) { pydatetimeObject *self = (pydatetimeObject *)obj; - Py_XDECREF(self->wrapped); + Py_CLEAR(self->wrapped); Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, " "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt); @@ -174,7 +183,7 @@ static void pydatetime_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -213,11 +222,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ pydatetimeType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + pydatetime_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_list.c' --- psycopg/adapter_list.c 2008-05-05 02:37:24 +0000 +++ psycopg/adapter_list.c 2008-07-21 05:41:54 +0000 @@ -108,9 +108,9 @@ reference to it; we'll need it during the recursive adapt() call (the encoding is here for a future expansion that will make .getquoted() work even without a connection to the backend. */ - Py_XDECREF(self->connection); + Py_CLEAR(self->connection); + Py_INCREF(conn); self->connection = (PyObject*)conn; - Py_INCREF(self->connection); Py_INCREF(Py_None); return Py_None; @@ -169,8 +169,8 @@ if (enc) self->encoding = strdup(enc); self->connection = NULL; + Py_INCREF(obj); self->wrapped = obj; - Py_INCREF(self->wrapped); Dprintf("list_setup: good list object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -179,13 +179,23 @@ return 0; } +static int +list_traverse(PyObject *obj, visitproc visit, void *arg) +{ + listObject *self = (listObject *)obj; + + Py_VISIT(self->wrapped); + Py_VISIT(self->connection); + return 0; +} + static void list_dealloc(PyObject* obj) { listObject *self = (listObject *)obj; - Py_XDECREF(self->wrapped); - Py_XDECREF(self->connection); + Py_CLEAR(self->wrapped); + Py_CLEAR(self->connection); if (self->encoding) free(self->encoding); Dprintf("list_dealloc: deleted list object at %p, " @@ -215,7 +225,7 @@ static void list_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -253,11 +263,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ listType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + list_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_mxdatetime.c' --- psycopg/adapter_mxdatetime.c 2008-05-05 02:37:24 +0000 +++ psycopg/adapter_mxdatetime.c 2008-07-21 05:41:54 +0000 @@ -152,8 +152,8 @@ ); self->type = type; + Py_INCREF(obj); self->wrapped = obj; - Py_INCREF(self->wrapped); Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -162,12 +162,21 @@ return 0; } +static int +mxdatetime_traverse(PyObject *obj, visitproc visit, void *arg) +{ + mxdatetimeObject *self = (mxdatetimeObject *)obj; + + Py_VISIT(self->wrapped); + return 0; +} + static void mxdatetime_dealloc(PyObject* obj) { mxdatetimeObject *self = (mxdatetimeObject *)obj; - Py_XDECREF(self->wrapped); + Py_CLEAR(self->wrapped); Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -198,7 +207,7 @@ static void mxdatetime_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -237,11 +246,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ mxdatetimeType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + mxdatetime_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_pboolean.c' --- psycopg/adapter_pboolean.c 2008-01-13 16:05:59 +0000 +++ psycopg/adapter_pboolean.c 2008-07-21 05:41:54 +0000 @@ -106,8 +106,8 @@ self, ((PyObject *)self)->ob_refcnt ); + Py_INCREF(obj); self->wrapped = obj; - Py_INCREF(self->wrapped); Dprintf("pboolean_setup: good pboolean object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -116,12 +116,21 @@ return 0; } +static int +pboolean_traverse(PyObject *obj, visitproc visit, void *arg) +{ + pbooleanObject *self = (pbooleanObject *)obj; + + Py_VISIT(self->wrapped); + return 0; +} + static void pboolean_dealloc(PyObject* obj) { pbooleanObject *self = (pbooleanObject *)obj; - Py_XDECREF(self->wrapped); + Py_CLEAR(self->wrapped); Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -151,7 +160,7 @@ static void pboolean_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -194,10 +203,10 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ pbooleanType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + pboolean_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/adapter_qstring.c' --- psycopg/adapter_qstring.c 2008-05-05 02:37:24 +0000 +++ psycopg/adapter_qstring.c 2008-07-21 05:41:54 +0000 @@ -216,10 +216,10 @@ Dprintf("qstring_prepare: set encoding to %s", conn->encoding); } - Py_XDECREF(self->conn); + Py_CLEAR(self->conn); if (conn) { + Py_INCREF(conn); self->conn = (PyObject*)conn; - Py_INCREF(self->conn); } Py_INCREF(Py_None); @@ -280,8 +280,8 @@ /* FIXME: remove this orrible strdup */ if (enc) self->encoding = strdup(enc); + Py_INCREF(str); self->wrapped = str; - Py_INCREF(self->wrapped); Dprintf("qstring_setup: good qstring object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -290,14 +290,25 @@ return 0; } +static int +qstring_traverse(PyObject *obj, visitproc visit, void *arg) +{ + qstringObject *self = (qstringObject *)obj; + + Py_VISIT(self->wrapped); + Py_VISIT(self->buffer); + Py_VISIT(self->conn); + return 0; +} + static void qstring_dealloc(PyObject* obj) { qstringObject *self = (qstringObject *)obj; - Py_XDECREF(self->wrapped); - Py_XDECREF(self->buffer); - Py_XDECREF(self->conn); + Py_CLEAR(self->wrapped); + Py_CLEAR(self->buffer); + Py_CLEAR(self->conn); if (self->encoding) free(self->encoding); @@ -330,7 +341,7 @@ static void qstring_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -369,11 +380,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ qstringType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + qstring_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/connection_type.c' --- psycopg/connection_type.c 2008-05-05 02:43:38 +0000 +++ psycopg/connection_type.c 2008-05-28 01:00:36 +0000 @@ -450,11 +450,11 @@ if (self->encoding) free(self->encoding); if (self->critical) free(self->critical); - Py_XDECREF(self->notice_list); - Py_XDECREF(self->notifies); - Py_XDECREF(self->async_cursor); - Py_XDECREF(self->string_types); - Py_XDECREF(self->binary_types); + Py_CLEAR(self->notice_list); + Py_CLEAR(self->notifies); + Py_CLEAR(self->async_cursor); + Py_CLEAR(self->string_types); + Py_CLEAR(self->binary_types); pthread_mutex_destroy(&(self->lock)); @@ -486,7 +486,7 @@ static void connection_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -497,6 +497,18 @@ self, self->dsn, self->closed); } +static int +connection_traverse(connectionObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->async_cursor); + Py_VISIT(self->notice_list); + Py_VISIT(self->notice_filter); + Py_VISIT(self->notifies); + Py_VISIT(self->string_types); + Py_VISIT(self->binary_types); + return 0; +} + /* object type */ @@ -530,10 +542,10 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ connectionType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + (traverseproc)connection_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/cursor_int.c' --- psycopg/cursor_int.c 2007-04-10 06:36:18 +0000 +++ psycopg/cursor_int.c 2008-05-28 09:45:37 +0000 @@ -34,15 +34,19 @@ void curs_reset(cursorObject *self) { + PyObject *tmp; + /* initialize some variables to default values */ self->notuples = 1; self->rowcount = -1; self->row = 0; - Py_XDECREF(self->description); + tmp = self->description; Py_INCREF(Py_None); self->description = Py_None; + Py_XDECREF(tmp); - Py_XDECREF(self->casts); + tmp = self->casts; self->casts = NULL; + Py_XDECREF(tmp); } === modified file 'psycopg/cursor_type.c' --- psycopg/cursor_type.c 2008-05-19 08:14:17 +0000 +++ psycopg/cursor_type.c 2008-07-21 05:41:54 +0000 @@ -1590,8 +1590,8 @@ "argument 1 must be subclass of psycopg2._psycopg.connection"); return 1; } */ + Py_INCREF(conn); self->conn = conn; - Py_INCREF((PyObject*)self->conn); self->closed = 0; self->mark = conn->mark; @@ -1607,6 +1607,7 @@ self->string_types = NULL; self->binary_types = NULL; + Py_INCREF(Py_None); self->description = Py_None; Py_INCREF(Py_None); self->pgstatus = Py_None; @@ -1614,11 +1615,10 @@ self->tuple_factory = Py_None; Py_INCREF(Py_None); self->query = Py_None; - Py_INCREF(Py_None); /* default tzinfo factory */ + Py_INCREF(pyPsycopgTzFixedOffsetTimezone); self->tzinfo_factory = pyPsycopgTzFixedOffsetTimezone; - Py_INCREF(self->tzinfo_factory); Dprintf("cursor_setup: good cursor object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -1634,15 +1634,15 @@ if (self->name) PyMem_Free(self->name); - Py_XDECREF((PyObject*)self->conn); - Py_XDECREF(self->casts); - Py_XDECREF(self->description); - Py_XDECREF(self->pgstatus); - Py_XDECREF(self->tuple_factory); - Py_XDECREF(self->tzinfo_factory); - Py_XDECREF(self->query); - Py_XDECREF(self->string_types); - Py_XDECREF(self->binary_types); + Py_CLEAR(self->conn); + Py_CLEAR(self->casts); + Py_CLEAR(self->description); + Py_CLEAR(self->pgstatus); + Py_CLEAR(self->tuple_factory); + Py_CLEAR(self->tzinfo_factory); + Py_CLEAR(self->query); + Py_CLEAR(self->string_types); + Py_CLEAR(self->binary_types); IFCLEARPGRES(self->pgres); @@ -1675,7 +1675,7 @@ static void cursor_del(PyObject* self) { - PyObject_Del(self); + PyObject_GC_Del(self); } static PyObject * @@ -1685,6 +1685,23 @@ "<cursor object at %p; closed: %d>", self, self->closed); } +static int +cursor_traverse(cursorObject *self, visitproc visit, void *arg) +{ + Py_VISIT((PyObject *)self->conn); + Py_VISIT(self->description); + Py_VISIT(self->pgstatus); + Py_VISIT(self->casts); + Py_VISIT(self->caster); + Py_VISIT(self->copyfile); + Py_VISIT(self->tuple_factory); + Py_VISIT(self->tzinfo_factory); + Py_VISIT(self->query); + Py_VISIT(self->string_types); + Py_VISIT(self->binary_types); + return 0; +} + /* object type */ @@ -1714,10 +1731,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | + Py_TPFLAGS_HAVE_GC, /*tp_flags*/ cursorType_doc, /*tp_doc*/ - 0, /*tp_traverse*/ + (traverseproc)cursor_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ === modified file 'psycopg/python.h' --- psycopg/python.h 2007-04-10 06:36:18 +0000 +++ psycopg/python.h 2008-05-28 01:00:36 +0000 @@ -41,4 +41,27 @@ #define freefunc destructor #endif +/* Py_VISIT and Py_CLEAR introduced in Python 2.4 */ +#ifndef Py_VISIT +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) +#endif + +#ifndef Py_CLEAR +#define Py_CLEAR(op) \ + do { \ + if (op) { \ + PyObject *tmp = (PyObject *)(op); \ + (op) = NULL; \ + Py_DECREF(tmp); \ + } \ + } while (0) +#endif + #endif /* !defined(PSYCOPG_PYTHON_H) */ === modified file 'psycopg/typecast.c' --- psycopg/typecast.c 2008-05-05 02:37:24 +0000 +++ psycopg/typecast.c 2008-05-28 09:22:40 +0000 @@ -382,11 +382,28 @@ { typecastObject *self = (typecastObject*)obj; - Py_XDECREF(self->values); - Py_XDECREF(self->name); - Py_XDECREF(self->pcast); - - PyObject_Del(self); + Py_CLEAR(self->values); + Py_CLEAR(self->name); + Py_CLEAR(self->pcast); + + obj->ob_type->tp_free(obj); +} + +static int +typecast_traverse(PyObject *obj, visitproc visit, void *arg) +{ + typecastObject *self = (typecastObject*)obj; + + Py_VISIT(self->values); + Py_VISIT(self->name); + Py_VISIT(self->pcast); + return 0; +} + +static void +typecast_del(void *self) +{ + PyObject_GC_Del(self); } static PyObject * @@ -427,10 +444,11 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_HAVE_RICHCOMPARE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE | + Py_TPFLAGS_HAVE_GC, /*tp_flags*/ "psycopg type-casting object", /*tp_doc*/ - 0, /*tp_traverse*/ + typecast_traverse, /*tp_traverse*/ 0, /*tp_clear*/ typecast_richcompare, /*tp_richcompare*/ @@ -454,7 +472,7 @@ 0, /*tp_init*/ 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_new*/ - 0, /*tp_free Low-level free-memory routine */ + typecast_del, /*tp_free Low-level free-memory routine */ 0, /*tp_is_gc For PyObject_IS_GC */ 0, /*tp_bases*/ 0, /*tp_mro method resolution order */ @@ -468,7 +486,7 @@ { typecastObject *obj; - obj = PyObject_NEW(typecastObject, &typecastType); + obj = PyObject_GC_New(typecastObject, &typecastType); if (obj == NULL) return NULL; Dprintf("typecast_new: new type at = %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, @@ -498,6 +516,8 @@ obj->pcast = cast; } + PyObject_GC_Track(obj); + Dprintf("typecast_new: typecast object created at %p", obj); return (PyObject *)obj; # Begin bundle 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