-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
gh-145119: Allow frozendict to be assigned to instance __dict__ #145123
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Allow :class:`frozendict` to be assigned to an instance's | ||
| :attr:`~object.__dict__`, enabling immutable instances. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3999,10 +3999,10 @@ subtype_dict(PyObject *obj, void *context) | |
| int | ||
| _PyObject_SetDict(PyObject *obj, PyObject *value) | ||
| { | ||
| if (value != NULL && !PyDict_Check(value)) { | ||
| if (value != NULL && !PyAnyDict_Check(value)) { | ||
| PyErr_Format(PyExc_TypeError, | ||
| "__dict__ must be set to a dictionary, " | ||
| "not a '%.200s'", Py_TYPE(value)->tp_name); | ||
| "__dict__ must be set to a dict or frozendict, " | ||
| "not a %T", value); | ||
| return -1; | ||
| } | ||
| if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | ||
|
|
@@ -8305,15 +8305,24 @@ object___dir___impl(PyObject *self) | |
| if (dict == NULL) { | ||
| dict = PyDict_New(); | ||
| } | ||
| else if (!PyDict_Check(dict)) { | ||
| Py_DECREF(dict); | ||
| dict = PyDict_New(); | ||
| } | ||
| else { | ||
| else if (PyDict_Check(dict)) { | ||
| /* Copy __dict__ to avoid mutating it. */ | ||
| PyObject *temp = PyDict_Copy(dict); | ||
| Py_SETREF(dict, temp); | ||
| } | ||
| else if (PyFrozenDict_Check(dict)) { | ||
| /* Convert frozendict to a mutable dict for merging. */ | ||
|
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. Please wait for #145517. If PyDict_Copy() is modified to always return a dict, you can reuse the PyDict_Check() branch for frozendict.
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. For now, you can use |
||
| PyObject *temp = PyDict_New(); | ||
| if (temp != NULL && PyDict_Update(temp, dict) < 0) { | ||
| Py_DECREF(temp); | ||
| temp = NULL; | ||
| } | ||
| Py_SETREF(dict, temp); | ||
| } | ||
| else { | ||
| Py_DECREF(dict); | ||
| dict = PyDict_New(); | ||
| } | ||
|
|
||
| if (dict == NULL) | ||
| goto error; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allowing
_PyObject_SetDict()to acceptPyAnyDict_Checkhere makesfrozendictassignable to everyPyObject_GenericSetDictuser, but some of those types still perform internal writes withPyDict_SetItem/PyDict_DelItem(for exampledescriptor_get_wrapped_attributeanddescriptor_set_wrapped_attributeinObjects/funcobject.cfor classmethod/staticmethod annotation caching).PyDict_SetItemstill rejects frozendict withPyErr_BadInternalCall, so afterobj.__dict__ = frozendict(...), operations like reading or updating__annotations__can now raiseSystemErrorinstead of behaving normally or reporting an intentional immutability error.Useful? React with 👍 / 👎.