Skip to content

Commit 9c44656

Browse files
authored
gh-105927: Add PyWeakref_GetRef() function (#105932)
Add tests on PyWeakref_NewRef(), PyWeakref_GetObject(), PyWeakref_GET_OBJECT() and PyWeakref_GetRef().
1 parent 4d140e5 commit 9c44656

File tree

11 files changed

+133
-8
lines changed

11 files changed

+133
-8
lines changed

Doc/c-api/weakref.rst

+19-8
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ simple reference object, and the second acts as a proxy for the original object
1111
as much as it can.
1212

1313

14-
.. c:function:: int PyWeakref_Check(ob)
14+
.. c:function:: int PyWeakref_Check(PyObject *ob)
1515
16-
Return true if *ob* is either a reference or proxy object. This function
16+
Return non-zero if *ob* is either a reference or proxy object. This function
1717
always succeeds.
1818
1919
20-
.. c:function:: int PyWeakref_CheckRef(ob)
20+
.. c:function:: int PyWeakref_CheckRef(PyObject *ob)
2121
22-
Return true if *ob* is a reference object. This function always succeeds.
22+
Return non-zero if *ob* is a reference object. This function always succeeds.
2323
2424
25-
.. c:function:: int PyWeakref_CheckProxy(ob)
25+
.. c:function:: int PyWeakref_CheckProxy(PyObject *ob)
2626
27-
Return true if *ob* is a proxy object. This function always succeeds.
27+
Return non-zero if *ob* is a proxy object. This function always succeeds.
2828
2929
3030
.. c:function:: PyObject* PyWeakref_NewRef(PyObject *ob, PyObject *callback)
@@ -51,10 +51,21 @@ as much as it can.
5151
``None``, or ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`.
5252
5353
54+
.. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
55+
56+
Get a :term:`strong reference` to the referenced object from a weak
57+
reference, *ref*, into *\*pobj*.
58+
Return 0 on success. Raise an exception and return -1 on error.
59+
60+
If the referent is no longer live, set *\*pobj* to ``NULL`` and return 0.
61+
62+
.. versionadded:: 3.13
63+
64+
5465
.. c:function:: PyObject* PyWeakref_GetObject(PyObject *ref)
5566
56-
Return the referenced object from a weak reference, *ref*. If the referent is
57-
no longer live, returns :const:`Py_None`.
67+
Return a :term:`borrowed reference` to the referenced object from a weak
68+
reference, *ref*. If the referent is no longer live, returns ``Py_None``.
5869
5970
.. note::
6071

Doc/data/refcounts.dat

+4
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,10 @@ PyWeakref_GET_OBJECT:PyObject*:ref:0:
28132813
PyWeakref_GetObject:PyObject*::0:
28142814
PyWeakref_GetObject:PyObject*:ref:0:
28152815

2816+
PyWeakref_GetRef:int:::
2817+
PyWeakref_GetRef:PyObject*:ref:0:
2818+
PyWeakref_GetRef:PyObject**:pobj:+1:
2819+
28162820
PyWeakref_NewProxy:PyObject*::+1:
28172821
PyWeakref_NewProxy:PyObject*:ob:0:
28182822
PyWeakref_NewProxy:PyObject*:callback:0:

Doc/data/stable_abi.dat

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.13.rst

+4
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ New Features
431431
of a :term:`borrowed reference`.
432432
(Contributed by Victor Stinner in :gh:`105922`.)
433433

434+
* Add :c:func:`PyWeakref_GetRef` function: similar to
435+
:c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or
436+
``NULL`` if the referent is no longer live.
437+
(Contributed by Victor Stinner in :gh:`105927`.)
434438

435439
Porting to Python 3.13
436440
----------------------

Include/weakrefobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ PyAPI_FUNC(PyObject *) PyWeakref_NewRef(PyObject *ob,
2828
PyAPI_FUNC(PyObject *) PyWeakref_NewProxy(PyObject *ob,
2929
PyObject *callback);
3030
PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref);
31+
PyAPI_FUNC(int) PyWeakref_GetRef(PyObject *ref, PyObject **pobj);
3132

3233

3334
#ifndef Py_LIMITED_API

Lib/test/test_stable_abi_ctypes.py

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PyWeakref_GetRef` function: similar to
2+
:c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or
3+
``NULL`` if the referent is no longer live. Patch by Victor Stinner.

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -2430,3 +2430,5 @@
24302430
added = '3.12'
24312431
[function.PyImport_AddModuleRef]
24322432
added = '3.13'
2433+
[function.PyWeakref_GetRef]
2434+
added = '3.13'

Modules/_testcapimodule.c

+79
Original file line numberDiff line numberDiff line change
@@ -3372,6 +3372,84 @@ check_pyimport_addmodule(PyObject *self, PyObject *args)
33723372
}
33733373

33743374

3375+
static PyObject *
3376+
test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
3377+
{
3378+
// Create a new heap type, create an instance of this type, and delete the
3379+
// type. This object supports weak references.
3380+
PyObject *new_type = PyObject_CallFunction((PyObject*)&PyType_Type,
3381+
"s(){}", "TypeName");
3382+
if (new_type == NULL) {
3383+
return NULL;
3384+
}
3385+
PyObject *obj = PyObject_CallNoArgs(new_type);
3386+
Py_DECREF(new_type);
3387+
if (obj == NULL) {
3388+
return NULL;
3389+
}
3390+
Py_ssize_t refcnt = Py_REFCNT(obj);
3391+
3392+
// test PyWeakref_NewRef(), reference is alive
3393+
PyObject *weakref = PyWeakref_NewRef(obj, NULL);
3394+
if (weakref == NULL) {
3395+
Py_DECREF(obj);
3396+
return NULL;
3397+
}
3398+
assert(PyWeakref_Check(weakref));
3399+
assert(PyWeakref_CheckRefExact(weakref));
3400+
assert(PyWeakref_CheckRefExact(weakref));
3401+
assert(Py_REFCNT(obj) == refcnt);
3402+
3403+
// test PyWeakref_GetRef(), reference is alive
3404+
PyObject *ref1;
3405+
assert(PyWeakref_GetRef(weakref, &ref1) == 0);
3406+
assert(ref1 == obj);
3407+
assert(Py_REFCNT(obj) == (refcnt + 1));
3408+
Py_DECREF(ref1);
3409+
3410+
// test PyWeakref_GetObject(), reference is alive
3411+
PyObject *ref2 = PyWeakref_GetObject(weakref);
3412+
assert(ref2 == obj);
3413+
3414+
// test PyWeakref_GET_OBJECT(), reference is alive
3415+
PyObject *ref3 = PyWeakref_GET_OBJECT(weakref);
3416+
assert(ref3 == obj);
3417+
3418+
// delete the referenced object
3419+
assert(Py_REFCNT(obj) == 1);
3420+
Py_DECREF(obj);
3421+
3422+
// test PyWeakref_GET_OBJECT(), reference is dead
3423+
assert(PyWeakref_GET_OBJECT(weakref) == Py_None);
3424+
3425+
// test PyWeakref_GetRef(), reference is dead
3426+
PyObject *ref4 = Py_True; // marker to check that value was set
3427+
assert(PyWeakref_GetRef(weakref, &ref4) == 0);
3428+
assert(ref4 == NULL);
3429+
3430+
// None is not a weak reference object
3431+
PyObject *invalid_weakref = Py_None;
3432+
assert(!PyWeakref_Check(invalid_weakref));
3433+
assert(!PyWeakref_CheckRefExact(invalid_weakref));
3434+
assert(!PyWeakref_CheckRefExact(invalid_weakref));
3435+
3436+
// test PyWeakref_GetRef(), invalid type
3437+
assert(!PyErr_Occurred());
3438+
PyObject *ref5 = Py_True; // marker to check that value was set
3439+
assert(PyWeakref_GetRef(invalid_weakref, &ref5) == -1);
3440+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
3441+
PyErr_Clear();
3442+
assert(ref5 == NULL);
3443+
3444+
// test PyWeakref_GetObject(), invalid type
3445+
assert(PyWeakref_GetObject(invalid_weakref) == NULL);
3446+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
3447+
PyErr_Clear();
3448+
3449+
Py_RETURN_NONE;
3450+
}
3451+
3452+
33753453
static PyMethodDef TestMethods[] = {
33763454
{"set_errno", set_errno, METH_VARARGS},
33773455
{"test_config", test_config, METH_NOARGS},
@@ -3516,6 +3594,7 @@ static PyMethodDef TestMethods[] = {
35163594
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
35173595
{"test_atexit", test_atexit, METH_NOARGS},
35183596
{"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
3597+
{"test_weakref_capi", test_weakref_capi, METH_NOARGS},
35193598
{NULL, NULL} /* sentinel */
35203599
};
35213600

Objects/weakrefobject.c

+18
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,24 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
894894
}
895895

896896

897+
int
898+
PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
899+
{
900+
if (ref == NULL) {
901+
*pobj = NULL;
902+
PyErr_BadInternalCall();
903+
return -1;
904+
}
905+
if (!PyWeakref_Check(ref)) {
906+
*pobj = NULL;
907+
PyErr_SetString(PyExc_TypeError, "expected a weakref");
908+
return -1;
909+
}
910+
*pobj = _PyWeakref_GET_REF(ref);
911+
return 0;
912+
}
913+
914+
897915
PyObject *
898916
PyWeakref_GetObject(PyObject *ref)
899917
{

PC/python3dll.c

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)