Prevent overwriting previous declarations
Currently pybind11 doesn't check when you define a new object (e.g. a
class, function, or exception) that overwrites an existing one. If the
thing being overwritten is a class, this leads to a segfault (because
pybind still thinks the type is defined, even though Python no longer
has the type). In other cases this is harmless (e.g. replacing a
function with an exception), but even in that case it's most likely a
bug.
This code doesn't prevent you from actively doing something harmful,
like deliberately overwriting a previous definition, but detects
overwriting with a run-time error if it occurs in the standard
class/function/exception/def registration interfaces.
All of the additions are in non-template code; the result is actually a
tiny decrease in .so size compared to master without the new test code
(977304 to 977272 bytes), and about 4K higher with the new tests.
diff --git a/tests/test_issues.cpp b/tests/test_issues.cpp
index 29c4057..f5467cb 100644
--- a/tests/test_issues.cpp
+++ b/tests/test_issues.cpp
@@ -36,6 +36,18 @@
return OpTest2();
}
+// #461
+class Dupe1 {
+public:
+ Dupe1(int v) : v_{v} {}
+ int get_value() const { return v_; }
+private:
+ int v_;
+};
+class Dupe2 {};
+class Dupe3 {};
+class DupeException : public std::runtime_error {};
+
void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues");
@@ -237,7 +249,49 @@
static std::vector<int> list = { 1, 2, 3 };
m2.def("make_iterator_1", []() { return py::make_iterator<py::return_value_policy::copy>(list); });
m2.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
+
+ static std::vector<std::string> nothrows;
+ // Issue 461: registering two things with the same name:
+ py::class_<Dupe1>(m2, "Dupe1")
+ .def("get_value", &Dupe1::get_value)
+ ;
+ m2.def("dupe1_factory", [](int v) { return new Dupe1(v); });
+
+ py::class_<Dupe2>(m2, "Dupe2");
+ py::exception<DupeException>(m2, "DupeException");
+
+ try {
+ m2.def("Dupe1", [](int v) { return new Dupe1(v); });
+ nothrows.emplace_back("Dupe1");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::class_<Dupe3>(m2, "dupe1_factory");
+ nothrows.emplace_back("dupe1_factory");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::exception<Dupe3>(m2, "Dupe2");
+ nothrows.emplace_back("Dupe2");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ m2.def("DupeException", []() { return 30; });
+ nothrows.emplace_back("DupeException1");
+ }
+ catch (std::runtime_error &) {}
+ try {
+ py::class_<DupeException>(m2, "DupeException");
+ nothrows.emplace_back("DupeException2");
+ }
+ catch (std::runtime_error &) {}
+ m2.def("dupe_exception_failures", []() {
+ py::list l;
+ for (auto &e : nothrows) l.append(py::cast(e));
+ return l;
+ });
}
+
// MSVC workaround: trying to use a lambda here crashes MSCV
test_initializer issues(&init_issues);
diff --git a/tests/test_issues.py b/tests/test_issues.py
index e2ab0b4..cf645ef 100644
--- a/tests/test_issues.py
+++ b/tests/test_issues.py
@@ -182,3 +182,8 @@
assert list(make_iterator_1()) == [1, 2, 3]
assert list(make_iterator_2()) == [1, 2, 3]
assert(type(make_iterator_1()) != type(make_iterator_2()))
+
+def test_dupe_assignment():
+ """ Issue 461: overwriting a class with a function """
+ from pybind11_tests.issues import dupe_exception_failures
+ assert dupe_exception_failures() == []