mirror of
https://github.com/python/cpython.git
synced 2024-11-25 10:54:51 +08:00
3e3eacb5fc
going through the buffer interface API. Added tests for this to the _testcapi module and updated docs.
410 lines
9.6 KiB
C
410 lines
9.6 KiB
C
/*
|
|
* C Extension module to test Python interpreter C APIs.
|
|
*
|
|
* The 'test_*' functions exported by this module are run as part of the
|
|
* standard Python regression test, via Lib/test/test_capi.py.
|
|
*/
|
|
|
|
#include "Python.h"
|
|
|
|
static PyObject *TestError; /* set to exception object in init */
|
|
|
|
/* Raise TestError with test_name + ": " + msg, and return NULL. */
|
|
|
|
static PyObject *
|
|
raiseTestError(const char* test_name, const char* msg)
|
|
{
|
|
char buf[2048];
|
|
|
|
if (strlen(test_name) + strlen(msg) > sizeof(buf) - 50)
|
|
PyErr_SetString(TestError, "internal error msg too large");
|
|
else {
|
|
PyOS_snprintf(buf, sizeof(buf), "%s: %s", test_name, msg);
|
|
PyErr_SetString(TestError, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Test #defines from pyconfig.h (particularly the SIZEOF_* defines).
|
|
|
|
The ones derived from autoconf on the UNIX-like OSes can be relied
|
|
upon (in the absence of sloppy cross-compiling), but the Windows
|
|
platforms have these hardcoded. Better safe than sorry.
|
|
*/
|
|
static PyObject*
|
|
sizeof_error(const char* fatname, const char* typename,
|
|
int expected, int got)
|
|
{
|
|
char buf[1024];
|
|
PyOS_snprintf(buf, sizeof(buf),
|
|
"%.200s #define == %d but sizeof(%.200s) == %d",
|
|
fatname, expected, typename, got);
|
|
PyErr_SetString(TestError, buf);
|
|
return (PyObject*)NULL;
|
|
}
|
|
|
|
static PyObject*
|
|
test_config(PyObject *self, PyObject *args)
|
|
{
|
|
if (!PyArg_ParseTuple(args, ":test_config"))
|
|
return NULL;
|
|
|
|
#define CHECK_SIZEOF(FATNAME, TYPE) \
|
|
if (FATNAME != sizeof(TYPE)) \
|
|
return sizeof_error(#FATNAME, #TYPE, FATNAME, sizeof(TYPE))
|
|
|
|
CHECK_SIZEOF(SIZEOF_SHORT, short);
|
|
CHECK_SIZEOF(SIZEOF_INT, int);
|
|
CHECK_SIZEOF(SIZEOF_LONG, long);
|
|
CHECK_SIZEOF(SIZEOF_VOID_P, void*);
|
|
CHECK_SIZEOF(SIZEOF_TIME_T, time_t);
|
|
#ifdef HAVE_LONG_LONG
|
|
CHECK_SIZEOF(SIZEOF_LONG_LONG, LONG_LONG);
|
|
#endif
|
|
|
|
#undef CHECK_SIZEOF
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
static PyObject*
|
|
test_list_api(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject* list;
|
|
int i;
|
|
if (!PyArg_ParseTuple(args, ":test_list_api"))
|
|
return NULL;
|
|
|
|
/* SF bug 132008: PyList_Reverse segfaults */
|
|
#define NLIST 30
|
|
list = PyList_New(NLIST);
|
|
if (list == (PyObject*)NULL)
|
|
return (PyObject*)NULL;
|
|
/* list = range(NLIST) */
|
|
for (i = 0; i < NLIST; ++i) {
|
|
PyObject* anint = PyInt_FromLong(i);
|
|
if (anint == (PyObject*)NULL) {
|
|
Py_DECREF(list);
|
|
return (PyObject*)NULL;
|
|
}
|
|
PyList_SET_ITEM(list, i, anint);
|
|
}
|
|
/* list.reverse(), via PyList_Reverse() */
|
|
i = PyList_Reverse(list); /* should not blow up! */
|
|
if (i != 0) {
|
|
Py_DECREF(list);
|
|
return (PyObject*)NULL;
|
|
}
|
|
/* Check that list == range(29, -1, -1) now */
|
|
for (i = 0; i < NLIST; ++i) {
|
|
PyObject* anint = PyList_GET_ITEM(list, i);
|
|
if (PyInt_AS_LONG(anint) != NLIST-1-i) {
|
|
PyErr_SetString(TestError,
|
|
"test_list_api: reverse screwed up");
|
|
Py_DECREF(list);
|
|
return (PyObject*)NULL;
|
|
}
|
|
}
|
|
Py_DECREF(list);
|
|
#undef NLIST
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
static int
|
|
test_dict_inner(int count)
|
|
{
|
|
int pos = 0, iterations = 0, i;
|
|
PyObject *dict = PyDict_New();
|
|
PyObject *v, *k;
|
|
|
|
if (dict == NULL)
|
|
return -1;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
v = PyInt_FromLong(i);
|
|
PyDict_SetItem(dict, v, v);
|
|
Py_DECREF(v);
|
|
}
|
|
|
|
while (PyDict_Next(dict, &pos, &k, &v)) {
|
|
PyObject *o;
|
|
iterations++;
|
|
|
|
i = PyInt_AS_LONG(v) + 1;
|
|
o = PyInt_FromLong(i);
|
|
if (o == NULL)
|
|
return -1;
|
|
if (PyDict_SetItem(dict, k, o) < 0) {
|
|
Py_DECREF(o);
|
|
return -1;
|
|
}
|
|
Py_DECREF(o);
|
|
}
|
|
|
|
Py_DECREF(dict);
|
|
|
|
if (iterations != count) {
|
|
PyErr_SetString(
|
|
TestError,
|
|
"test_dict_iteration: dict iteration went wrong ");
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static PyObject*
|
|
test_dict_iteration(PyObject* self, PyObject* args)
|
|
{
|
|
int i;
|
|
|
|
if (!PyArg_ParseTuple(args, ":test_dict_iteration"))
|
|
return NULL;
|
|
|
|
for (i = 0; i < 200; i++) {
|
|
if (test_dict_inner(i) < 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG)
|
|
PyLong_{As, From}{Unsigned,}LongLong().
|
|
|
|
Note that the meat of the test is contained in testcapi_long.h.
|
|
This is revolting, but delicate code duplication is worse: "almost
|
|
exactly the same" code is needed to test LONG_LONG, but the ubiquitous
|
|
dependence on type names makes it impossible to use a parameterized
|
|
function. A giant macro would be even worse than this. A C++ template
|
|
would be perfect.
|
|
|
|
The "report an error" functions are deliberately not part of the #include
|
|
file: if the test fails, you can set a breakpoint in the appropriate
|
|
error function directly, and crawl back from there in the debugger.
|
|
*/
|
|
|
|
#define UNBIND(X) Py_DECREF(X); (X) = NULL
|
|
|
|
static PyObject *
|
|
raise_test_long_error(const char* msg)
|
|
{
|
|
return raiseTestError("test_long_api", msg);
|
|
}
|
|
|
|
#define TESTNAME test_long_api_inner
|
|
#define TYPENAME long
|
|
#define F_S_TO_PY PyLong_FromLong
|
|
#define F_PY_TO_S PyLong_AsLong
|
|
#define F_U_TO_PY PyLong_FromUnsignedLong
|
|
#define F_PY_TO_U PyLong_AsUnsignedLong
|
|
|
|
#include "testcapi_long.h"
|
|
|
|
static PyObject *
|
|
test_long_api(PyObject* self, PyObject* args)
|
|
{
|
|
if (!PyArg_ParseTuple(args, ":test_long_api"))
|
|
return NULL;
|
|
|
|
return TESTNAME(raise_test_long_error);
|
|
}
|
|
|
|
#undef TESTNAME
|
|
#undef TYPENAME
|
|
#undef F_S_TO_PY
|
|
#undef F_PY_TO_S
|
|
#undef F_U_TO_PY
|
|
#undef F_PY_TO_U
|
|
|
|
#ifdef HAVE_LONG_LONG
|
|
|
|
static PyObject *
|
|
raise_test_longlong_error(const char* msg)
|
|
{
|
|
return raiseTestError("test_longlong_api", msg);
|
|
}
|
|
|
|
#define TESTNAME test_longlong_api_inner
|
|
#define TYPENAME LONG_LONG
|
|
#define F_S_TO_PY PyLong_FromLongLong
|
|
#define F_PY_TO_S PyLong_AsLongLong
|
|
#define F_U_TO_PY PyLong_FromUnsignedLongLong
|
|
#define F_PY_TO_U PyLong_AsUnsignedLongLong
|
|
|
|
#include "testcapi_long.h"
|
|
|
|
static PyObject *
|
|
test_longlong_api(PyObject* self, PyObject* args)
|
|
{
|
|
if (!PyArg_ParseTuple(args, ":test_longlong_api"))
|
|
return NULL;
|
|
|
|
return TESTNAME(raise_test_longlong_error);
|
|
}
|
|
|
|
#undef TESTNAME
|
|
#undef TYPENAME
|
|
#undef F_S_TO_PY
|
|
#undef F_PY_TO_S
|
|
#undef F_U_TO_PY
|
|
#undef F_PY_TO_U
|
|
|
|
/* Test the L code for PyArg_ParseTuple. This should deliver a LONG_LONG
|
|
for both long and int arguments. The test may leak a little memory if
|
|
it fails.
|
|
*/
|
|
static PyObject *
|
|
test_L_code(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *tuple, *num;
|
|
LONG_LONG value;
|
|
|
|
if (!PyArg_ParseTuple(args, ":test_L_code"))
|
|
return NULL;
|
|
|
|
tuple = PyTuple_New(1);
|
|
if (tuple == NULL)
|
|
return NULL;
|
|
|
|
num = PyLong_FromLong(42);
|
|
if (num == NULL)
|
|
return NULL;
|
|
|
|
PyTuple_SET_ITEM(tuple, 0, num);
|
|
|
|
value = -1;
|
|
if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0)
|
|
return NULL;
|
|
if (value != 42)
|
|
return raiseTestError("test_L_code",
|
|
"L code returned wrong value for long 42");
|
|
|
|
Py_DECREF(num);
|
|
num = PyInt_FromLong(42);
|
|
if (num == NULL)
|
|
return NULL;
|
|
|
|
PyTuple_SET_ITEM(tuple, 0, num);
|
|
|
|
value = -1;
|
|
if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0)
|
|
return NULL;
|
|
if (value != 42)
|
|
return raiseTestError("test_L_code",
|
|
"L code returned wrong value for int 42");
|
|
|
|
Py_DECREF(tuple);
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
#endif /* ifdef HAVE_LONG_LONG */
|
|
|
|
#ifdef Py_USING_UNICODE
|
|
|
|
/* Test the u and u# codes for PyArg_ParseTuple. May leak memory in case
|
|
of an error.
|
|
*/
|
|
static PyObject *
|
|
test_u_code(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *tuple, *obj;
|
|
Py_UNICODE *value;
|
|
int len;
|
|
|
|
if (!PyArg_ParseTuple(args, ":test_u_code"))
|
|
return NULL;
|
|
|
|
tuple = PyTuple_New(1);
|
|
if (tuple == NULL)
|
|
return NULL;
|
|
|
|
obj = PyUnicode_Decode("test", strlen("test"),
|
|
"ascii", NULL);
|
|
if (obj == NULL)
|
|
return NULL;
|
|
|
|
PyTuple_SET_ITEM(tuple, 0, obj);
|
|
|
|
value = 0;
|
|
if (PyArg_ParseTuple(tuple, "u:test_u_code", &value) < 0)
|
|
return NULL;
|
|
if (value != PyUnicode_AS_UNICODE(obj))
|
|
return raiseTestError("test_u_code",
|
|
"u code returned wrong value for u'test'");
|
|
value = 0;
|
|
if (PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len) < 0)
|
|
return NULL;
|
|
if (value != PyUnicode_AS_UNICODE(obj) ||
|
|
len != PyUnicode_GET_SIZE(obj))
|
|
return raiseTestError("test_u_code",
|
|
"u# code returned wrong values for u'test'");
|
|
|
|
Py_DECREF(tuple);
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
#endif
|
|
|
|
static PyObject *
|
|
raise_exception(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *exc;
|
|
PyObject *exc_args, *v;
|
|
int num_args, i;
|
|
|
|
if (!PyArg_ParseTuple(args, "Oi:raise_exception",
|
|
&exc, &num_args))
|
|
return NULL;
|
|
|
|
exc_args = PyTuple_New(num_args);
|
|
if (exc_args == NULL)
|
|
return NULL;
|
|
for (i = 0; i < num_args; ++i) {
|
|
v = PyInt_FromLong(i);
|
|
if (v == NULL) {
|
|
Py_DECREF(exc_args);
|
|
return NULL;
|
|
}
|
|
PyTuple_SET_ITEM(exc_args, i, v);
|
|
}
|
|
PyErr_SetObject(exc, exc_args);
|
|
return NULL;
|
|
}
|
|
|
|
static PyMethodDef TestMethods[] = {
|
|
{"raise_exception", raise_exception, METH_VARARGS},
|
|
{"test_config", test_config, METH_VARARGS},
|
|
{"test_list_api", test_list_api, METH_VARARGS},
|
|
{"test_dict_iteration", test_dict_iteration, METH_VARARGS},
|
|
{"test_long_api", test_long_api, METH_VARARGS},
|
|
#ifdef HAVE_LONG_LONG
|
|
{"test_longlong_api", test_longlong_api, METH_VARARGS},
|
|
{"test_L_code", test_L_code, METH_VARARGS},
|
|
#endif
|
|
#ifdef Py_USING_UNICODE
|
|
{"test_u_code", test_u_code, METH_VARARGS},
|
|
#endif
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
DL_EXPORT(void)
|
|
init_testcapi(void)
|
|
{
|
|
PyObject *m, *d;
|
|
|
|
m = Py_InitModule("_testcapi", TestMethods);
|
|
|
|
TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
|
|
d = PyModule_GetDict(m);
|
|
PyDict_SetItemString(d, "error", TestError);
|
|
}
|