Существует ли идиоматический c способ проверки расширения python C, чтобы вы могли с высокой степенью уверенности в правильности подсчета ссылок и отсутствии утечек памяти?
В настоящее время я просто раскомментирую некоторые операторы printf
в своих функциях деаллокатора, а затем вручную печатаю различные ситуации, чтобы доказать себе, что мои Py_INCREF
и Py_DECREF
верны - что ссылка рассчитывает на объекты уменьшились до 0, и, как и ожидалось, вызывался коллокатор. Я ищу способ автоматизировать это в регрессионных тестах.
Например, скажем, у вас есть тип C с именем Foo
с оберткой Python с именем PyFoo
. A Foo
может иметь много детей; каждый ребенок может принадлежать только одному родителю. Когда счетчик дочерних ссылок выходит из области видимости, он не должен освобождать связанный тип C - он должен просто уменьшать родительский тип. Когда счетчик ссылок родителя становится равным 0, он должен отвечать за освобождение связанного C типа
typedef struct {
PyObject_HEAD
Foo *wrapped_foo;
PyObject *parent;
} PyFoo;
/*
* Add a child to a parent, PY_INCREF the parent
* /
static void PyFoo_push(PyFoo *self, PyObject *args, PyObject *kwargs)
{
int field = 0;
PyFoo *child = NULL;
if (!PyArg_ParseTuple(args, "iO!", &field, &FooType, &child)) {
return NULL;
}
Foo_push(self->foo, field, child->foo);
child->parent = self;
Py_INCREF(self);
}
static void PyFoo_dealloc(PyFoo *self)
{
if (self->parent) {
printf("I'm a child, decreffing my parent\n");
Py_XDECREF(self->parent);
} else {
printf("I'm a parent, freeing my wrapped foo\n");
Foo_destroy(self->foo);
}
}
Теперь в Python я напишу что-то вроде следующего, чтобы проверить его работу:
>>> parent = Foo()
>>> child = Foo()
>>> parent['ABC'] = child
>>> del child
I'm a child, decreffing my parent
>>> del parent
I'm a parent, freeing my wrapped foo
>>> parent = Foo()
>>> child = Foo()
>>> parent['ABC'] = child
>>> del parent
>>> del child
I'm a child, decreffing my parent
I'm a parent, freeing my wrapped foo
Я также предоставил функцию для возврата значения Py_REFCNT объекта PyFoo обратно в Python. Я могу написать несколько тестов с этим, но это не так здорово, потому что я действительно не хочу показывать это как частную функцию в моей библиотеке, и это также не учитывает, когда ссылочная учетная запись падает до 0.