From 3b329c684aa924f539f29486723dbb490e03ed83 Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Tue, 28 Mar 2023 12:27:28 -0700 Subject: [PATCH 1/3] gh-103091: Add PyType_AssignVersionTag --- Doc/c-api/type.rst | 9 +++++++++ Include/object.h | 9 +++++++++ Lib/test/test_type_cache.py | 15 ++++++++++++++- ...2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst | 1 + Modules/_testcapimodule.c | 13 +++++++++++++ Objects/typeobject.c | 4 ++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 7b5d1fac40ed87..5a8ff5295c3fe0 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -232,6 +232,15 @@ Type Objects .. versionadded:: 3.11 +.. c:function:: int PyType_AssignVersionTag(PyTypeObject *type) + + Attempt to assign a version tag to the given type. + + Returns 1 if the type already had a valid version tag or a new one was + assigned, or 0 if a new tag could not be assigned. + + .. versionadded:: 3.12 + Creating Heap-Allocated Types ............................. diff --git a/Include/object.h b/Include/object.h index 2943a6066818cd..ea983e4e4aae30 100644 --- a/Include/object.h +++ b/Include/object.h @@ -861,6 +861,15 @@ static inline int PyType_CheckExact(PyObject *op) { # define PyType_CheckExact(op) PyType_CheckExact(_PyObject_CAST(op)) #endif +#if !defined(Py_LIMITED_API) +/* Attempt to assign a version tag to the given type. + * + * Returns 1 if the type already had a valid version tag or a new one was + * assigned, or 0 if a new tag could not be assigned. + */ +PyAPI_FUNC(int) PyType_AssignVersionTag(PyTypeObject *type); +#endif + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py index 8502f6b0584b00..0ca82cc2fb0a62 100644 --- a/Lib/test/test_type_cache.py +++ b/Lib/test/test_type_cache.py @@ -9,7 +9,7 @@ # Skip this test if the _testcapi module isn't available. type_get_version = import_helper.import_module('_testcapi').type_get_version - +type_assign_version = import_helper.import_module('_testcapi').type_assign_version @support.cpython_only @unittest.skipIf(_clear_type_cache is None, "requires sys._clear_type_cache") @@ -42,6 +42,19 @@ def test_tp_version_tag_unique(self): self.assertEqual(len(set(all_version_tags)), 30, msg=f"{all_version_tags} contains non-unique versions") + def test_type_assign_version(self): + class C: + x = 5 + + self.assertEqual(type_assign_version(C), 1) + c_ver = type_get_version(C) + + C.x = 6 + self.assertEqual(type_get_version(C), 0) + self.assertEqual(type_assign_version(C), 1) + self.assertNotEqual(type_get_version(C), 0) + self.assertNotEqual(type_get_version(C), c_ver) + if __name__ == "__main__": support.run_unittest(TypeCacheTests) diff --git a/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst new file mode 100644 index 00000000000000..fd746ccc02c6ef --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst @@ -0,0 +1 @@ +Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyType_AssignVersionTag()``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3d9a2aeeb7cfd5..183354adc67a88 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2733,6 +2733,18 @@ type_get_version(PyObject *self, PyObject *type) } +static PyObject * +type_assign_version(PyObject *self, PyObject *type) +{ + if (!PyType_Check(type)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + int res = PyType_AssignVersionTag((PyTypeObject *)type); + return PyLong_FromLong(res); +} + + // Test PyThreadState C API static PyObject * test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args)) @@ -3499,6 +3511,7 @@ static PyMethodDef TestMethods[] = { {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, + {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyType_AssignVersionTag")}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"frame_getlocals", frame_getlocals, METH_O, NULL}, {"frame_getglobals", frame_getglobals, METH_O, NULL}, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 69e84743f13aac..b3564e08be9363 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -598,6 +598,10 @@ assign_version_tag(PyTypeObject *type) return 1; } +int PyType_AssignVersionTag(PyTypeObject *type) +{ + return assign_version_tag(type); +} static PyMemberDef type_members[] = { {"__basicsize__", T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),READONLY}, From 840f6c9b508fa1a6eaf12991ed4f606108de1ac4 Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Fri, 31 Mar 2023 11:17:39 -0700 Subject: [PATCH 2/3] Rename to PyUnstable_AssignVersionTag, move to Include/cptyhon/object.h, fix some newlines --- Doc/c-api/type.rst | 2 +- Include/cpython/object.h | 7 +++++++ Include/object.h | 9 --------- Lib/test/test_type_cache.py | 1 + .../C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst | 2 +- Modules/_testcapimodule.c | 4 ++-- Objects/typeobject.c | 3 ++- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 5a8ff5295c3fe0..832dd346a36193 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -232,7 +232,7 @@ Type Objects .. versionadded:: 3.11 -.. c:function:: int PyType_AssignVersionTag(PyTypeObject *type) +.. c:function:: int PyUnstable_AssignVersionTag(PyTypeObject *type) Attempt to assign a version tag to the given type. diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 859ffb91e223dc..419e20a838b7bd 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -553,3 +553,10 @@ PyAPI_FUNC(int) PyType_AddWatcher(PyType_WatchCallback callback); PyAPI_FUNC(int) PyType_ClearWatcher(int watcher_id); PyAPI_FUNC(int) PyType_Watch(int watcher_id, PyObject *type); PyAPI_FUNC(int) PyType_Unwatch(int watcher_id, PyObject *type); + +/* Attempt to assign a version tag to the given type. + * + * Returns 1 if the type already had a valid version tag or a new one was + * assigned, or 0 if a new tag could not be assigned. + */ +PyAPI_FUNC(int) PyUnstable_AssignVersionTag(PyTypeObject *type); diff --git a/Include/object.h b/Include/object.h index ea983e4e4aae30..2943a6066818cd 100644 --- a/Include/object.h +++ b/Include/object.h @@ -861,15 +861,6 @@ static inline int PyType_CheckExact(PyObject *op) { # define PyType_CheckExact(op) PyType_CheckExact(_PyObject_CAST(op)) #endif -#if !defined(Py_LIMITED_API) -/* Attempt to assign a version tag to the given type. - * - * Returns 1 if the type already had a valid version tag or a new one was - * assigned, or 0 if a new tag could not be assigned. - */ -PyAPI_FUNC(int) PyType_AssignVersionTag(PyTypeObject *type); -#endif - #ifdef __cplusplus } #endif diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py index 0ca82cc2fb0a62..24f83cd3e172c7 100644 --- a/Lib/test/test_type_cache.py +++ b/Lib/test/test_type_cache.py @@ -11,6 +11,7 @@ type_get_version = import_helper.import_module('_testcapi').type_get_version type_assign_version = import_helper.import_module('_testcapi').type_assign_version + @support.cpython_only @unittest.skipIf(_clear_type_cache is None, "requires sys._clear_type_cache") class TypeCacheTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst index fd746ccc02c6ef..2669acfbd4e965 100644 --- a/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst +++ b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst @@ -1 +1 @@ -Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyType_AssignVersionTag()``. +Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyUnstable_AssignVersionTag()``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 183354adc67a88..efc7542b980b0b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2740,7 +2740,7 @@ type_assign_version(PyObject *self, PyObject *type) PyErr_SetString(PyExc_TypeError, "argument must be a type"); return NULL; } - int res = PyType_AssignVersionTag((PyTypeObject *)type); + int res = PyUnstable_AssignVersionTag((PyTypeObject *)type); return PyLong_FromLong(res); } @@ -3511,7 +3511,7 @@ static PyMethodDef TestMethods[] = { {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, - {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyType_AssignVersionTag")}, + {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_AssignVersionTag")}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"frame_getlocals", frame_getlocals, METH_O, NULL}, {"frame_getglobals", frame_getglobals, METH_O, NULL}, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b3564e08be9363..3067f2da83096d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -598,11 +598,12 @@ assign_version_tag(PyTypeObject *type) return 1; } -int PyType_AssignVersionTag(PyTypeObject *type) +int PyUnstable_AssignVersionTag(PyTypeObject *type) { return assign_version_tag(type); } + static PyMemberDef type_members[] = { {"__basicsize__", T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),READONLY}, {"__itemsize__", T_PYSSIZET, offsetof(PyTypeObject, tp_itemsize), READONLY}, From b8383404821972d2bd5020578429686062930b3f Mon Sep 17 00:00:00 2001 From: Brett Simmers Date: Thu, 20 Apr 2023 09:06:19 -0700 Subject: [PATCH 3/3] Rename to PyUnstable_Type_AssignVersionTag --- Doc/c-api/type.rst | 2 +- Include/cpython/object.h | 2 +- .../next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst | 2 +- Modules/_testcapimodule.c | 4 ++-- Objects/typeobject.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 832dd346a36193..69b15296993301 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -232,7 +232,7 @@ Type Objects .. versionadded:: 3.11 -.. c:function:: int PyUnstable_AssignVersionTag(PyTypeObject *type) +.. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type) Attempt to assign a version tag to the given type. diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 419e20a838b7bd..ddfd77298e4c2e 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -559,4 +559,4 @@ PyAPI_FUNC(int) PyType_Unwatch(int watcher_id, PyObject *type); * Returns 1 if the type already had a valid version tag or a new one was * assigned, or 0 if a new tag could not be assigned. */ -PyAPI_FUNC(int) PyUnstable_AssignVersionTag(PyTypeObject *type); +PyAPI_FUNC(int) PyUnstable_Type_AssignVersionTag(PyTypeObject *type); diff --git a/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst index 2669acfbd4e965..28c77b6816af87 100644 --- a/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst +++ b/Misc/NEWS.d/next/C API/2023-03-28-12-31-51.gh-issue-103091.CzZyaZ.rst @@ -1 +1 @@ -Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyUnstable_AssignVersionTag()``. +Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyUnstable_Type_AssignVersionTag()``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index efc7542b980b0b..58de4682e894a2 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2740,7 +2740,7 @@ type_assign_version(PyObject *self, PyObject *type) PyErr_SetString(PyExc_TypeError, "argument must be a type"); return NULL; } - int res = PyUnstable_AssignVersionTag((PyTypeObject *)type); + int res = PyUnstable_Type_AssignVersionTag((PyTypeObject *)type); return PyLong_FromLong(res); } @@ -3511,7 +3511,7 @@ static PyMethodDef TestMethods[] = { {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, - {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_AssignVersionTag")}, + {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"frame_getlocals", frame_getlocals, METH_O, NULL}, {"frame_getglobals", frame_getglobals, METH_O, NULL}, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3067f2da83096d..7c83fc2988b639 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -598,7 +598,7 @@ assign_version_tag(PyTypeObject *type) return 1; } -int PyUnstable_AssignVersionTag(PyTypeObject *type) +int PyUnstable_Type_AssignVersionTag(PyTypeObject *type) { return assign_version_tag(type); }