4
4
#include < memory>
5
5
#include < vector>
6
6
7
- class VectorOwns4PythonObjects {
7
+ class VecOwnsObjs {
8
8
public:
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); }
15
10
16
11
void set_item (py::ssize_t i, const py::object &obj) {
17
12
if (!(i >= 0 && i < size ())) {
@@ -31,63 +26,120 @@ class VectorOwns4PythonObjects {
31
26
32
27
bool is_empty () const { return vec.empty (); }
33
28
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
-
41
29
static int tp_traverse (PyObject *self_base, visitproc visit, void *arg) {
42
30
#if PY_VERSION_HEX >= 0x03090000 // Python 3.9
43
31
Py_VISIT (Py_TYPE (self_base));
44
32
#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
+ }
50
40
}
51
- auto &self = py::cast<VectorOwns4PythonObjects &>(py::handle{self_base});
41
+ auto &self = py::cast<VecOwnsObjs &>(py::handle{self_base});
52
42
for (const auto &obj : self.vec ) {
53
43
Py_VISIT (obj.ptr ());
54
44
}
55
45
return 0 ;
56
46
}
57
47
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
+
58
90
private:
59
91
std::vector<py::object> vec{};
60
92
};
61
93
94
+ bool VecOwnsObjs::should_check_holder_initialization = false ;
95
+ bool VecOwnsObjs::should_raise_error_on_set_state = false ;
96
+
62
97
TEST_SUBMODULE (invalid_holder_access, m) {
63
98
m.doc () = " Test invalid holder access" ;
64
99
65
100
#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
+ }
72
106
return vec;
73
107
});
74
108
#endif
75
109
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 {
80
112
auto *const type = &heap_type->ht_type ;
81
113
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;
83
116
}))
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);
93
145
}
0 commit comments