| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
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
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,66 @@ | ||
| 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 | ||
|
|
||
| # simple case, no inherante | ||
| class MyType: | ||
| pass | ||
| MyType.attr = "mutable" | ||
|
|
||
| type_freeze(MyType) | ||
| err_msg = "cannot set 'attr' attribute of immutable type 'MyType'" | ||
| with self.assertRaisesRegex(TypeError, err_msg): | ||
| # the class is now immutable | ||
| MyType.attr = "immutable" | ||
|
|
||
| # test MRO: PyType_Freeze() requires base classes to be immutable | ||
| class A: pass | ||
| class B: pass | ||
| class C(B): pass | ||
| class D(A, C): pass | ||
|
|
||
| self.assertEqual(D.mro(), [D, A, C, B, object]) | ||
| with self.assertRaises(TypeError): | ||
| type_freeze(D) | ||
|
|
||
| type_freeze(A) | ||
| type_freeze(B) | ||
| type_freeze(C) | ||
| # all parent classes are now immutable, so D can be made immutable | ||
| # as well | ||
| type_freeze(D) | ||
|
Comment thread
vstinner marked this conversation as resolved.
|
||
|
|
||
| def test_freeze_meta(self): | ||
| """test PyType_Freeze() with overridden MRO""" | ||
| type_freeze = _testcapi.type_freeze | ||
|
|
||
| class Base: | ||
| value = 1 | ||
|
|
||
| class Meta(type): | ||
| def mro(cls): | ||
| return (cls, Base, object) | ||
|
|
||
| class FreezeThis(metaclass=Meta): | ||
| """This has `Base` in the MRO, but not tp_bases""" | ||
|
|
||
| self.assertEqual(FreezeThis.value, 1) | ||
|
|
||
| with self.assertRaises(TypeError): | ||
| type_freeze(FreezeThis) | ||
|
|
||
| Base.value = 2 | ||
| self.assertEqual(FreezeThis.value, 2) | ||
|
|
||
| type_freeze(Base) | ||
| with self.assertRaises(TypeError): | ||
| Base.value = 3 | ||
| type_freeze(FreezeThis) | ||
| self.assertEqual(FreezeThis.value, 2) | ||
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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -2538,3 +2538,5 @@ | |
| added = '3.14' | ||
| [function.PyUnicode_Equal] | ||
| added = '3.14' | ||
| [function.PyType_Freeze] | ||
| added = '3.14' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -4637,6 +4637,32 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type) | |
| return 1; | ||
| } | ||
|
|
||
| static int | ||
| check_immutable_bases(const char *type_name, PyObject *bases, int skip_first) | ||
| { | ||
| Py_ssize_t i = 0; | ||
| if (skip_first) { | ||
| // When testing the MRO, skip the type itself | ||
| i = 1; | ||
| } | ||
| for (; i<PyTuple_GET_SIZE(bases); i++) { | ||
| PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i); | ||
|
Comment thread
Copy link
Copy Markdown
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualitySorry for the necro-posting.
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI don't know, I just moved the code. The test was already there before.
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityGot it, thanks!
Sorry, something went wrong.
All reactions
|
||
| 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; | ||
| } | ||
|
|
||
|
|
||
| /* Set *dest to the offset specified by a special "__*offset__" member. | ||
| * Return 0 on success, -1 on failure. | ||
| */ | ||
| Expand Down Expand Up | @@ -4820,19 +4846,8 @@ PyType_FromMetaclass( | |
| * 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) < 0) { | ||
| goto finally; | ||
| } | ||
| } | ||
|
|
||
| Expand Down Expand Up | @@ -11319,6 +11334,30 @@ add_operators(PyTypeObject *type) | |
| } | ||
|
|
||
|
|
||
| int | ||
| PyType_Freeze(PyTypeObject *type) | ||
| { | ||
| // gh-121654: Check the __mro__ instead of __bases__ | ||
| PyObject *mro = type_get_mro(type, NULL); | ||
| if (!PyTuple_Check(mro)) { | ||
| Py_DECREF(mro); | ||
| PyErr_SetString(PyExc_TypeError, "unable to get the type MRO"); | ||
| return -1; | ||
| } | ||
|
Comment thread
Comment on lines
+11342
to
+11346
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityHow difficult to add test for this case?
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI'm not sure that it's possible to create a type with a MRO which is not a tuple. Using a metaclass, it's possible to override the MRO, but internally, Python converts mro() result into a tuple. I added this check since type_get_mro() returns None if type->tp_mro is NULL. But I have no idea how to create a type with type->tp_mro=NULL. It's more a sanity check.
Sorry, something went wrong.
All reactions
|
||
|
|
||
| int check = check_immutable_bases(type->tp_name, mro, 1); | ||
| Py_DECREF(mro); | ||
| if (check < 0) { | ||
| return -1; | ||
| } | ||
|
|
||
| type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; | ||
| PyType_Modified(type); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| /* Cooperative 'super' */ | ||
|
|
||
| typedef struct { | ||
| Expand Down | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Back | FazBrowse Home | New Git URL |
Uh oh!
There was an error while loading. Please reload this page.