Skip to content

fix(regression): support embedded submodule #5650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -1342,13 +1342,18 @@ class module_ : public object {
// GraalPy doesn't support PyModule_GetFilenameObject,
// so getting by attribute (see PR #5584)
handle this_module = m_ptr;
result.attr("__file__") = this_module.attr("__file__");
if (object this_file = getattr(this_module, "__file__", none())) {
result.attr("__file__") = this_file;
}
#else
handle this_file = PyModule_GetFilenameObject(m_ptr);
if (!this_file) {
if (this_file) {
result.attr("__file__") = this_file;
} else if (PyErr_ExceptionMatches(PyExc_SystemError) != 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh ... if we have to catch a SystemError here to accommodate submodules, I regret posting this comment on PR #5584.

I think it'll be best to get rid of this #else branch then.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python even throws exceptions when iterating over ranges. I would expect Python's low level C error handling to be more lightweight (if one can say that when it comes to python) than what happens if this were translated to a C++ exception.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be extremely rare, this is only for embedded submodules, normally it works. I think it's probably fine to leave this way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, I'm OK if you want to keep it.

What still makes me feel uneasy: the risk of masking unexpected SystemErrors.

Which is of course extremely low, but not zero. (Google experience: anything that can happen, does happen.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is part of the Stable API and just accesses a dictionary. My two cents: the current implementation is safe.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a link to the implementation handy?

What's on my mind: Why does it raise SystemError, and not AttributeError?

I'd feel more cozy ignoring an AttributeError.

But as I said, I'm fine with this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

The function explicitly sets the system error.

Seems like an unfortunate choice, to have it in the same class of exceptions as e.g. out-of-memory, but 🤷

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>>> SystemError.__mro__
(<class 'SystemError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
>>> MemoryError.__mro__
(<class 'MemoryError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)

https://docs.python.org/3/library/exceptions.html#SystemError

Raised when the interpreter finds an internal error, but the situation does not look so serious to cause it to abandon all hope.

Doesn’t seem too bad, I think it’s for things like this, exceptions that shouldn’t make it up to the user.

PyErr_Clear();
} else {
throw error_already_set();
}
result.attr("__file__") = this_file;
#endif
attr(name) = result;
return result;
Expand Down
6 changes: 6 additions & 0 deletions tests/test_embed/test_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) {
.def_property_readonly("the_message", &Widget::the_message);

m.def("add", [](int i, int j) { return i + j; });

auto sub = m.def_submodule("sub");
sub.def("add", [](int i, int j) { return i + j; });
}

PYBIND11_EMBEDDED_MODULE(trampoline_module, m) {
Expand Down Expand Up @@ -316,6 +319,9 @@ TEST_CASE("Restart the interpreter") {
auto cpp_module = py::module_::import("widget_module");
REQUIRE(cpp_module.attr("add")(1, 2).cast<int>() == 3);

// Also verify submodules work
REQUIRE(cpp_module.attr("sub").attr("add")(1, 41).cast<int>() == 42);

// C++ type information is reloaded and can be used in python modules.
auto py_module = py::module_::import("test_interpreter");
auto py_widget = py_module.attr("DerivedWidget")("Hello after restart");
Expand Down
Loading