| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | #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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 13:43:09 +01:00
										 |  |  |     fp = Py_fopen(filename, "w+"); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 13:43:09 +01:00
										 |  |  |     fp = Py_fopen(filename, "w+"); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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, "<refcnt %zd at %p>", | 
					
						
							|  |  |  |                   Py_REFCNT(test_string), (void *)test_string); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 13:43:09 +01:00
										 |  |  |     fp = Py_fopen(filename, "w+"); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (PyObject_Print(test_string, fp, 0) < 0){ | 
					
						
							|  |  |  |         fclose(fp); | 
					
						
							| 
									
										
										
										
											2024-04-01 17:01:22 +02:00
										 |  |  |         Py_SET_REFCNT(test_string, 1); | 
					
						
							|  |  |  |         Py_DECREF(test_string); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  |         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
 | 
					
						
							| 
									
										
										
										
											2025-01-06 13:43:09 +01:00
										 |  |  |     fp = Py_fopen(filename, "r"); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (PyObject_Print(test_string, fp, 0) < 0) { | 
					
						
							|  |  |  |         fclose(fp); | 
					
						
							| 
									
										
										
										
											2024-04-01 17:01:22 +02:00
										 |  |  |         Py_DECREF(test_string); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fclose(fp); | 
					
						
							| 
									
										
										
										
											2024-04-01 17:01:22 +02:00
										 |  |  |     Py_DECREF(test_string); | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 09:57:23 -04:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | pyobject_clear_weakrefs_no_callbacks(PyObject *self, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyUnstable_Object_ClearWeakRefsNoCallbacks(obj); | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 08:27:16 -05:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int result = PyUnstable_Object_EnableDeferredRefcount(obj); | 
					
						
							|  |  |  |     return PyLong_FromLong(result); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 14:32:27 -05:00
										 |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-01-27 08:36:33 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  | 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}, | 
					
						
							| 
									
										
										
										
											2024-06-18 09:57:23 -04:00
										 |  |  |     {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, | 
					
						
							| 
									
										
										
										
											2024-11-13 08:27:16 -05:00
										 |  |  |     {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, | 
					
						
							| 
									
										
										
										
											2025-01-28 14:32:27 -05:00
										 |  |  |     {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, | 
					
						
							| 
									
										
										
										
											2024-04-01 13:52:25 +01:00
										 |  |  |     {NULL}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyTestCapi_Init_Object(PyObject *m) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (PyModule_AddFunctions(m, test_methods) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } |