Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-113633: Use module state structure for _testcapi. #113634

Merged
merged 1 commit into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use module state for the _testcapi extension module.
115 changes: 64 additions & 51 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,32 @@

// Forward declarations
static struct PyModuleDef _testcapimodule;
static PyObject *TestError; /* set to exception object in init */

// Module state
typedef struct {
PyObject *error; // _testcapi.error object
} testcapistate_t;

static testcapistate_t*
get_testcapi_state(PyObject *module)
{
void *state = PyModule_GetState(module);
assert(state != NULL);
return (testcapistate_t *)state;
}

/* Raise TestError with test_name + ": " + msg, and return NULL. */
static PyObject *
get_testerror(PyObject *self) {
testcapistate_t *state = get_testcapi_state((PyObject *)Py_TYPE(self));
return state->error;
}

/* Raise _testcapi.error with test_name + ": " + msg, and return NULL. */

static PyObject *
raiseTestError(const char* test_name, const char* msg)
raiseTestError(PyObject *self, const char* test_name, const char* msg)
{
PyErr_Format(TestError, "%s: %s", test_name, msg);
PyErr_Format(get_testerror(self), "%s: %s", test_name, msg);
return NULL;
}

Expand All @@ -52,10 +69,10 @@ raiseTestError(const char* test_name, const char* msg)
platforms have these hardcoded. Better safe than sorry.
*/
static PyObject*
sizeof_error(const char* fatname, const char* typname,
sizeof_error(PyObject *self, const char* fatname, const char* typname,
int expected, int got)
{
PyErr_Format(TestError,
PyErr_Format(get_testerror(self),
"%s #define == %d but sizeof(%s) == %d",
fatname, expected, typname, got);
return (PyObject*)NULL;
Expand All @@ -66,7 +83,7 @@ test_config(PyObject *self, PyObject *Py_UNUSED(ignored))
{
#define CHECK_SIZEOF(FATNAME, TYPE) \
if (FATNAME != sizeof(TYPE)) \
return sizeof_error(#FATNAME, #TYPE, FATNAME, sizeof(TYPE))
return sizeof_error(self, #FATNAME, #TYPE, FATNAME, sizeof(TYPE))

CHECK_SIZEOF(SIZEOF_SHORT, short);
CHECK_SIZEOF(SIZEOF_INT, int);
Expand All @@ -89,15 +106,15 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored))
#endif
#define CHECK_SIZEOF(TYPE, EXPECTED) \
if (EXPECTED != sizeof(TYPE)) { \
PyErr_Format(TestError, \
PyErr_Format(get_testerror(self), \
"sizeof(%s) = %u instead of %u", \
#TYPE, sizeof(TYPE), EXPECTED); \
return (PyObject*)NULL; \
}
#define IS_SIGNED(TYPE) (((TYPE)-1) < (TYPE)0)
#define CHECK_SIGNNESS(TYPE, SIGNED) \
if (IS_SIGNED(TYPE) != SIGNED) { \
PyErr_Format(TestError, \
PyErr_Format(get_testerror(self), \
"%s signness is, instead of %i", \
#TYPE, IS_SIGNED(TYPE), SIGNED); \
return (PyObject*)NULL; \
Expand Down Expand Up @@ -170,7 +187,7 @@ test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
for (i = 0; i < NLIST; ++i) {
PyObject* anint = PyList_GET_ITEM(list, i);
if (PyLong_AS_LONG(anint) != NLIST-1-i) {
PyErr_SetString(TestError,
PyErr_SetString(get_testerror(self),
"test_list_api: reverse screwed up");
Py_DECREF(list);
return (PyObject*)NULL;
Expand All @@ -183,7 +200,7 @@ test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
}

static int
test_dict_inner(int count)
test_dict_inner(PyObject *self, int count)
{
Py_ssize_t pos = 0, iterations = 0;
int i;
Expand Down Expand Up @@ -230,7 +247,7 @@ test_dict_inner(int count)

if (iterations != count) {
PyErr_SetString(
TestError,
get_testerror(self),
"test_dict_iteration: dict iteration went wrong ");
return -1;
} else {
Expand All @@ -246,7 +263,7 @@ test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored))
int i;

for (i = 0; i < 200; i++) {
if (test_dict_inner(i) < 0) {
if (test_dict_inner(self, i) < 0) {
return NULL;
}
}
Expand Down Expand Up @@ -330,14 +347,14 @@ test_lazy_hash_inheritance(PyObject* self, PyObject *Py_UNUSED(ignored))
if (obj == NULL) {
PyErr_Clear();
PyErr_SetString(
TestError,
get_testerror(self),
"test_lazy_hash_inheritance: failed to create object");
return NULL;
}

if (type->tp_dict != NULL) {
PyErr_SetString(
TestError,
get_testerror(self),
"test_lazy_hash_inheritance: type initialised too soon");
Py_DECREF(obj);
return NULL;
Expand All @@ -347,23 +364,23 @@ test_lazy_hash_inheritance(PyObject* self, PyObject *Py_UNUSED(ignored))
if ((hash == -1) && PyErr_Occurred()) {
PyErr_Clear();
PyErr_SetString(
TestError,
get_testerror(self),
"test_lazy_hash_inheritance: could not hash object");
Py_DECREF(obj);
return NULL;
}

if (type->tp_dict == NULL) {
PyErr_SetString(
TestError,
get_testerror(self),
"test_lazy_hash_inheritance: type not initialised by hash()");
Py_DECREF(obj);
return NULL;
}

if (type->tp_hash != PyType_Type.tp_hash) {
PyErr_SetString(
TestError,
get_testerror(self),
"test_lazy_hash_inheritance: unexpected hash function");
Py_DECREF(obj);
return NULL;
Expand Down Expand Up @@ -423,7 +440,7 @@ py_buildvalue_ints(PyObject *self, PyObject *args)
}

static int
test_buildvalue_N_error(const char *fmt)
test_buildvalue_N_error(PyObject *self, const char *fmt)
{
PyObject *arg, *res;

Expand All @@ -439,7 +456,7 @@ test_buildvalue_N_error(const char *fmt)
}
Py_DECREF(res);
if (Py_REFCNT(arg) != 1) {
PyErr_Format(TestError, "test_buildvalue_N: "
PyErr_Format(get_testerror(self), "test_buildvalue_N: "
"arg was not decrefed in successful "
"Py_BuildValue(\"%s\")", fmt);
return -1;
Expand All @@ -448,13 +465,13 @@ test_buildvalue_N_error(const char *fmt)
Py_INCREF(arg);
res = Py_BuildValue(fmt, raise_error, NULL, arg);
if (res != NULL || !PyErr_Occurred()) {
PyErr_Format(TestError, "test_buildvalue_N: "
PyErr_Format(get_testerror(self), "test_buildvalue_N: "
"Py_BuildValue(\"%s\") didn't complain", fmt);
return -1;
}
PyErr_Clear();
if (Py_REFCNT(arg) != 1) {
PyErr_Format(TestError, "test_buildvalue_N: "
PyErr_Format(get_testerror(self), "test_buildvalue_N: "
"arg was not decrefed in failed "
"Py_BuildValue(\"%s\")", fmt);
return -1;
Expand All @@ -478,25 +495,25 @@ test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored))
return NULL;
}
if (res != arg) {
return raiseTestError("test_buildvalue_N",
return raiseTestError(self, "test_buildvalue_N",
"Py_BuildValue(\"N\") returned wrong result");
}
if (Py_REFCNT(arg) != 2) {
return raiseTestError("test_buildvalue_N",
return raiseTestError(self, "test_buildvalue_N",
"arg was not decrefed in Py_BuildValue(\"N\")");
}
Py_DECREF(res);
Py_DECREF(arg);

if (test_buildvalue_N_error("O&N") < 0)
if (test_buildvalue_N_error(self, "O&N") < 0)
return NULL;
if (test_buildvalue_N_error("(O&N)") < 0)
if (test_buildvalue_N_error(self, "(O&N)") < 0)
return NULL;
if (test_buildvalue_N_error("[O&N]") < 0)
if (test_buildvalue_N_error(self, "[O&N]") < 0)
return NULL;
if (test_buildvalue_N_error("{O&N}") < 0)
if (test_buildvalue_N_error(self, "{O&N}") < 0)
return NULL;
if (test_buildvalue_N_error("{()O&(())N}") < 0)
if (test_buildvalue_N_error(self, "{()O&(())N}") < 0)
return NULL;

Py_RETURN_NONE;
Expand Down Expand Up @@ -906,7 +923,7 @@ test_string_to_double(PyObject *self, PyObject *Py_UNUSED(ignored)) {

Py_RETURN_NONE;
fail:
return raiseTestError("test_string_to_double", msg);
return raiseTestError(self, "test_string_to_double", msg);
#undef CHECK_STRING
#undef CHECK_INVALID
}
Expand Down Expand Up @@ -1057,7 +1074,7 @@ test_capsule(PyObject *self, PyObject *Py_UNUSED(ignored))

exit:
if (error) {
return raiseTestError("test_capsule", error);
return raiseTestError(self, "test_capsule", error);
}
Py_RETURN_NONE;
#undef FAIL
Expand Down Expand Up @@ -1268,7 +1285,7 @@ test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored))
ptr = view.buf;
for (i = 0; i < 5; i++) {
if (ptr[2*i] != i) {
PyErr_SetString(TestError,
PyErr_SetString(get_testerror(self),
"test_from_contiguous: incorrect result");
return NULL;
}
Expand All @@ -1281,7 +1298,7 @@ test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored))
ptr = view.buf;
for (i = 0; i < 5; i++) {
if (*(ptr-2*i) != i) {
PyErr_SetString(TestError,
PyErr_SetString(get_testerror(self),
"test_from_contiguous: incorrect result");
return NULL;
}
Expand Down Expand Up @@ -1334,7 +1351,7 @@ test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored))
Py_RETURN_NONE;

error:
PyErr_SetString(TestError,
PyErr_SetString(get_testerror(self),
"test_pep3118_obsolete_write_locks: failure");
return NULL;
}
Expand Down Expand Up @@ -1953,7 +1970,7 @@ test_pythread_tss_key_state(PyObject *self, PyObject *args)
{
Py_tss_t tss_key = Py_tss_NEEDS_INIT;
if (PyThread_tss_is_created(&tss_key)) {
return raiseTestError("test_pythread_tss_key_state",
return raiseTestError(self, "test_pythread_tss_key_state",
"TSS key not in an uninitialized state at "
"creation time");
}
Expand All @@ -1962,27 +1979,27 @@ test_pythread_tss_key_state(PyObject *self, PyObject *args)
return NULL;
}
if (!PyThread_tss_is_created(&tss_key)) {
return raiseTestError("test_pythread_tss_key_state",
return raiseTestError(self, "test_pythread_tss_key_state",
"PyThread_tss_create succeeded, "
"but with TSS key in an uninitialized state");
}
if (PyThread_tss_create(&tss_key) != 0) {
return raiseTestError("test_pythread_tss_key_state",
return raiseTestError(self, "test_pythread_tss_key_state",
"PyThread_tss_create unsuccessful with "
"an already initialized key");
}
#define CHECK_TSS_API(expr) \
(void)(expr); \
if (!PyThread_tss_is_created(&tss_key)) { \
return raiseTestError("test_pythread_tss_key_state", \
return raiseTestError(self, "test_pythread_tss_key_state", \
"TSS key initialization state was not " \
"preserved after calling " #expr); }
CHECK_TSS_API(PyThread_tss_set(&tss_key, NULL));
CHECK_TSS_API(PyThread_tss_get(&tss_key));
#undef CHECK_TSS_API
PyThread_tss_delete(&tss_key);
if (PyThread_tss_is_created(&tss_key)) {
return raiseTestError("test_pythread_tss_key_state",
return raiseTestError(self, "test_pythread_tss_key_state",
"PyThread_tss_delete called, but did not "
"set the key state to uninitialized");
}
Expand All @@ -1993,7 +2010,7 @@ test_pythread_tss_key_state(PyObject *self, PyObject *args)
return NULL;
}
if (PyThread_tss_is_created(ptr_key)) {
return raiseTestError("test_pythread_tss_key_state",
return raiseTestError(self, "test_pythread_tss_key_state",
"TSS key not in an uninitialized state at "
"allocation time");
}
Expand Down Expand Up @@ -3825,14 +3842,9 @@ static PyTypeObject ContainerNoGC_type = {

static struct PyModuleDef _testcapimodule = {
PyModuleDef_HEAD_INIT,
"_testcapi",
NULL,
-1,
TestMethods,
NULL,
NULL,
NULL,
NULL
.m_name = "_testcapi",
.m_size = sizeof(testcapistate_t),
.m_methods = TestMethods,
};

/* Per PEP 489, this module will not be converted to multi-phase initialization
Expand Down Expand Up @@ -3927,9 +3939,10 @@ PyInit__testcapi(void)
PyModule_AddIntConstant(m, "the_number_three", 3);
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);

TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
Py_INCREF(TestError);
PyModule_AddObject(m, "error", TestError);
testcapistate_t *state = get_testcapi_state(m);
state->error = PyErr_NewException("_testcapi.error", NULL, NULL);
Py_INCREF(state->error);
PyModule_AddObject(m, "error", state->error);

if (PyType_Ready(&ContainerNoGC_type) < 0) {
return NULL;
Expand Down