Allow module-local classes to be loaded externally
The main point of `py::module_local` is to make the C++ -> Python cast
unique so that returning/casting a C++ instance is well-defined.
Unfortunately it also makes loading unique, but this isn't particularly
desirable: when an instance contains `Type` instance there's no reason
it shouldn't be possible to pass that instance to a bound function
taking a `Type` parameter, even if that function is in another module.
This commit solves the issue by allowing foreign module (and global)
type loaders have a chance to load the value if the local module loader
fails. The implementation here does this by storing a module-local
loading function in a capsule in the python type, which we can then call
if the local (and possibly global, if the local type is masking a global
type) version doesn't work.
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index afa91ac..5c78abe 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -829,7 +829,7 @@
pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) +
"\": an object with that name is already defined");
- if (get_type_info(*rec.type, false /* don't throw */, !rec.module_local))
+ if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type))
pybind11_fail("generic_type: type \"" + std::string(rec.name) +
"\" is already registered!");
@@ -866,6 +866,12 @@
auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr());
tinfo->simple_ancestors = parent_tinfo->simple_ancestors;
}
+
+ if (rec.module_local) {
+ // Stash the local typeinfo and loader so that external modules can access it.
+ tinfo->module_local_load = &type_caster_generic::local_load;
+ setattr(m_ptr, "_pybind11_module_local_typeinfo", capsule(tinfo));
+ }
}
/// Helper function which tags all parents of a type using mult. inheritance