Skip to content

Commit d661ec5

Browse files
committed
gh-108240: Add PyCapsule_SetTraverse() function
The _socket extension uses PyCapsule_SetTraverse() to visit and clear the socket type in the garbage collector. So the _socket.socket type can be cleared in some corner cases when it wasn't possible before.
1 parent a0bb4a3 commit d661ec5

File tree

10 files changed

+159
-72
lines changed

10 files changed

+159
-72
lines changed

Doc/c-api/capsule.rst

+16
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,19 @@ Refer to :ref:`using-capsules` for more information on using these objects.
157157
``NULL``.
158158
159159
Return ``0`` on success. Return nonzero and set an exception on failure.
160+
161+
162+
.. c:function:: int PyCapsule_SetTraverse(PyObject *capsule, traverseproc traverse_func, inquiry clear_func)
163+
164+
Set a traverse and clear functions inside *capsule*.
165+
166+
It can be used if the capsule contains Python objects which should be
167+
visited (and cleared) by the garbage collector (:mod:`gc`). When this
168+
function is called, the capsule is tracked by the GC.
169+
170+
See :c:member:`~PyTypeObject.tp_traverse` and
171+
:c:member:`~PyTypeObject.tp_clear` for the signature of these functions.
172+
173+
Return ``0`` on success. Return nonzero and set an exception on failure.
174+
175+
.. versionadded:: 3.13

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
@@ -866,6 +866,10 @@ New Features
866866
:term:`shutting down <interpreter shutdown>`.
867867
(Contributed by Victor Stinner in :gh:`108014`.)
868868

869+
* Add :c:func:`PyCapsule_SetTraverse` function to set GC traverse and clear
870+
functions on a capsule.
871+
(Contributed by Victor Stinner in :gh:`108014`.)
872+
869873
Porting to Python 3.13
870874
----------------------
871875

Include/pycapsule.h

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ PyAPI_FUNC(int) PyCapsule_SetName(PyObject *capsule, const char *name);
4848

4949
PyAPI_FUNC(int) PyCapsule_SetContext(PyObject *capsule, void *context);
5050

51+
PyAPI_FUNC(int) PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_func);
52+
5153
PyAPI_FUNC(void *) PyCapsule_Import(
5254
const char *name, /* UTF-8 encoded string */
5355
int no_block);

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,2 @@
1+
Add :c:func:`PyCapsule_SetTraverse` function to set GC traverse and clear
2+
functions on a capsule. Patch by Victor Stinner.

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -2450,3 +2450,5 @@
24502450
added = '3.13'
24512451
[function.PyDict_GetItemStringRef]
24522452
added = '3.13'
2453+
[function.PyCapsule_SetTraverse]
2454+
added = '3.13'

Modules/socketmodule.c

+30-6
Original file line numberDiff line numberDiff line change
@@ -7314,20 +7314,39 @@ os_init(void)
73147314
}
73157315
#endif
73167316

7317+
static int
7318+
sock_capi_traverse(PyObject *capsule, visitproc visit, void *arg)
7319+
{
7320+
PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
7321+
assert(capi != NULL);
7322+
Py_VISIT(capi->Sock_Type);
7323+
return 0;
7324+
}
7325+
7326+
static int
7327+
sock_capi_clear(PyObject *capsule)
7328+
{
7329+
PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
7330+
assert(capi != NULL);
7331+
Py_CLEAR(capi->Sock_Type);
7332+
return 0;
7333+
}
7334+
73177335
static void
7318-
sock_free_api(PySocketModule_APIObject *capi)
7336+
sock_capi_free(PySocketModule_APIObject *capi)
73197337
{
7320-
Py_DECREF(capi->Sock_Type);
7338+
Py_XDECREF(capi->Sock_Type); // sock_capi_free() can clear it
73217339
Py_DECREF(capi->error);
73227340
Py_DECREF(capi->timeout_error);
73237341
PyMem_Free(capi);
73247342
}
73257343

73267344
static void
7327-
sock_destroy_api(PyObject *capsule)
7345+
sock_capi_destroy(PyObject *capsule)
73287346
{
73297347
void *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
7330-
sock_free_api(capi);
7348+
assert(capi != NULL);
7349+
sock_capi_free(capi);
73317350
}
73327351

73337352
static PySocketModule_APIObject *
@@ -7432,11 +7451,16 @@ socket_exec(PyObject *m)
74327451
}
74337452
PyObject *capsule = PyCapsule_New(capi,
74347453
PySocket_CAPSULE_NAME,
7435-
sock_destroy_api);
7454+
sock_capi_destroy);
74367455
if (capsule == NULL) {
7437-
sock_free_api(capi);
7456+
sock_capi_free(capi);
74387457
goto error;
74397458
}
7459+
if (PyCapsule_SetTraverse(capsule, sock_capi_traverse, sock_capi_clear) < 0) {
7460+
sock_capi_free(capi);
7461+
goto error;
7462+
}
7463+
74407464
if (PyModule_Add(m, PySocket_CAPI_NAME, capsule) < 0) {
74417465
goto error;
74427466
}

0 commit comments

Comments
 (0)