8000 gh-93649: Split _testcapimodule.c into smaller files by vstinner · Pull Request #129400 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content
/ cpython Public

gh-93649: Split _testcapimodule.c into smaller files #129400

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
gh-93649: Split _testcapimodule.c into smaller files
* 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
commit 78d4204ae02f67b41130de5e1323ac9b8d5df456
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 _testca 8000 pi/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
Loading
0