You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[C API] Revisit usage of the PyCapsule C API with multi-phase initialization API #85964
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2021-01-23.12:49:36.720>created_at=<Date2020-09-16.14:07:49.185>labels= ['expert-C-API', '3.10']
title='[C API] Revisit usage of the PyCapsule C API with multi-phase initialization API'updated_at=<Date2021-01-25.09:46:36.596>user='https://github.com/vstinner'
More and more extension modules are converted to the multi-phase initialization API (PEP-489) in bpo-1635741.
The problem is that the usage of the PyCapsule C API is not adapted in these extensions, to isolate well capsule objects from other module instances.
For example, the pyexpat extension uses "static struct PyExpat_CAPI capi;". It was fine when it was not possible to create more than one instance of the extension module. But with PR 22222 (currently under review), it becomes possible to have multiple extension module instances. Each module instance creates its own capsule object, but all capsule object points to the same unique "struct PyExpat_CAPI" instance.
For the specific case of the pyexpat in its current implementation, reusing the same "struct PyExpat_CAPI" instance is ok-ish, since the value is the same for all module instances. But this design sounds fragile.
It would be safer to allocate a "struct PyExpat_CAPI" on the heap memory in each module instance, and use a PyCapsule destructor function (3rd parameter of PyCapsule_New()).
The _ctypes does that:
---
void*space=PyMem_Calloc(2, sizeof(int));
if (space==NULL)
returnNULL;
errobj=PyCapsule_New(space, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor);
with:
---
staticvoidpymem_destructor(PyObject*ptr)
{
void*p=PyCapsule_GetPointer(ptr, CTYPES_CAPSULE_NAME_PYMEM);
if (p) {
PyMem_Free(p);
}
}
The PyCapsule API is used by multiple extension modules:
_ctypes: allocate memory on the heap and uses a destructor to release it
_curses: static variable, PyInit__curses() sets PyCurses_API[0] to &PyCursesWindow_Type (static type)
_datetime: static variable, PyInit__datetime() creates a timezone object and stores it into CAPI.TimeZone_UTC
_decimal: static variable
_socket: static variable, PyInit__socket() sets PySocketModuleAPI.error to PyExc_OSError, and sets PySocketModuleAPI.timeout_error to _socket.timeout (a new exception object)
pyexpat: static varaible
unicodedata: static variable
posix: nt._add_dll_directory() creates a PyCapsule using AddDllDirectory() result as a the pointer value
The _datetime module overrides the
--
See also the PEP-630 "Isolating Extension Modules".
Right now, it's ok-ish. Mohamed Koubaa is working on PR 22145 to pass a module state into internal functions, and so the PyCapsule pointer must be different in each module instance.
Also note that the capsule generally needs to hold references to the objects in exposes, and not rely on the module object to keep things alive. (Module objects with multi-phase init are not unique nor immortal.)
_ctypes is an exception, since it doesn't have ant PyObject*. But in most others the destructor should also contain some DECREFs.
hai shi: "I have checked all capi instances have been allocated in the heap memory. So I think this bpo can be closed ;)"
I also checks and I confirm that PyCapsule_New() is no longer used with a pointer pointing to static data. Well, there is one last case, in an unit test on the API itself (in _testcapi). This corner case is acceptable ;-)
Activity
vstinner commentedon Sep 16, 2020
More and more extension modules are converted to the multi-phase initialization API (PEP-489) in bpo-1635741.
The problem is that the usage of the PyCapsule C API is not adapted in these extensions, to isolate well capsule objects from other module instances.
For example, the pyexpat extension uses "static struct PyExpat_CAPI capi;". It was fine when it was not possible to create more than one instance of the extension module. But with PR 22222 (currently under review), it becomes possible to have multiple extension module instances. Each module instance creates its own capsule object, but all capsule object points to the same unique "struct PyExpat_CAPI" instance.
For the specific case of the pyexpat in its current implementation, reusing the same "struct PyExpat_CAPI" instance is ok-ish, since the value is the same for all module instances. But this design sounds fragile.
It would be safer to allocate a "struct PyExpat_CAPI" on the heap memory in each module instance, and use a PyCapsule destructor function (3rd parameter of PyCapsule_New()).
The _ctypes does that:
---
with:
---
The PyCapsule API is used by multiple extension modules:
_ctypes: allocate memory on the heap and uses a destructor to release it
_curses: static variable, PyInit__curses() sets PyCurses_API[0] to &PyCursesWindow_Type (static type)
_datetime: static variable, PyInit__datetime() creates a timezone object and stores it into CAPI.TimeZone_UTC
_decimal: static variable
_socket: static variable, PyInit__socket() sets PySocketModuleAPI.error to PyExc_OSError, and sets PySocketModuleAPI.timeout_error to _socket.timeout (a new exception object)
pyexpat: static varaible
unicodedata: static variable
posix: nt._add_dll_directory() creates a PyCapsule using AddDllDirectory() result as a the pointer value
The _datetime module overrides the
--
See also the PEP-630 "Isolating Extension Modules".
vstinner commentedon Sep 16, 2020
Right now, it's ok-ish. Mohamed Koubaa is working on PR 22145 to pass a module state into internal functions, and so the PyCapsule pointer must be different in each module instance.
encukou commentedon Oct 20, 2020
Also note that the capsule generally needs to hold references to the objects in exposes, and not rely on the module object to keep things alive. (Module objects with multi-phase init are not unique nor immortal.)
_ctypes is an exception, since it doesn't have ant PyObject*. But in most others the destructor should also contain some DECREFs.
vstinner commentedon Oct 27, 2020
Oh right, that's a great advice!
vstinner commentedon Jan 3, 2021
New changeset 7c83eaa by Hai Shi in branch 'master':
bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)
7c83eaa
erlend-aasland commentedon Jan 5, 2021
Hai Shi, do you mind if I have a go at the socket module CAPI capsule?
shihai1991 commentedon Jan 5, 2021
Welcome to join this bpo, you can enhance those extension modules if you like :)
erlend-aasland commentedon Jan 5, 2021
Thanks :) I'll take a look at socket first.
tiran commentedon Jan 5, 2021
PySocketModuleAPI.timeout_error now points to builtin TimeoutError exception.
I'm also in the process of removing PySocketModuleAPI from _ssl.c.
erlend-aasland commentedon Jan 5, 2021
That's the only user of the API, right? In that case, I'll target _decimal instead. Hai Shi is working on _curses and _datetime, AFAIK.
erlend-aasland commentedon Jan 5, 2021
BTW, do we still want to keep the socket CAPI for external users?
tiran commentedon Jan 5, 2021
Yes, at least for a while. socket.h is not part of the public include headers. It's still possible that the CAPI is used by an external project.
vstinner commentedon Jan 6, 2021
New changeset fe9f446 by Erlend Egeberg Aasland in branch 'master':
bpo-41798: Allocate _decimal extension module C API on the heap (GH-24117)
fe9f446
vstinner commentedon Jan 6, 2021
New changeset f22b7ca by Erlend Egeberg Aasland in branch 'master':
bpo-41798: Allocate _socket module C API on the heap (GH-24126)
f22b7ca
vstinner commentedon Jan 6, 2021
New changeset 1ab0459 by Hai Shi in branch 'master':
bpo-41798: Allocate the _datetime.datetime_CAPI on the heap memory (GH-24096)
1ab0459
vstinner commentedon Jan 20, 2021
New changeset 61d2639 by Erlend Egeberg Aasland in branch 'master':
bpo-41798: Allocate unicodedata CAPI on the heap (GH-24128)
61d2639
vstinner commentedon Jan 22, 2021
New changeset 2f12a1b by Hai Shi in branch 'master':
bpo-41798: Allocate the _curses._C_API on the heap memory (GH-24186)
2f12a1b
shihai1991 commentedon Jan 23, 2021
I have checked all capi instances have been allocated in the heap memory.
So I think this bpo can be closed ;)
Thanks Erlend for your contribution.
Thanks victor, petr for your review&merge.
vstinner commentedon Jan 25, 2021
hai shi: "I have checked all capi instances have been allocated in the heap memory. So I think this bpo can be closed ;)"
I also checks and I confirm that PyCapsule_New() is no longer used with a pointer pointing to static data. Well, there is one last case, in an unit test on the API itself (in _testcapi). This corner case is acceptable ;-)
Thanks all for the fixes!