summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/cpython/object.h1
-rw-r--r--Lib/test/test_type_cache.py13
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst2
-rw-r--r--Objects/typeobject.c7
4 files changed, 22 insertions, 1 deletions
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index c939316..7512bb7 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -229,6 +229,7 @@ struct _typeobject {
/* bitset of which type-watchers care about this type */
unsigned char tp_watched;
+ uint16_t tp_versions_used;
};
/* This struct is used by the specializer
diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py
index 295df78..58572c6 100644
--- a/Lib/test/test_type_cache.py
+++ b/Lib/test/test_type_cache.py
@@ -79,6 +79,19 @@ class TypeCacheTests(unittest.TestCase):
_clear_type_cache()
+ def test_per_class_limit(self):
+ class C:
+ x = 0
+
+ type_assign_version(C)
+ orig_version = type_get_version(C)
+ for i in range(1001):
+ C.x = i
+ type_assign_version(C)
+
+ new_version = type_get_version(C)
+ self.assertEqual(new_version, 0)
+
@support.cpython_only
class TypeCacheWithSpecializationTests(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst
new file mode 100644
index 0000000..1a401ec
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst
@@ -0,0 +1,2 @@
+Limit the number of versions that a single class can use. Prevents a few
+wayward classes using up all the version numbers.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index a850473..e220d10 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -908,6 +908,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
}
}
+#define MAX_VERSIONS_PER_CLASS 1000
+
static int
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
{
@@ -922,7 +924,10 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
return 0;
}
-
+ if (type->tp_versions_used >= MAX_VERSIONS_PER_CLASS) {
+ return 0;
+ }
+ type->tp_versions_used++;
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
/* static types */
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {