diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 76d998a3ba..8647d76cf1 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1541,6 +1541,14 @@ class module_ : public object { return *this; } + /** \rst + Overload of `def` taking the function name as a ``std::string``. + \endrst */ + template + module_ &def(const std::string &name_, Func &&f, const Extra &...extra) { + return def(name_.c_str(), f, std::forward(extra)...); + } + /** \rst Create and return a new Python submodule with the given name and docstring. This also works recursively, i.e. @@ -1587,6 +1595,18 @@ class module_ : public object { return result; } + /** \rst + Overload of `def_submodule` taking the function name and documentation as ``std::string``. + \endrst */ + module_ def_submodule(const std::string &name, const std::string &doc) { + return def_submodule(name.c_str(), doc.c_str()); + } + + /** \rst + Overload of `def_submodule` taking the function name as a ``std::string``. + \endrst */ + module_ def_submodule(const std::string &name) { return def_submodule(name.c_str(), nullptr); } + /// Import and return a module or throws `error_already_set`. static module_ import(const char *name) { PyObject *obj = PyImport_ImportModule(name); @@ -1596,6 +1616,11 @@ class module_ : public object { return reinterpret_steal(obj); } + /** \rst + Overload of `import` taking the function name as a ``std::string``. + \endrst */ + static module_ import(const std::string &name) { return import(name.c_str()); } + /// Reload the module or throws `error_already_set`. void reload() { PyObject *obj = PyImport_ReloadModule(ptr()); @@ -1622,6 +1647,14 @@ class module_ : public object { PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); } + /** \rst + Overload of `add_object` taking the function name as a ``std::string``. + \endrst */ + PYBIND11_NOINLINE void + add_object(const std::string &name, handle obj, bool overwrite = false) { + return add_object(name.c_str(), obj, overwrite); + } + // DEPRECATED (since PR #5688): Use PyModuleDef directly instead. using module_def = PyModuleDef; @@ -1662,6 +1695,18 @@ class module_ : public object { // For Python 2, reinterpret_borrow was correct. return reinterpret_borrow(m); } + + /** \rst + Overload of `create_extension_module` taking the module name and documentation as + ``std::string``. + \endrst */ + static module_ create_extension_module(const std::string &name, + const std::string &doc, + PyModuleDef *def, + mod_gil_not_used gil_not_used + = mod_gil_not_used(false)) { + return create_extension_module(name.c_str(), doc.c_str(), def, gil_not_used); + } }; PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index 0a970065b4..9d5970260c 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -268,6 +268,19 @@ TEST_SUBMODULE(exceptions, m) { return false; }); + m.def("modulenotfound_exception_matches_base_string", []() { + try { + // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError + py::module_::import(std::string("nonexistent")); + } catch (py::error_already_set &ex) { + if (!ex.matches(PyExc_ImportError)) { + throw; + } + return true; + } + return false; + }); + m.def("throw_already_set", [](bool err) { if (err) { py::set_error(PyExc_ValueError, "foo"); diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 59845b441b..505ccb6178 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -133,6 +133,7 @@ def test_exception_matches(): assert m.exception_matches() assert m.exception_matches_base() assert m.modulenotfound_exception_matches_base() + assert m.modulenotfound_exception_matches_base_string() def test_custom(msg): diff --git a/tests/test_modules.cpp b/tests/test_modules.cpp index 842a3bc4b8..889f5275e5 100644 --- a/tests/test_modules.cpp +++ b/tests/test_modules.cpp @@ -121,4 +121,11 @@ TEST_SUBMODULE(modules, m) { }); m.def("def_submodule", [](py::module_ m, const char *name) { return m.def_submodule(name); }); + + // Test std::string versions of def_submodule and def + py::module m_sub_string = m.def_submodule(std::string("submodule_string")); + m_sub_string.def(std::string("submodule_string_func"), + []() { return "submodule_string_func()"; }); + m_sub_string.def( + std::string("submodule_string_func_with_arg"), [](int x) { return x + 1; }, py::arg("x")); } diff --git a/tests/test_modules.py b/tests/test_modules.py index ea8ca7e5e4..1cee311017 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -7,6 +7,7 @@ import env from pybind11_tests import ConstructorStats from pybind11_tests import modules as m +from pybind11_tests.modules import submodule_string as mstr from pybind11_tests.modules import subsubmodule as ms @@ -25,6 +26,14 @@ def test_nested_modules(): assert ms.submodule_func() == "submodule_func()" + assert ( + pybind11_tests.modules.submodule_string.__name__ + == "pybind11_tests.modules.submodule_string" + ) + + assert mstr.submodule_string_func() == "submodule_string_func()" + assert mstr.submodule_string_func_with_arg(x=2) == 3 + def test_reference_internal(): b = ms.B()