mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-44914: Add tests for some invariants of tp_version_tag (GH-27774)
This commit is contained in:
parent
62bd97303e
commit
d84d2c4985
2 changed files with 65 additions and 0 deletions
47
Lib/test/test_type_cache.py
Normal file
47
Lib/test/test_type_cache.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
""" Tests for the internal type cache in CPython. """
|
||||
import unittest
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
try:
|
||||
from sys import _clear_type_cache
|
||||
except ImportError:
|
||||
_clear_type_cache = None
|
||||
|
||||
# Skip this test if the _testcapi module isn't available.
|
||||
type_get_version = import_helper.import_module('_testcapi').type_get_version
|
||||
|
||||
|
||||
@support.cpython_only
|
||||
@unittest.skipIf(_clear_type_cache is None, "requires sys._clear_type_cache")
|
||||
class TypeCacheTests(unittest.TestCase):
|
||||
def test_tp_version_tag_unique(self):
|
||||
"""tp_version_tag should be unique assuming no overflow, even after
|
||||
clearing type cache.
|
||||
"""
|
||||
# Check if global version tag has already overflowed.
|
||||
Y = type('Y', (), {})
|
||||
Y.x = 1
|
||||
Y.x # Force a _PyType_Lookup, populating version tag
|
||||
y_ver = type_get_version(Y)
|
||||
# Overflow, or not enough left to conduct the test.
|
||||
if y_ver == 0 or y_ver > 0xFFFFF000:
|
||||
self.skipTest("Out of type version tags")
|
||||
# Note: try to avoid any method lookups within this loop,
|
||||
# It will affect global version tag.
|
||||
all_version_tags = []
|
||||
append_result = all_version_tags.append
|
||||
assertNotEqual = self.assertNotEqual
|
||||
for _ in range(30):
|
||||
_clear_type_cache()
|
||||
X = type('Y', (), {})
|
||||
X.x = 1
|
||||
X.x
|
||||
tp_version_tag_after = type_get_version(X)
|
||||
assertNotEqual(tp_version_tag_after, 0, msg="Version overflowed")
|
||||
append_result(tp_version_tag_after)
|
||||
self.assertEqual(len(set(all_version_tags)), 30,
|
||||
msg=f"{all_version_tags} contains non-unique versions")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
support.run_unittest(TypeCacheTests)
|
|
@ -5575,6 +5575,23 @@ test_fatal_error(PyObject *self, PyObject *args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// type->tp_version_tag
|
||||
static PyObject *
|
||||
type_get_version(PyObject *self, PyObject *type)
|
||||
{
|
||||
if (!PyType_Check(type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "argument must be a type");
|
||||
return NULL;
|
||||
}
|
||||
PyObject *res = PyLong_FromUnsignedLong(
|
||||
((PyTypeObject *)type)->tp_version_tag);
|
||||
if (res == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
return NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
||||
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
|
||||
|
@ -5854,6 +5871,7 @@ static PyMethodDef TestMethods[] = {
|
|||
{"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
|
||||
{"fatal_error", test_fatal_error, METH_VARARGS,
|
||||
PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")},
|
||||
{"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue