8000 gh-121654: Add PyType_Freeze() function · vstinner/cpython@2d24aba · GitHub
[go: up one dir, main page]

Skip to content

Commit 2d24aba

Browse files
committed
pythongh-121654: Add PyType_Freeze() function
1 parent a1ddaae commit 2d24aba

File tree

11 files changed

+100
-13
lines changed

11 files changed

+100
-13
lines changed

Doc/c-api/type.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,17 @@ The following functions and structs are used to create
381381
:c:member:`~PyTypeObject.tp_new` is deprecated and in Python 3.14+ it
382382
will be no longer allowed.
383383
384+
.. c:function:: int PyType_Freeze(PyTypeObject *type)
385+
386+
Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.
387+
388+
Base classes must be immutable.
389+
390+
On success, return ``0``.
391+
On error, set an exception and return ``-1``.
392+
393+
.. versionadded:: 3.14
394+
384395
.. raw:: html
385396
386397
<!-- Keep old URL fragments working (see gh-97908) -->

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ New Features
472472
an interned string and deallocate it during module shutdown.
473473
(Contribued by Eddie Elizondo in :gh:`113601`.)
474474

475+
* Add :c:func:`PyType_Freeze` function to make a type immutable.
476+
(Contributed by Victor Stinner in :gh:`121654`.)
477+
475478
Porting to Python 3.14
476479
----------------------
477480

Include/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,10 @@ static inline int PyType_CheckExact(PyObject *op) {
786786
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *);
787787
#endif
788788

789+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
790+
PyAPI_FUNC(int) PyType_Freeze(PyTypeObject *type);
791+
#endif
792+
789793
#ifdef __cplusplus
790794
}
791795
#endif

Lib/test/test_capi/test_type.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from test.support import import_helper
2+
import unittest
3+
4+
_testcapi = import_helper.import_module('_testcapi')
5+
6+
7+
class TypeTests(unittest.TestCase):
8+
def test_freeze(self):
9+
# test PyType_Freeze()
10+
type_freeze = _testcapi.type_freeze
11+
12+
class MyType:
13+
pass
14+
MyType.attr = "mutable"
15+
16+
type_freeze(MyType)
17+
with self.assertRaises(TypeError):
18+
MyType.attr = "immutable"
19+
20+
# PyType_Freeze() requires base classes to be immutable
21+
class Mutable:
22+
"mutable base class"
23+
pass
24+
class MyType2(Mutable):
25+
pass
26+
with self.assertRaises(TypeError):
27+
type_freeze(MyType2)

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyType_Freeze` function to make a type immutable. Patch by
2+
Victor Stinner.

Misc/stable_abi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,3 +2510,5 @@
25102510
added = '3.14'
25112511
[function.PyIter_NextItem]
25122512
added = '3.14'
2513+
[function.PyType_Freeze]
2514+
added = '3.14'

Modules/_testcapimodule.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3329,6 +3329,19 @@ test_critical_sections(PyObject *module, PyObject *Py_UNUSED(args))
33293329
Py_RETURN_NONE;
33303330
}
33313331

3332+
static PyObject *
3333+
type_freeze(PyObject *module, PyObject *args)
3334+
{
3335+
PyTypeObject *type;
3336+
if (!PyArg_ParseTuple(args, "O!", &PyType_Type, &type)) {
3337+
return NULL;
3338+
}
3339+
if (PyType_Freeze(type) < 0) {
3340+
return NULL;
3341+
}
3342+
Py_RETURN_NONE;
3343+
}
3344+
33323345
static PyMethodDef TestMethods[] = {
33333346
{"set_errno", set_errno, METH_VARARGS},
33343347
{"test_config", test_config, METH_NOARGS},
@@ -3469,6 +3482,7 @@ static PyMethodDef TestMethods[] = {
34693482
{"test_weakref_capi", test_weakref_capi, METH_NOARGS},
34703483
{"function_set_warning", function_set_warning, METH_NOARGS},
34713484
{"test_critical_sections", test_critical_sections, METH_NOARGS},
3485+
{"type_freeze", type_freeze, METH_VARARGS},
34723486
{NULL, NULL} /* sentinel */
34733487
};
34743488

Objects/typeobject.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4642,6 +4642,26 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type)
46424642
return 1;
46434643
}
46444644

4645+
static int
4646+
check_immutable_bases(const char *type_name, PyObject *bases)
4647+
{
4648+
for (Py_ssize_t i=0; i<PyTuple_GET_SIZE(bases); i++) {
4649+
PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
4650+
if (!b) {
4651+
return -1;
4652+
}
4653+
if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) {
4654+
PyErr_Format(
4655+
PyExc_TypeError,
4656+
"Creating immutable type %s from mutable base %N",
4657+
type_name, b
4658+
);
4659+
return -1;
4660+
}
4661+
}
4662+
return 0;
4663+
}
4664+
46454665
static PyObject *
46464666
_PyType_FromMetaclass_impl(
46474667
PyTypeObject *metaclass, PyObject *module,
@@ -4798,19 +4818,8 @@ _PyType_FromMetaclass_impl(
47984818
* and only heap types can be mutable.)
47994819
*/
48004820
if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) {
4801-
for (int i=0; i<PyTuple_GET_SIZE(bases); i++) {
4802-
PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
4803-
if (!b) {
4804-
goto finally;
4805-
}
4806-
if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) {
4807-
PyErr_Format(
4808-
PyExc_TypeError,
4809-
"Creating immutable type %s from mutable base %N",
4810-
spec->name, b
4811-
);
4812-
goto finally;
4813-
}
4821+
if (check_immutable_bases(spec->name, bases) < 0) {
4822+
goto finally;
48144823
}
48154824
}
48164825

@@ -11178,6 +11187,18 @@ add_operators(PyTypeObject *type, PyTypeObject *def)
1117811187
}
1117911188

1118011189

11190+
int
11191+
PyType_Freeze(PyTypeObject *type)
11192+
{
11193+
PyObject *bases = type->tp_bases;
11194+
if (check_immutable_bases(type->tp_name, bases) < 0) {
11195+
return -1;
11196+
}
11197+
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
11198+
return 0;
11199+
}
11200+
11201+
1118111202
/* Cooperative 'super' */
1118211203

1118311204
typedef struct {

PC/python3dll.c

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
0