gh-137210: Add a struct, slot & function for checking an extension's ABI (GH-137212)

Co-authored-by: Steve Dower <steve.dower@microsoft.com>
This commit is contained in:
Petr Viktorin 2025-09-05 16:23:18 +02:00 committed by GitHub
parent c1a9c23195
commit 0c74fc8af0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 654 additions and 8 deletions

View file

@ -4,6 +4,7 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_object.h" // _PyType_IsReady()
#include "pycore_unicodeobject.h" // _PyUnicodeWriter_FormatV()
typedef double va_double;
@ -668,6 +669,116 @@ PyModule_AddType(PyObject *module, PyTypeObject *type)
return PyModule_AddObjectRef(module, name, (PyObject *)type);
}
static int _abiinfo_raise(const char *module_name, const char *format, ...)
{
PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
if (!writer) {
return -1;
}
if (module_name) {
if (PyUnicodeWriter_WriteUTF8(writer, module_name, -1) < 0) {
PyUnicodeWriter_Discard(writer);
return -1;
}
if (PyUnicodeWriter_WriteASCII(writer, ": ", 2) < 0) {
PyUnicodeWriter_Discard(writer);
return -1;
}
}
va_list vargs;
va_start(vargs, format);
if (_PyUnicodeWriter_FormatV(writer, format, vargs) < 0) {
PyUnicodeWriter_Discard(writer);
return -1;
}
PyObject *message = PyUnicodeWriter_Finish(writer);
if (!message) {
return -1;
}
PyErr_SetObject(PyExc_ImportError, message);
Py_DECREF(message);
return -1;
}
int PyABIInfo_Check(PyABIInfo *info, const char *module_name)
{
if (!info) {
return _abiinfo_raise(module_name, "NULL PyABIInfo");
}
/* abiinfo_major_version */
if (info->abiinfo_major_version == 0) {
return 0;
}
if (info->abiinfo_major_version > 1) {
return _abiinfo_raise(module_name, "PyABIInfo version too high");
}
/* Internal ABI */
if (info->flags & PyABIInfo_INTERNAL) {
if (info->abi_version && (info->abi_version != PY_VERSION_HEX)) {
return _abiinfo_raise(
module_name,
"incompatible internal ABI (0x%x != 0x%x)",
info->abi_version, PY_VERSION_HEX);
}
}
#define XY_MASK 0xffff0000
if (info->flags & PyABIInfo_STABLE) {
/* Greater-than major.minor version check */
if (info->abi_version) {
if ((info->abi_version & XY_MASK) > (PY_VERSION_HEX & XY_MASK)) {
return _abiinfo_raise(
module_name,
"incompatible future stable ABI version (%d.%d)",
((info->abi_version) >> 24) % 0xff,
((info->abi_version) >> 16) % 0xff);
}
if (info->abi_version < Py_PACK_VERSION(3, 2)) {
return _abiinfo_raise(
module_name,
"invalid stable ABI version (%d.%d)",
((info->abi_version) >> 24) % 0xff,
((info->abi_version) >> 16) % 0xff);
}
}
if (info->flags & PyABIInfo_INTERNAL) {
return _abiinfo_raise(module_name,
"cannot use both internal and stable ABI");
}
}
else {
/* Exact major.minor version check */
if (info->abi_version) {
if ((info->abi_version & XY_MASK) != (PY_VERSION_HEX & XY_MASK)) {
return _abiinfo_raise(
module_name,
"incompatible ABI version (%d.%d)",
((info->abi_version) >> 24) % 0xff,
((info->abi_version) >> 16) % 0xff);
}
}
}
#undef XY_MASK
/* Free-threading/GIL */
uint16_t gilflags = info->flags & (PyABIInfo_GIL | PyABIInfo_FREETHREADED);
#if Py_GIL_DISABLED
if (gilflags == PyABIInfo_GIL) {
return _abiinfo_raise(module_name,
"incompatible with free-threaded CPython");
}
#else
if (gilflags == PyABIInfo_FREETHREADED) {
return _abiinfo_raise(module_name,
"only compatible with free-threaded CPython");
}
#endif
return 0;
}
/* Exported functions for version helper macros */