44#include < memory>
55#include < vector>
66
7- class VectorOwns4PythonObjects {
7+ class VecOwnsObjs {
88public:
9- void append (const py::object &obj) {
10- if (size () >= 4 ) {
11- throw std::out_of_range (" Index out of range" );
12- }
13- vec.emplace_back (obj);
14- }
9+ void append (const py::object &obj) { vec.emplace_back (obj); }
1510
1611 void set_item (py::ssize_t i, const py::object &obj) {
1712 if (!(i >= 0 && i < size ())) {
@@ -31,63 +26,120 @@ class VectorOwns4PythonObjects {
3126
3227 bool is_empty () const { return vec.empty (); }
3328
34- void sanity_check () const {
35- auto current_size = size ();
36- if (current_size < 0 || current_size > 4 ) {
37- throw std::out_of_range (" Invalid size" );
38- }
39- }
40-
4129 static int tp_traverse (PyObject *self_base, visitproc visit, void *arg) {
4230#if PY_VERSION_HEX >= 0x03090000 // Python 3.9
4331 Py_VISIT (Py_TYPE (self_base));
4432#endif
45- auto *const instance = reinterpret_cast <py::detail::instance *>(self_base);
46- if (!instance->get_value_and_holder ().holder_constructed ()) {
47- // The holder has not been constructed yet. Skip the traversal to avoid segmentation
48- // faults.
49- return 0 ;
33+ if (should_check_holder_initialization) {
34+ auto *const instance = reinterpret_cast <py::detail::instance *>(self_base);
35+ if (!instance->get_value_and_holder ().holder_constructed ()) {
36+ // The holder has not been constructed yet. Skip the traversal to avoid
37+ // segmentation faults.
38+ return 0 ;
39+ }
5040 }
51- auto &self = py::cast<VectorOwns4PythonObjects &>(py::handle{self_base});
41+ auto &self = py::cast<VecOwnsObjs &>(py::handle{self_base});
5242 for (const auto &obj : self.vec ) {
5343 Py_VISIT (obj.ptr ());
5444 }
5545 return 0 ;
5646 }
5747
48+ static int tp_clear (PyObject *self_base) {
49+ if (should_check_holder_initialization) {
50+ auto *const instance = reinterpret_cast <py::detail::instance *>(self_base);
51+ if (!instance->get_value_and_holder ().holder_constructed ()) {
52+ // The holder has not been constructed yet. Skip the traversal to avoid
53+ // segmentation faults.
54+ return 0 ;
55+ }
56+ }
57+ auto &self = py::cast<VecOwnsObjs &>(py::handle{self_base});
58+ for (auto &obj : self.vec ) {
59+ Py_CLEAR (obj.ptr ());
60+ }
61+ self.vec .clear ();
62+ return 0 ;
63+ }
64+
65+ py::object get_state () const {
66+ py::list state{};
67+ for (const auto &item : vec) {
68+ state.append (item);
69+ }
70+ return py::tuple (state);
71+ }
72+
73+ static bool get_should_check_holder_initialization () {
74+ return should_check_holder_initialization;
75+ }
76+
77+ static void set_should_check_holder_initialization (bool value) {
78+ should_check_holder_initialization = value;
79+ }
80+
81+ static bool get_should_raise_error_on_set_state () { return should_raise_error_on_set_state; }
82+
83+ static void set_should_raise_error_on_set_state (bool value) {
84+ should_raise_error_on_set_state = value;
85+ }
86+
87+ static bool should_check_holder_initialization;
88+ static bool should_raise_error_on_set_state;
89+
5890private:
5991 std::vector<py::object> vec{};
6092};
6193
94+ bool VecOwnsObjs::should_check_holder_initialization = false ;
95+ bool VecOwnsObjs::should_raise_error_on_set_state = false ;
96+
6297TEST_SUBMODULE (invalid_holder_access, m) {
6398 m.doc () = " Test invalid holder access" ;
6499
65100#if defined(PYBIND11_CPP14)
66- m.def (" create_vector" , []() -> std::unique_ptr<VectorOwns4PythonObjects> {
67- auto vec = std::make_unique<VectorOwns4PythonObjects>();
68- vec->append (py::none ());
69- vec->append (py::int_ (1 ));
70- vec->append (py::str (" test" ));
71- vec->append (py::tuple ());
101+ m.def (" create_vector" , [](const py::iterable &iterable) -> std::unique_ptr<VecOwnsObjs> {
102+ auto vec = std::make_unique<VecOwnsObjs>();
103+ for (const auto &item : iterable) {
104+ vec->append (py::reinterpret_borrow<py::object>(item));
105+ }
72106 return vec;
73107 });
74108#endif
75109
76- py::class_<VectorOwns4PythonObjects>(
77- m,
78- " VectorOwns4PythonObjects" ,
79- py::custom_type_setup ([](PyHeapTypeObject *heap_type) -> void {
110+ py::class_<VecOwnsObjs>(
111+ m, " VecOwnsObjs" , py::custom_type_setup ([](PyHeapTypeObject *heap_type) -> void {
80112 auto *const type = &heap_type->ht_type ;
81113 type->tp_flags |= Py_TPFLAGS_HAVE_GC;
82- type->tp_traverse = &VectorOwns4PythonObjects::tp_traverse;
114+ type->tp_traverse = &VecOwnsObjs::tp_traverse;
115+ type->tp_clear = &VecOwnsObjs::tp_clear;
83116 }))
84- .def (" append" , &VectorOwns4PythonObjects::append, py::arg (" obj" ))
85- .def (" set_item" , &VectorOwns4PythonObjects::set_item, py::arg (" i" ), py::arg (" obj" ))
86- .def (" get_item" , &VectorOwns4PythonObjects::get_item, py::arg (" i" ))
87- .def (" size" , &VectorOwns4PythonObjects::size)
88- .def (" is_empty" , &VectorOwns4PythonObjects::is_empty)
89- .def (" __setitem__" , &VectorOwns4PythonObjects::set_item, py::arg (" i" ), py::arg (" obj" ))
90- .def (" __getitem__" , &VectorOwns4PythonObjects::get_item, py::arg (" i" ))
91- .def (" __len__" , &VectorOwns4PythonObjects::size)
92- .def (" sanity_check" , &VectorOwns4PythonObjects::sanity_check);
117+ .def_static (" set_should_check_holder_initialization" ,
118+ &VecOwnsObjs::set_should_check_holder_initialization,
119+ py::arg (" value" ))
120+ .def_static (" set_should_raise_error_on_set_state" ,
121+ &VecOwnsObjs::set_should_raise_error_on_set_state,
122+ py::arg (" value" ))
123+ #if defined(PYBIND11_CPP14)
124+ .def (py::pickle ([](const VecOwnsObjs &self) -> py::object { return self.get_state (); },
125+ [](const py::object &state) -> std::unique_ptr<VecOwnsObjs> {
126+ if (!py::isinstance<py::tuple>(state)) {
127+ throw std::runtime_error (" Invalid state" );
128+ }
129+ auto vec = std::make_unique<VecOwnsObjs>();
130+ if (VecOwnsObjs::get_should_raise_error_on_set_state ()) {
131+ throw std::runtime_error (" raise error on set_state for testing" );
132+ }
133+ for (const auto &item : state) {
134+ vec->append (py::reinterpret_borrow<py::object>(item));
135+ }
136+ return vec;
137+ }),
138+ py::arg (" state" ))
139+ #endif
140+ .def (" append" , &VecOwnsObjs::append, py::arg (" obj" ))
141+ .def (" is_empty" , &VecOwnsObjs::is_empty)
142+ .def (" __setitem__" , &VecOwnsObjs::set_item, py::arg (" i" ), py::arg (" obj" ))
143+ .def (" __getitem__" , &VecOwnsObjs::get_item, py::arg (" i" ))
144+ .def (" __len__" , &VecOwnsObjs::size);
93145}
0 commit comments