Skip to content

Commit dbf7057

Browse files
committed
Add PyWeakref_GetRef() function
1 parent 9669348 commit dbf7057

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

docs/api.rst

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Python 3.13
3131
3232
See `PyImport_AddModuleRef() documentation <https://docs.python.org/dev/c-api/import.html#c.PyImport_AddModuleRef>`__.
3333
34+
.. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
35+
36+
See `PyWeakref_GetRef() documentation <https://docs.python.org/dev/c-api/weakref.html#c.PyWeakref_GetRef>`__.
3437
3538
Python 3.12
3639
-----------

docs/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2023-06-21: Add ``PyWeakref_GetRef()`` function.
45
* 2023-06-20: Add ``PyImport_AddModuleRef()`` function.
56
* 2022-11-15: Add experimental operations to the ``upgrade_pythoncapi.py``
67
script: ``Py_NewRef``, ``Py_CLEAR`` and ``Py_SETREF``.

pythoncapi_compat.h

+28
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ PyCode_GetCellvars(PyCodeObject *code)
570570
# endif
571571
#endif
572572

573+
573574
// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1
574575
#if PY_VERSION_HEX < 0x030D00A0
575576
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
@@ -580,6 +581,33 @@ PyImport_AddModuleRef(const char *name)
580581
#endif
581582

582583

584+
// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1
585+
#if PY_VERSION_HEX < 0x030D0000
586+
PYCAPI_COMPAT_STATIC_INLINE(int)
587+
PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
588+
{
589+
PyObject *obj;
590+
if (ref != NULL && !PyWeakref_Check(ref)) {
591+
*pobj = NULL;
592+
PyErr_SetString(PyExc_TypeError, "expected a weakref");
593+
return -1;
594+
}
595+
obj = PyWeakref_GetObject(ref);
596+
if (obj == NULL) {
597+
// SystemError if ref is NULL
598+
*pobj = NULL;
599+
return -1;
600+
}
601+
if (obj == Py_None) {
602+
*pobj = NULL;
603+
return 0;
604+
}
605+
*pobj = Py_NewRef(obj);
606+
return 0;
607+
}
608+
#endif
609+
610+
583611
#ifdef __cplusplus
584612
}
585613
#endif

tests/test_pythoncapi_compat_cext.c

+73
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,78 @@ test_import(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
668668
}
669669

670670

671+
static void
672+
gc_collect(void)
673+
{
674+
#if defined(PYPY_VERSION)
675+
PyObject *mod = PyImport_ImportModule("gc");
676+
assert(mod != NULL);
677+
678+
PyObject *res = PyObject_CallMethod(mod, "collect", NULL);
679+
Py_DECREF(mod);
680+
assert(res != NULL);
681+
Py_DECREF(res);
682+
#else
683+
PyGC_Collect();
684+
#endif
685+
}
686+
687+
688+
static PyObject *
689+
test_weakref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
690+
{
691+
// Create a new heap type, create an instance of this type, and delete the
692+
// type. This object supports weak references.
693+
PyObject *new_type = PyObject_CallFunction((PyObject*)&PyType_Type,
694+
"s(){}", "TypeName");
695+
if (new_type == NULL) {
696+
return NULL;
697+
}
698+
PyObject *obj = PyObject_CallNoArgs(new_type);
699+
Py_DECREF(new_type);
700+
if (obj == NULL) {
701+
return NULL;
702+
}
703+
Py_ssize_t refcnt = Py_REFCNT(obj);
704+
705+
// create a weak reference
706+
PyObject *weakref = PyWeakref_NewRef(obj, NULL);
707+
if (weakref == NULL) {
708+
return NULL;
709+
}
710+
711+
// test PyWeakref_GetRef(), reference is alive
712+
PyObject *ref1;
713+
assert(PyWeakref_GetRef(weakref, &ref1) == 0);
714+
assert(ref1 == obj);
715+
assert(Py_REFCNT(obj) == (refcnt + 1));
716+
Py_DECREF(ref1);
717+
718+
// delete the referenced object
719+
assert(Py_REFCNT(obj) == 1);
720+
Py_DECREF(obj);
721+
gc_collect();
722+
723+
// test PyWeakref_GetRef(), reference is dead
724+
PyObject *ref2 = Py_True; // marker to check that value was set
725+
assert(PyWeakref_GetRef(weakref, &ref2) == 0);
726+
assert(ref2 == NULL);
727+
728+
// test PyWeakref_GetRef(), invalid type
729+
PyObject *invalid_weakref = Py_None;
730+
assert(!PyErr_Occurred());
731+
PyObject *ref3 = Py_True; // marker to check that value was set
732+
assert(PyWeakref_GetRef(invalid_weakref, &ref3) == -1);
733+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
734+
assert(ref3 == NULL);
735+
PyErr_Clear();
736+
737+
Py_DECREF(weakref);
738+
739+
Py_RETURN_NONE;
740+
}
741+
742+
671743
static struct PyMethodDef methods[] = {
672744
{"test_object", test_object, METH_NOARGS, _Py_NULL},
673745
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -687,6 +759,7 @@ static struct PyMethodDef methods[] = {
687759
#endif
688760
{"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL},
689761
{"test_import", test_import, METH_NOARGS, _Py_NULL},
762+
{"test_weakref", test_weakref, METH_NOARGS, _Py_NULL},
690763
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
691764
};
692765

0 commit comments

Comments
 (0)