8000 gh-121654: Add PyType_Freeze() function by vstinner · Pull Request #122457 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-121654: Add PyType_Freeze() function #122457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 25, 2024
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-121654: Add PyType_Freeze() function
  • Loading branch information
vstinner committed Aug 26, 2024
commit 2d24aba77bc9624563e6eb4543051a1a8b682a23
11 changes: 11 additions & 0 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,17 @@ The following functions and structs are used to create
:c:member:`~PyTypeObject.tp_new` is deprecated and in Python 3.14+ it
will be no longer allowed.

.. c:function:: int PyType_Freeze(PyTypeObject *type)

Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.

Base classes must be immutable.

On success, return ``0``.
On error, set an exception and return ``-1``.

.. versionadded:: 3.14

.. raw:: html

<!-- Keep old URL fragments working (see gh-97908) -->
Expand Down
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ New Features
an interned string and deallocate it during module shutdown.
(Contribued by Eddie Elizondo in :gh:`113601`.)

* Add :c:func:`PyType_Freeze` function to make a type immutable.
(Contributed by Victor Stinner in :gh:`121654`.)

Porting to Python 3.14
----------------------

Expand Down
4 changes: 4 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,10 @@ static inline int PyType_CheckExact(PyObject *op) {
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *);
#endif

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
PyAPI_FUNC(int) PyType_Freeze(PyTypeObject *type);
#endif

#ifdef __cplusplus
}
#endif
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_capi/test_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from test.support import import_helper
import unittest

_testcapi = import_helper.import_module('_testcapi')


class TypeTests(unittest.TestCase):
def test_freeze(self):
# test PyType_Freeze()
type_freeze = _testcapi.type_freeze

class MyType:
pass
MyType.attr = "mutable"

type_freeze(MyType)
with self.assertRaises(TypeError):
MyType.attr = "immutable"

# PyType_Freeze() requires base classes to be immutable
class Mutable:
"mutable base class"
pass
class MyType2(Mutable):
pass
with self.assertRaises(TypeError):
type_freeze(MyType2)
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyType_Freeze` function to make a type immutable. Patch by
Victor Stinner.
2 changes: 2 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2510,3 +2510,5 @@
added = '3.14'
[function.PyIter_NextItem]
added = '3.14'
[function.PyType_Freeze]
added = '3.14'
14 changes: 14 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3329,6 +3329,19 @@ test_critical_sections(PyObject *module, PyObject *Py_UNUSED(args))
Py_RETURN_NONE;
}

static PyObject *
type_freeze(PyObject *module, PyObject *args)
{
PyTypeObject *type;
if (!PyArg_ParseTuple(args, "O!", &PyType_Type, &type)) {
return NULL;
}
if (PyType_Freeze(type) < 0) {
return NULL;
}
Py_RETURN_NONE;
}

static PyMethodDef TestMethods[] = {
{"set_errno", set_errno, METH_VARARGS},
{"test_config", test_config, METH_NOARGS},
Expand Down Expand Up @@ -3469,6 +3482,7 @@ static PyMethodDef TestMethods[] = {
{"test_weakref_capi", test_weakref_capi, METH_NOARGS},
{"function_set_warning", function_set_warning, METH_NOARGS},
{"test_critical_sections", test_critical_sections, METH_NOARGS},
{"type_freeze", type_freeze, METH_VARARGS},
{NULL, NULL} /* sentinel */
};

Expand Down
47 changes: 34 additions & 13 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4642,6 +4642,26 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type)
return 1;
}

static int
check_immutable_bases(const char *type_name, PyObject *bases)
{
for (Py_ssize_t i=0; i<PyTuple_GET_SIZE(bases); i++) {
PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the necro-posting.
IIUC only improperly constructed tuples can store NULL items. Is this such a case? If so, is the following check redundant?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know, I just moved the code. The test was already there before.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it, thanks!

if (!b) {
return -1;
}
if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) {
PyErr_Format(
PyExc_TypeError,
"Creating immutable type %s from mutable base %N",
type_name, b
);
return -1;
}
}
return 0;
}

static PyObject *
_PyType_FromMetaclass_impl(
PyTypeObject *metaclass, PyObject *module,
Expand Down Expand Up @@ -4798,19 +4818,8 @@ _PyType_FromMetaclass_impl(
* and only heap types can be mutable.)
*/
if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) {
for (int i=0; i<PyTuple_GET_SIZE(bases); i++) {
PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
if (!b) {
goto finally;
}
if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) {
PyErr_Format(
PyExc_TypeError,
"Creating immutable type %s from mutable base %N",
spec->name, b
);
goto finally;
}
if (check_immutable_bases(spec->name, bases) < 0) {
goto finally;
}
}

Expand Down Expand Up @@ -11178,6 +11187,18 @@ add_operators(PyTypeObject *type, PyTypeObject *def)
}


int
PyType_Freeze(PyTypeObject *type)
{
PyObject *bases = type->tp_bases;
if (check_immutable_bases(type->tp_name, bases) < 0) {
return -1;
}
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
return 0;
}


/* Cooperative 'super' */

typedef struct {
Expand Down
1 change: 1 addition & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0