#include "parts.h" #include "util.h" static PyObject * call_pyobject_print(PyObject *self, PyObject * args) { PyObject *object; PyObject *filename; PyObject *print_raw; FILE *fp; int flags = 0; if (!PyArg_UnpackTuple(args, "call_pyobject_print", 3, 3, &object, &filename, &print_raw)) { return NULL; } fp = Py_fopen(filename, "w+"); if (Py_IsTrue(print_raw)) { flags = Py_PRINT_RAW; } if (PyObject_Print(object, fp, flags) < 0) { fclose(fp); return NULL; } fclose(fp); Py_RETURN_NONE; } static PyObject * pyobject_print_null(PyObject *self, PyObject *args) { PyObject *filename; FILE *fp; if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { return NULL; } fp = Py_fopen(filename, "w+"); if (PyObject_Print(NULL, fp, 0) < 0) { fclose(fp); return NULL; } fclose(fp); Py_RETURN_NONE; } static PyObject * pyobject_print_noref_object(PyObject *self, PyObject *args) { PyObject *test_string; PyObject *filename; FILE *fp; char correct_string[100]; test_string = PyUnicode_FromString("Spam spam spam"); Py_SET_REFCNT(test_string, 0); PyOS_snprintf(correct_string, 100, "", Py_REFCNT(test_string), (void *)test_string); if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { return NULL; } fp = Py_fopen(filename, "w+"); if (PyObject_Print(test_string, fp, 0) < 0){ fclose(fp); Py_SET_REFCNT(test_string, 1); Py_DECREF(test_string); return NULL; } fclose(fp); Py_SET_REFCNT(test_string, 1); Py_DECREF(test_string); return PyUnicode_FromString(correct_string); } static PyObject * pyobject_print_os_error(PyObject *self, PyObject *args) { PyObject *test_string; PyObject *filename; FILE *fp; test_string = PyUnicode_FromString("Spam spam spam"); if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { return NULL; } // open file in read mode to induce OSError fp = Py_fopen(filename, "r"); if (PyObject_Print(test_string, fp, 0) < 0) { fclose(fp); Py_DECREF(test_string); return NULL; } fclose(fp); Py_DECREF(test_string); Py_RETURN_NONE; } static PyObject * pyobject_clear_weakrefs_no_callbacks(PyObject *self, PyObject *obj) { PyUnstable_Object_ClearWeakRefsNoCallbacks(obj); Py_RETURN_NONE; } static PyObject * pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) { int result = PyUnstable_Object_EnableDeferredRefcount(obj); return PyLong_FromLong(result); } static int MyObject_dealloc_called = 0; static void MyObject_dealloc(PyObject *op) { // PyUnstable_TryIncRef should return 0 if object is being deallocated assert(Py_REFCNT(op) == 0); assert(!PyUnstable_TryIncRef(op)); assert(Py_REFCNT(op) == 0); MyObject_dealloc_called++; Py_TYPE(op)->tp_free(op); } static PyTypeObject MyType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "MyType", .tp_basicsize = sizeof(PyObject), .tp_dealloc = MyObject_dealloc, }; static PyObject * test_py_try_inc_ref(PyObject *self, PyObject *unused) { if (PyType_Ready(&MyType) < 0) { return NULL; } MyObject_dealloc_called = 0; PyObject *op = PyObject_New(PyObject, &MyType); if (op == NULL) { return NULL; } PyUnstable_EnableTryIncRef(op); #ifdef Py_GIL_DISABLED // PyUnstable_EnableTryIncRef sets the shared flags to // `_Py_REF_MAYBE_WEAKREF` if the flags are currently zero to ensure that // the shared reference count is merged on deallocation. assert((op->ob_ref_shared & _Py_REF_SHARED_FLAG_MASK) >= _Py_REF_MAYBE_WEAKREF); #endif if (!PyUnstable_TryIncRef(op)) { PyErr_SetString(PyExc_AssertionError, "PyUnstable_TryIncRef failed"); Py_DECREF(op); return NULL; } Py_DECREF(op); // undo try-incref Py_DECREF(op); // dealloc assert(MyObject_dealloc_called == 1); Py_RETURN_NONE; } static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, {"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS}, {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, {NULL}, }; int _PyTestCapi_Init_Object(PyObject *m) { if (PyModule_AddFunctions(m, test_methods) < 0) { return -1; } return 0; }