Skip to content

Commit 98f1bbb

Browse files
Ignore deprecation warnings about old-style __init__/__setstate__ constructors in the tests (originally done in #2746) (#2759)
* Ignore old-style __init__ warnings * Simplify ignoreOldStyleInitWarnings with py::exec * Only wrap single class_::defs to ignore DeprecationWarnings about old-style __init__
1 parent e57dd47 commit 98f1bbb

File tree

3 files changed

+76
-44
lines changed

3 files changed

+76
-44
lines changed

tests/pybind11_tests.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22
#include <pybind11/pybind11.h>
3+
#include <pybind11/eval.h>
34

45
#if defined(_MSC_VER) && _MSC_VER < 1910
56
// We get some really long type names here which causes MSVC 2015 to emit warnings
@@ -69,3 +70,15 @@ template<> class type_caster<RValueCaster> {
6970
};
7071
PYBIND11_NAMESPACE_END(detail)
7172
PYBIND11_NAMESPACE_END(pybind11)
73+
74+
template <typename F>
75+
void ignoreOldStyleInitWarnings(F &&body) {
76+
py::exec(R"(
77+
message = "pybind11-bound class '.+' is using an old-style placement-new '(?:__init__|__setstate__)' which has been deprecated"
78+
79+
import warnings
80+
with warnings.catch_warnings():
81+
warnings.filterwarnings("ignore", message=message, category=FutureWarning)
82+
body()
83+
)", py::dict(py::arg("body") = py::cpp_function(body)));
84+
}

tests/test_factory_constructors.cpp

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,14 @@ TEST_SUBMODULE(factory_constructors, m) {
183183
auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);};
184184

185185
// test_init_factory_basic, test_init_factory_casting
186-
py::class_<TestFactory3, std::shared_ptr<TestFactory3>>(m, "TestFactory3")
186+
py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
187+
pyTestFactory3
187188
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
188-
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }))
189-
.def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor
190-
189+
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
190+
ignoreOldStyleInitWarnings([&pyTestFactory3]() {
191+
pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }); // placement-new ctor
192+
});
193+
pyTestFactory3
191194
// factories returning a derived type:
192195
.def(py::init(c4a)) // derived ptr
193196
.def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
@@ -304,24 +307,32 @@ TEST_SUBMODULE(factory_constructors, m) {
304307
static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); }
305308
#endif
306309
};
307-
py::class_<NoisyAlloc>(m, "NoisyAlloc")
310+
311+
312+
py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc");
308313
// Since these overloads have the same number of arguments, the dispatcher will try each of
309314
// them until the arguments convert. Thus we can get a pre-allocation here when passing a
310315
// single non-integer:
311-
.def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation
312-
.def(py::init([](double d) { return new NoisyAlloc(d); }))
313-
314-
// The two-argument version: first the factory pointer overload.
315-
.def(py::init([](int i, int) { return new NoisyAlloc(i); }))
316-
// Return-by-value:
317-
.def(py::init([](double d, int) { return NoisyAlloc(d); }))
318-
// Old-style placement new init; requires preallocation
319-
.def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); })
320-
// Requires deallocation of previous overload preallocated value:
321-
.def(py::init([](int i, double) { return new NoisyAlloc(i); }))
322-
// Regular again: requires yet another preallocation
323-
.def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); })
324-
;
316+
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
317+
pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }); // Regular constructor, runs first, requires preallocation
318+
});
319+
320+
pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); }));
321+
322+
// The two-argument version: first the factory pointer overload.
323+
pyNoisyAlloc.def(py::init([](int i, int) { return new NoisyAlloc(i); }));
324+
// Return-by-value:
325+
pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); }));
326+
// Old-style placement new init; requires preallocation
327+
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
328+
pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); });
329+
});
330+
// Requires deallocation of previous overload preallocated value:
331+
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
332+
// Regular again: requires yet another preallocation
333+
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
334+
pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); });
335+
});
325336

326337

327338

tests/test_pickling.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ TEST_SUBMODULE(pickling, m) {
3131
using Pickleable::Pickleable;
3232
};
3333

34-
py::class_<Pickleable>(m, "Pickleable")
34+
py::class_<Pickleable> pyPickleable(m, "Pickleable");
35+
pyPickleable
3536
.def(py::init<std::string>())
3637
.def("value", &Pickleable::value)
3738
.def("extra1", &Pickleable::extra1)
@@ -43,17 +44,20 @@ TEST_SUBMODULE(pickling, m) {
4344
.def("__getstate__", [](const Pickleable &p) {
4445
/* Return a tuple that fully encodes the state of the object */
4546
return py::make_tuple(p.value(), p.extra1(), p.extra2());
46-
})
47-
.def("__setstate__", [](Pickleable &p, py::tuple t) {
48-
if (t.size() != 3)
49-
throw std::runtime_error("Invalid state!");
50-
/* Invoke the constructor (need to use in-place version) */
51-
new (&p) Pickleable(t[0].cast<std::string>());
52-
53-
/* Assign any additional state */
54-
p.setExtra1(t[1].cast<int>());
55-
p.setExtra2(t[2].cast<int>());
5647
});
48+
ignoreOldStyleInitWarnings([&pyPickleable]() {
49+
pyPickleable
50+
.def("__setstate__", [](Pickleable &p, py::tuple t) {
51+
if (t.size() != 3)
52+
throw std::runtime_error("Invalid state!");
53+
/* Invoke the constructor (need to use in-place version) */
54+
new (&p) Pickleable(t[0].cast<std::string>());
55+
56+
/* Assign any additional state */
57+
p.setExtra1(t[1].cast<int>());
58+
p.setExtra2(t[2].cast<int>());
59+
});
60+
});
5761

5862
py::class_<PickleableNew, Pickleable>(m, "PickleableNew")
5963
.def(py::init<std::string>())
@@ -87,27 +91,31 @@ TEST_SUBMODULE(pickling, m) {
8791
using PickleableWithDict::PickleableWithDict;
8892
};
8993

90-
py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
94+
py::class_<PickleableWithDict> pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr());
95+
pyPickleableWithDict
9196
.def(py::init<std::string>())
9297
.def_readwrite("value", &PickleableWithDict::value)
9398
.def_readwrite("extra", &PickleableWithDict::extra)
9499
.def("__getstate__", [](py::object self) {
95100
/* Also include __dict__ in state */
96101
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
97-
})
98-
.def("__setstate__", [](py::object self, py::tuple t) {
99-
if (t.size() != 3)
100-
throw std::runtime_error("Invalid state!");
101-
/* Cast and construct */
102-
auto& p = self.cast<PickleableWithDict&>();
103-
new (&p) PickleableWithDict(t[0].cast<std::string>());
104-
105-
/* Assign C++ state */
106-
p.extra = t[1].cast<int>();
107-
108-
/* Assign Python state */
109-
self.attr("__dict__") = t[2];
110102
});
103+
ignoreOldStyleInitWarnings([&pyPickleableWithDict]() {
104+
pyPickleableWithDict
105+
.def("__setstate__", [](py::object self, py::tuple t) {
106+
if (t.size() != 3)
107+
throw std::runtime_error("Invalid state!");
108+
/* Cast and construct */
109+
auto& p = self.cast<PickleableWithDict&>();
110+
new (&p) PickleableWithDict(t[0].cast<std::string>());
111+
112+
/* Assign C++ state */
113+
p.extra = t[1].cast<int>();
114+
115+
/* Assign Python state */
116+
self.attr("__dict__") = t[2];
117+
});
118+
});
111119

112120
py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew")
113121
.def(py::init<std::string>())

0 commit comments

Comments
 (0)