-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Required prerequisites
- Make sure you've read the documentation. Your issue may be addressed there.
- Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
2.10.3
Problem description
I'm trying to bind a C++ class to Python using py::class_<CustomClass>. I have some internal knowledge about my custom class, which should be immutable. So I want to turn on Py_TPFLAGS_IMMUTABLETYPE (since Python 3.10) in my custom Python type object's tp_flags.
I tried to set the tp_flags using py::custom_type_setup:
auto ImmutableCustomTypeObject =
py::class_<ImmutableCustom>(mod,
"ImmutableCustom",
"Instances of this class are immutable.",
py::custom_type_setup([](PyHeapTypeObject* heap_type) {
auto* type = &heap_type->ht_type;
#ifdef Py_TPFLAGS_IMMUTABLETYPE
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
#endif
}));However, the typeobject becomes frozen immediately in PyType_Ready after calling the custom_type_setup_callback:
pybind11/include/pybind11/detail/class.h
Lines 716 to 735 in 3cc7e42
| if (rec.custom_type_setup_callback) { | |
| rec.custom_type_setup_callback(heap_type); | |
| } | |
| if (PyType_Ready(type) < 0) { | |
| pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string()); | |
| } | |
| assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); | |
| /* Register type with the parent scope */ | |
| if (rec.scope) { | |
| setattr(rec.scope, rec.name, (PyObject *) type); | |
| } else { | |
| Py_INCREF(type); // Keep it alive forever (reference leak) | |
| } | |
| if (module_) { // Needed by pydoc | |
| setattr((PyObject *) type, "__module__", module_); | |
| } |
then a TypeError will raise at line 734 when importing the built extension:
pybind11/include/pybind11/detail/class.h
Lines 733 to 735 in 3cc7e42
| if (module_) { // Needed by pydoc | |
| setattr((PyObject *) type, "__module__", module_); | |
| } |
which is equivalent to:
ImmutableCustom.__module__: str = mod.__name__it raises:
>>> import _C
TypeError: cannot set '__module__' attribute of immutable type '_C.ImmutableCustom'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: initialization failedAlso, after PyType_Ready, I cannot do cls.def_property_readonly(...).def(...); since it is immutable.
I tried to modify the tp_flags after I had fully initialized the class definition:
auto ImmutableCustomTypeObject =
py::class_<ImmutableCustom>(mod,
"ImmutableCustom",
"Instances of this class are immutable.");
reinterpret_cast<PyTypeObject*>(ImmutableCustomTypeObject.ptr())->tp_name = "mymod.ImmutableCustomTypeObject";
py::setattr(ImmutableCustomTypeObject, "__module__", py::str("mymod"));
ImmutableCustomTypeObject
.def_property_readonly(...)
.def_property_readonly(...)
.def(...)
.def(...);
#ifdef Py_TPFLAGS_IMMUTABLETYPE
reinterpret_cast<PyTypeObject*>(ImmutableCustomTypeObject.ptr())->tp_flags |=
Py_TPFLAGS_IMMUTABLETYPE;
#endifI'm not sure if this is allowed to flip tp_flags if the type is already called with PyType_Ready.
Reproducible example code
Initialize a bound class with tp_flags |= Py_TPFLAGS_IMMUTABLETYPE (Python 3.10+):
auto ImmutableCustomTypeObject =
py::class_<ImmutableCustom>(mod,
"ImmutableCustom",
"Instances of this class are immutable.",
py::custom_type_setup([](PyHeapTypeObject* heap_type) {
auto* type = &heap_type->ht_type;
#ifdef Py_TPFLAGS_IMMUTABLETYPE
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
#endif
}));Then import the C++ extension in Python:
>>> import _C
TypeError: cannot set '__module__' attribute of immutable type '_C.ImmutableCustom'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: initialization failedIs this a regression? Put the last known working version here if it is.
Not a regression