Fix nullptr dereference when loading an external-only module_local type
diff --git a/tests/local_bindings.h b/tests/local_bindings.h
index 8f48ed9..b6afb80 100644
--- a/tests/local_bindings.h
+++ b/tests/local_bindings.h
@@ -8,9 +8,9 @@
int i = -1;
};
-/// Registered with py::local in both main and secondary modules:
+/// Registered with py::module_local in both main and secondary modules:
using LocalType = LocalBase<0>;
-/// Registered without py::local in both modules:
+/// Registered without py::module_local in both modules:
using NonLocalType = LocalBase<1>;
/// A second non-local type (for stl_bind tests):
using NonLocal2 = LocalBase<2>;
@@ -21,6 +21,10 @@
/// Mixed: global first, then local
using MixedGlobalLocal = LocalBase<5>;
+/// Registered with py::module_local only in the secondary module:
+using ExternalType1 = LocalBase<6>;
+using ExternalType2 = LocalBase<7>;
+
using LocalVec = std::vector<LocalType>;
using LocalVec2 = std::vector<NonLocal2>;
using LocalMap = std::unordered_map<std::string, LocalType>;
@@ -39,7 +43,7 @@
// Simple bindings (used with the above):
-template <typename T, int Adjust, typename... Args>
+template <typename T, int Adjust = 0, typename... Args>
py::class_<T> bind_local(Args && ...args) {
return py::class_<T>(std::forward<Args>(args)...)
.def(py::init<int>())
diff --git a/tests/pybind11_cross_module_tests.cpp b/tests/pybind11_cross_module_tests.cpp
index 7dd70e0..928b255 100644
--- a/tests/pybind11_cross_module_tests.cpp
+++ b/tests/pybind11_cross_module_tests.cpp
@@ -20,6 +20,10 @@
// Definitions here are tested by importing both this module and the
// relevant pybind11_tests submodule from a test_whatever.py
+ // test_load_external
+ bind_local<ExternalType1>(m, "ExternalType1", py::module_local());
+ bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
+
// test_exceptions.py
m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); });
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); });
diff --git a/tests/test_local_bindings.cpp b/tests/test_local_bindings.cpp
index 359d6c6..97c02db 100644
--- a/tests/test_local_bindings.cpp
+++ b/tests/test_local_bindings.cpp
@@ -15,6 +15,10 @@
#include <numeric>
TEST_SUBMODULE(local_bindings, m) {
+ // test_load_external
+ m.def("load_external1", [](ExternalType1 &e) { return e.i; });
+ m.def("load_external2", [](ExternalType2 &e) { return e.i; });
+
// test_local_bindings
// Register a class with py::module_local:
bind_local<LocalType, -1>(m, "LocalType", py::module_local())
diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py
index e9a63ca..b3dc361 100644
--- a/tests/test_local_bindings.py
+++ b/tests/test_local_bindings.py
@@ -3,6 +3,22 @@
from pybind11_tests import local_bindings as m
+def test_load_external():
+ """Load a `py::module_local` type that's only registered in an external module"""
+ import pybind11_cross_module_tests as cm
+
+ assert m.load_external1(cm.ExternalType1(11)) == 11
+ assert m.load_external2(cm.ExternalType2(22)) == 22
+
+ with pytest.raises(TypeError) as excinfo:
+ assert m.load_external2(cm.ExternalType1(21)) == 21
+ assert "incompatible function arguments" in str(excinfo.value)
+
+ with pytest.raises(TypeError) as excinfo:
+ assert m.load_external1(cm.ExternalType2(12)) == 12
+ assert "incompatible function arguments" in str(excinfo.value)
+
+
def test_local_bindings():
"""Tests that duplicate `py::module_local` class bindings work across modules"""