Skip to content

Commit

Permalink
pythongh-93649: Split _testcapimodule.c into smaller files
Browse files Browse the repository at this point in the history
* Move many functions from _testcapimodule.c into more specific files
  in Modules/_testcapi/.

* Add files:

  * Modules/_testcapi/frame.c
  * Modules/_testcapi/function.c
  * Modules/_testcapi/type.c

* In moved code:

  * Replace get_testerror() with PyExc_AssertionError.
  * Replace raiseTestError() with
    PyErr_Format(PyExc_AssertionError, ...).
  • Loading branch information
vstinner committed Jan 31, 2025
1 parent 8df5193 commit 78d4204
Show file tree
Hide file tree
Showing 18 changed files with 1,337 additions and 1,257 deletions.
56 changes: 56 additions & 0 deletions Lib/test/test_capi/test_frame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import sys
import unittest
from test.support import import_helper


_testcapi = import_helper.import_module('_testcapi')


class TestCAPI(unittest.TestCase):
def getframe(self):
return sys._getframe()

def test_frame_getters(self):
frame = self.getframe()
self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))

def test_getvar(self):
current_frame = sys._getframe()
x = 1
self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
with self.assertRaises(NameError):
_testcapi.frame_getvar(current_frame, "y")
with self.assertRaises(NameError):
_testcapi.frame_getvarstring(current_frame, b"y")

# wrong name type
with self.assertRaises(TypeError):
_testcapi.frame_getvar(current_frame, b'x')
with self.assertRaises(TypeError):
_testcapi.frame_getvar(current_frame, 123)

def getgenframe(self):
yield sys._getframe()

def test_frame_get_generator(self):
gen = self.getgenframe()
frame = next(gen)
self.assertIs(gen, _testcapi.frame_getgenerator(frame))

def test_frame_fback_api(self):
"""Test that accessing `f_back` does not cause a segmentation fault on
a frame created with `PyFrame_New` (GH-99110)."""
def dummy():
pass

frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
# The following line should not cause a segmentation fault.
self.assertIsNone(frame.f_back)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ def check_negative_refcount(self, code):
code = textwrap.dedent(code)
rc, out, err = assert_python_failure('-c', code)
self.assertRegex(err,
br'_testcapimodule\.c:[0-9]+: '
br'object\.c:[0-9]+: '
br'_Py_NegativeRefcount: Assertion failed: '
br'object has negative ref count')

Expand Down
49 changes: 0 additions & 49 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
import threading
import unittest
import weakref
try:
import _testcapi
except ImportError:
_testcapi = None

from collections.abc import Mapping
from test import support
Expand Down Expand Up @@ -773,51 +769,6 @@ def f():
self.assertIs(catcher.unraisable.exc_type, TypeError)
self.assertIsNone(weak())

@unittest.skipIf(_testcapi is None, 'need _testcapi')
class TestCAPI(unittest.TestCase):
def getframe(self):
return sys._getframe()

def test_frame_getters(self):
frame = self.getframe()
self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))

def test_getvar(self):
current_frame = sys._getframe()
x = 1
self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
with self.assertRaises(NameError):
_testcapi.frame_getvar(current_frame, "y")
with self.assertRaises(NameError):
_testcapi.frame_getvarstring(current_frame, b"y")

# wrong name type
with self.assertRaises(TypeError):
_testcapi.frame_getvar(current_frame, b'x')
with self.assertRaises(TypeError):
_testcapi.frame_getvar(current_frame, 123)

def getgenframe(self):
yield sys._getframe()

def test_frame_get_generator(self):
gen = self.getgenframe()
frame = next(gen)
self.assertIs(gen, _testcapi.frame_getgenerator(frame))

def test_frame_fback_api(self):
"""Test that accessing `f_back` does not cause a segmentation fault on
a frame created with `PyFrame_New` (GH-99110)."""
def dummy():
pass

frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
# The following line should not cause a segmentation fault.
self.assertIsNone(frame.f_back)

if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/function.c _testcapi/type.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
Expand Down
77 changes: 77 additions & 0 deletions Modules/_testcapi/dict.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,82 @@
#include "parts.h"
#include "util.h"


static int
test_dict_inner(PyObject *self, int count)
{
Py_ssize_t pos = 0, iterations = 0;
int i;
PyObject *dict = PyDict_New();
PyObject *v, *k;

if (dict == NULL)
return -1;

for (i = 0; i < count; i++) {
v = PyLong_FromLong(i);
if (v == NULL) {
goto error;
}
if (PyDict_SetItem(dict, v, v) < 0) {
Py_DECREF(v);
goto error;
}
Py_DECREF(v);
}

k = v = UNINITIALIZED_PTR;
while (PyDict_Next(dict, &pos, &k, &v)) {
PyObject *o;
iterations++;

assert(k != UNINITIALIZED_PTR);
assert(v != UNINITIALIZED_PTR);
i = PyLong_AS_LONG(v) + 1;
o = PyLong_FromLong(i);
if (o == NULL) {
goto error;
}
if (PyDict_SetItem(dict, k, o) < 0) {
Py_DECREF(o);
goto error;
}
Py_DECREF(o);
k = v = UNINITIALIZED_PTR;
}
assert(k == UNINITIALIZED_PTR);
assert(v == UNINITIALIZED_PTR);

Py_DECREF(dict);

if (iterations != count) {
PyErr_SetString(
PyExc_AssertionError,
"test_dict_iteration: dict iteration went wrong ");
return -1;
} else {
return 0;
}
error:
Py_DECREF(dict);
return -1;
}

static PyObject*
test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored))
{
int i;

for (i = 0; i < 200; i++) {
if (test_dict_inner(self, i) < 0) {
return NULL;
}
}

Py_RETURN_NONE;
}


static PyObject *
dict_containsstring(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -182,6 +258,7 @@ dict_popstring_null(PyObject *self, PyObject *args)
}

static PyMethodDef test_methods[] = {
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
{"dict_containsstring", dict_containsstring, METH_VARARGS},
{"dict_getitemref", dict_getitemref, METH_VARARGS},
{"dict_getitemstringref", dict_getitemstringref, METH_VARARGS},
Expand Down
58 changes: 58 additions & 0 deletions Modules/_testcapi/float.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,63 @@
#include "clinic/float.c.h"


/* Test PyOS_string_to_double. */
static PyObject *
test_string_to_double(PyObject *self, PyObject *Py_UNUSED(ignored))
{
double result;
const char *msg;

#define CHECK_STRING(STR, expected) \
do { \
result = PyOS_string_to_double(STR, NULL, NULL); \
if (result == -1.0 && PyErr_Occurred()) { \
return NULL; \
} \
if (result != (double)expected) { \
msg = "conversion of " STR " to float failed"; \
goto fail; \
} \
} while (0)

#define CHECK_INVALID(STR) \
do { \
result = PyOS_string_to_double(STR, NULL, NULL); \
if (result == -1.0 && PyErr_Occurred()) { \
if (PyErr_ExceptionMatches(PyExc_ValueError)) { \
PyErr_Clear(); \
} \
else { \
return NULL; \
} \
} \
else { \
msg = "conversion of " STR " didn't raise ValueError"; \
goto fail; \
} \
} while (0)

CHECK_STRING("0.1", 0.1);
CHECK_STRING("1.234", 1.234);
CHECK_STRING("-1.35", -1.35);
CHECK_STRING(".1e01", 1.0);
CHECK_STRING("2.e-2", 0.02);

CHECK_INVALID(" 0.1");
CHECK_INVALID("\t\n-3");
CHECK_INVALID(".123 ");
CHECK_INVALID("3\n");
CHECK_INVALID("123abc");

Py_RETURN_NONE;
fail:
PyErr_Format(PyExc_AssertionError, "test_string_to_double: %s", msg);
return NULL;
#undef CHECK_STRING
#undef CHECK_INVALID
}


/*[clinic input]
module _testcapi
[clinic start generated code]*/
Expand Down Expand Up @@ -100,6 +157,7 @@ _testcapi_float_unpack_impl(PyObject *module, const char *data,
}

static PyMethodDef test_methods[] = {
{"test_string_to_double", test_string_to_double, METH_NOARGS},
_TESTCAPI_FLOAT_PACK_METHODDEF
_TESTCAPI_FLOAT_UNPACK_METHODDEF
{NULL},
Expand Down
Loading

0 comments on commit 78d4204

Please sign in to comment.