You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Python has a private_PyObject_GetDictPtr() function which is used by 30 projects of the PyPI top 15,000 projects. It has multiple issues:
It returns a PyObject** type (pointer to an object) which is an unusual type.
It ignores silently (memory allocation) errors: very bad :-(
Using the PyObject** pointer to set an object dictionary is dangerous, because assigning directly to the pointer returned by this function could result in the object and its __dict__ getting out of sync.
Using the PyObject** pointer to set or clear the object dictionary is unsafe with free threading. Free-threading stores and reads dictionary pointer atomically.
The function name is misleading as it suggests that it is getting a pointer, without side effects, and that pointer can be written to. It also perpetuates the idea that all objects have a dictionary (at the C level), and that accessing that dictionary is cheap.
It's private :-)
I propose adding a public PyObject_GetDict() function to get an object __dict__ dictionary.
API: int PyObject_GetDict(PyObject *obj, PyObject **dict)
Get a pointer to __dict__ of the object obj.
If there is a __dict__, set *dict to a strong reference to the dictionary and return 1.
If there is no __dict__, set *dict to NULL without setting an exception and return 0.
On error, set an exception and return -1.
The function is similar to the existing PyObject* PyObject_GenericGetDict(PyObject *o, void *context) function, but it does not raise an exception if the object has no __dict__.
The idea is to replace _PyObject_GetDictPtr() usage with PyObject_GetDict(). Other possible replacements are:
At the moment I frequently see Rust / PyO3 code do the equivalent of getattr(obj, "__dict__") (listed as one of your replacements). Is the motivation for this specialized API performance?
Would PyObject_GetDict() materialize the dict if an object uses Py_TPFLAGS_INLINE_VALUES?
I don't have a strong opinion about this function, but I looked at a few random projects listed on the issue and this would not replace the usages I saw:
frozendict: Usage of _PyObject_GetDictPtr is verbatim dictobject.c code copied from CPython 3.7-3.10 pybind11: Calls Py_CLEAR() on the field - can't use PyObject_GetDict cvxpy: no direct uses (uses pybind11) catboost: Calls Py_CLEAR on the pointer
__Pyx_PyObject_GetMethod looks like it's only used when compiling for older versions of CPython and ma_version_tag (__PYX_GET_DICT_VERSION) is pending removal.
I really need to be more persuaded that PyObject_GetAttributeString(obj, "__dict__") isn't a good enough alternative.
"It might not have a dict" - then it's not your own type and you shouldn't be messing with its internals. Just use its attributes normally.
What are the use cases for clearing the dict? Are they uses that we want to encourage, or should be we trying to lead people into better ways of achieving the same thing? (All I can think of is doing a type transform on an already-constructed object, which I don't think we want to encourage...)
Also, _PyObject_GetDictPtr is a documented API. Wouldn't it be better to just make it public with minor changes (make it raise an exception on memory errors, fix the return type to ensure that the returned pointer is not writable)
How is this different from PyObject_GenericGetDict?
"The function is similar to the existing PyObject* PyObject_GenericGetDict(PyObject *o, void *context) function, but it does not raise an exception if the object has no __dict__."
Also, _PyObject_GetDictPtr is a documented API. Wouldn't it be better to just make it public with minor changes (make it raise an exception on memory errors, fix the return type to ensure that the returned pointer is not writable)
The API has multiple issues that I listed in my first message. C extensions use it to set or clear the dict which is bad.
PyObject_GetDict() doesn't seem to replace properly the private _PyObject_GetDictPtr() function. There are other existing alternatives: PyObject_GenericGetDict() and PyObject_GetAttributeString(obj, "__dict__"). So I don't think that it's worth it to add PyObject_GetDict() function, and I close this issue.
Thanks for your feedback and the constructive discussion.
Activity
davidhewitt commentedon Oct 16, 2025
At the moment I frequently see Rust / PyO3 code do the equivalent of
getattr(obj, "__dict__")(listed as one of your replacements). Is the motivation for this specialized API performance?colesbury commentedon Oct 16, 2025
Would
PyObject_GetDict()materialize the dict if an object usesPy_TPFLAGS_INLINE_VALUES?I don't have a strong opinion about this function, but I looked at a few random projects listed on the issue and this would not replace the usages I saw:
frozendict: Usage of_PyObject_GetDictPtris verbatimdictobject.ccode copied from CPython 3.7-3.10pybind11: CallsPy_CLEAR()on the field - can't usePyObject_GetDictcvxpy: no direct uses (uses pybind11)catboost: CallsPy_CLEARon the pointervstinner commentedon Oct 16, 2025
@davidhewitt:
Yes. But also to not raise an exception if the object has no
__dict__.Object with no dictionary:
@colesbury:
Yes, it does:
Cython may benefit of
PyObject_GetDict()in these two specialized functions.Cython
__Pyx_PyObject_GetMethod():Cython
__Pyx_get_object_dict_version():colesbury commentedon Oct 16, 2025
__Pyx_PyObject_GetMethodlooks like it's only used when compiling for older versions of CPython andma_version_tag(__PYX_GET_DICT_VERSION) is pending removal.zooba commentedon Oct 16, 2025
I really need to be more persuaded that
PyObject_GetAttributeString(obj, "__dict__")isn't a good enough alternative."It might not have a dict" - then it's not your own type and you shouldn't be messing with its internals. Just use its attributes normally.
What are the use cases for clearing the dict? Are they uses that we want to encourage, or should be we trying to lead people into better ways of achieving the same thing? (All I can think of is doing a type transform on an already-constructed object, which I don't think we want to encourage...)
ronaldoussoren commentedon Oct 17, 2025
How is this different from PyObject_GenericGetDict?
Also,
_PyObject_GetDictPtris a documented API. Wouldn't it be better to just make it public with minor changes (make it raise an exception on memory errors, fix the return type to ensure that the returned pointer is not writable)vstinner commentedon Oct 17, 2025
"The function is similar to the existing PyObject* PyObject_GenericGetDict(PyObject *o, void *context) function, but it does not raise an exception if the object has no
__dict__."The API has multiple issues that I listed in my first message. C extensions use it to set or clear the dict which is bad.
encukou commentedon Oct 20, 2025
Do you know any project that uses
_PyObject_GetDictPtr, doesn't write to the pointer, and couldn't switch toPyType_Lookup?vstinner commentedon Nov 17, 2025
PyObject_GetDict()doesn't seem to replace properly the private_PyObject_GetDictPtr()function. There are other existing alternatives:PyObject_GenericGetDict()andPyObject_GetAttributeString(obj, "__dict__"). So I don't think that it's worth it to addPyObject_GetDict()function, and I close this issue.Thanks for your feedback and the constructive discussion.