Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-103091: Add PyType_AssignVersionTag
  • Loading branch information
swtaarrs committed Mar 28, 2023
commit 3b329c684aa924f539f29486723dbb490e03ed83
9 changes: 9 additions & 0 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
.............................
Expand Down
9 changes: 9 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put the definition in Include/cpython/object.h, rather than use this #ifdef here. (See line 825.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/* 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
Expand Down
15 changes: 14 additions & 1 deletion Lib/test/test_type_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a new C-API function to eagerly assign a version tag to a PyTypeObject: ``PyType_AssignVersionTag()``.
13 changes: 13 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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},
Expand Down
4 changes: 4 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down