Skip to content

gh-145119: Allow frozendict to be assigned to instance __dict__ - #145123

Closed
mohsinm-dev wants to merge 2 commits into
python:mainfrom
mohsinm-dev:gh-145119-frozendict-instance-dict
Closed

gh-145119: Allow frozendict to be assigned to instance __dict__#145123
mohsinm-dev wants to merge 2 commits into
python:mainfrom
mohsinm-dev:gh-145119-frozendict-instance-dict

Conversation

@mohsinm-dev

@mohsinm-devmohsinm-dev commented Feb 22, 2026

Copy link
Copy Markdown
Contributor

Allow frozendict (and its subclasses) to be assigned to an instance's __dict__, enabling immutable instances.

Changes:

  • _PyObject_SetDict: accept frozendict via PyAnyDict_Check
  • _PyDict_SetItem_LockHeld: raise TypeError on set/delete instead of SystemError
  • object___dir___impl: convert frozendict to a mutable dict for merging

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6b702cf72b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadObjects/typeobject.c
_PyObject_SetDict(PyObject *obj, PyObject *value)
{
if (value != NULL && !PyDict_Check(value)) {
if (value != NULL && !PyAnyDict_Check(value)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid frozendict dict on objects mutating their own dict

Allowing _PyObject_SetDict() to accept PyAnyDict_Check here makes frozendict assignable to every PyObject_GenericSetDict user, but some of those types still perform internal writes with PyDict_SetItem/PyDict_DelItem (for example descriptor_get_wrapped_attribute and descriptor_set_wrapped_attribute in Objects/funcobject.c for classmethod/staticmethod annotation caching). PyDict_SetItem still rejects frozendict with PyErr_BadInternalCall, so after obj.__dict__ = frozendict(...), operations like reading or updating __annotations__ can now raise SystemError instead of behaving normally or reporting an intentional immutability error.

Useful? React with 👍 / 👎.

@mohsinm-dev
mohsinm-devforce-pushed the gh-145119-frozendict-instance-dict branch from 6b702cf to 8d6ec8dCompareFebruary 22, 2026 19:22
@skirpichev

Copy link
Copy Markdown
Member

In order to keep the commit history intact, please avoid squashing or amending history and then force-pushing to the PR. Reviewers often want to look at individual commits. When the PR is merged, everything will be squashed into a single commit.

Comment threadObjects/typeobject.c
Py_SETREF(dict, temp);
}
else if (PyFrozenDict_Check(dict)) {
/* Convert frozendict to a mutable dict for merging. */

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.

Please wait for #145517. If PyDict_Copy() is modified to always return a dict, you can reuse the PyDict_Check() branch for frozendict.

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.

For now, you can use _PyDict_CopyAsDict() from Include/internal/pycore_dict.h.

Comment threadObjects/typeobject.c Outdated
Comment on lines 4004 to 4005
"__dict__ must be set to a dictionary, "
"not a '%.200s'", Py_TYPE(value)->tp_name);

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.

Suggested change
"__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);

@vstinner

Copy link
Copy Markdown
Member

_PyDict_SetItem_LockHeld: raise TypeError on set/delete instead of SystemError

I extracted these changes from your PR and created a new PR based on it: PR gh-145564.

@StanFromIreland

Copy link
Copy Markdown
Member

The attached issue was closed.

@mohsinm-dev
mohsinm-dev deleted the gh-145119-frozendict-instance-dict branch March 9, 2026 01:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mohsinm-dev@skirpichev@vstinner@StanFromIreland