Import NGWPC Python InterpreterUtil changes#968
Conversation
4858170 to
5a018c3
Compare
5a018c3 to
3108309
Compare
|
The |
3108309 to
b03270f
Compare
|
Lol, so I realized in trying to reproduce the error in a unit/regression test to red/green this PR that the move to C++17 somewhat obviates the change. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf guarantees that the RHS is fully evaluated, and hence the exception thrown, before the LHS starts to be evaluated and creates the I think it's maybe still worth integrating this regardless,
|
…that it could return later
…ule() If the call to pybind11's pybind11::module_::import(topLevelName) failed and threw an exception, the map of importedTopLevelModules was left with a default-constructed entry containing a pybind11::object with a NULL pointer. Later attempts to use that top level module would believe it had been imported, because the key existed in the map, and then dereference that NULL pointer and crash. Thus, separate the import call from the modification to the map, so that the map is left unmodified if the import throws.
88f67fb to
8aee3a3
Compare
christophertubbs
left a comment
There was a problem hiding this comment.
I'd prefer the strict version checking be relaxed a tad - requiring 3.11 or something? Great. Requiring 3.11.6, specifically? A tad dicey.
A potential risk factor I've found was a lack of gil locking (unless I missed it somewhere). That's a ticking multithreaded time bomb.
I understand that a good bit of my concerns are a tad out of scope - if you're just trying to fix one thing and move implementation code out of the header, you don't necessarily want to rock the boat too much. But... there are technically new lines of code so I reserve the right to complain.
I'm setting this review as just a comment - someone with a higher skill level should review. A lot of this is just a tad too clever for me and I think most of my complaints are preexisting issues.
| @@ -32,7 +31,6 @@ namespace utils { | |||
| class InterpreterUtil { | |||
There was a problem hiding this comment.
Maybe out of scope, but I wish there was a bit more of a clear "This is why we're juggling public and private scope definitions" comment. Made sense after a bit of research, though.
There was a problem hiding this comment.
Yeah, I dunno. There feels like some sort of intuitive order in these declarations. I'm just not messing with it here. Perhaps the juggling is more tangible because there are no longer function bodies interspersed to hide that it's going on.
There was a problem hiding this comment.
I believe it's in that order because things need to be deconstructed in a highly specific order.
My primary concern is that someone will go through and try to "fix" it.
| } | ||
|
|
||
| InterpreterUtil::InterpreterUtil() { | ||
| guardPtr = std::make_shared<py::scoped_interpreter>(); |
There was a problem hiding this comment.
this-> would make my life easier. Without it, I have to go hunting for more information on it. Not a huge deal, but it would make it easier for the lower skilled C++ dev.
There was a problem hiding this comment.
I've worked in a whole bunch of different C++ code bases, and this-> has not been idiomatic in any of them. It's kinda taken as a signature of someone who learned to code in Java, and never really acclimated to the switch to C++.
More common for marking member variables as distinct from argument and locals were prefix m_foo or just _foo, or suffix foo_ (my preference).
In the m_ case, globals were g_, function arguments a_, static variables s_, and local variables were unmarked. Even that was a pretty eccentric style, that I haven't seen in any code not sharing that library's heritage (LBL BoxLib/Chombo/AMREX).
There was a problem hiding this comment.
It's kinda taken as a signature of someone who learned to code in Java, and never really acclimated to the switch to C++.
Rude
Anyways, I generally go with foo_ as well. I should have put "my" and "lower skilled C++ dev" in bold. We're going to have a lot of primary Python and R devs passing through this, so I always err on the side of "as obvious as humanly possible, even if it's a tad excessive". As it stands, guardPtr is a tad ambiguous (to me at least), hence the preference for this->.
A transition for this styling is out of scope, but I wanted the preference known at least.
Resolve when you see this. I'd do it, but I want to make sure I'm not shouting into the void.
|
|
||
| importTopLevelModule("numpy"); | ||
| py::str runtime_numpy_version = importedTopLevelModules["numpy"].attr("version").attr("version"); | ||
| if(std::string(runtime_numpy_version) != numpy_version) { |
There was a problem hiding this comment.
Same concern of strict version comparison. It's obviously a little trickier to be less strict here, but it'd help make the system more resilient if it just went "Major minor? Fine". That is, unless it's doing a further check to ensure that some internal processing/runtime linkage assumption is correct.
There was a problem hiding this comment.
See comment above: I think there is a need for this, because the implementation is (independent of this code) not always able to handled version mismatches.
There was a problem hiding this comment.
Bobby/Phil - mark this as resolved if my concern isn't worth addressing due to high version incompatibility risk.
| } | ||
| } | ||
|
|
||
| /* static */ py::object InterpreterUtil::getPyModule(const std::string &name) { |
There was a problem hiding this comment.
What are the chances that this could lead to a py::object leakage (i.e. the py::object living longer than the instance)?
There was a problem hiding this comment.
Probably pretty high if misused. Thankfully, it's not much used throughout the codebase.
If we were writing in Rust, there would be a lifetime parameter on the return value matching that of the InterpreterUtil itself. We don't have lifetime parameters in C++. Maybe in C++ 2035 or something.
There was a problem hiding this comment.
Probably pretty high if misused.
So it'll bite us immediately 😄
Is there anything we can do to highlight "Hey, check this thing out here if you're running into this type of problem"? An inline comment would only help if you knew exactly where to look for it. If we had logging 100% locked down, we could have a debug statement there, but that's not really the case.
This is all just a spitball - I can't think of a solid solution since I don't think we have a diagnostic guide or anything for it.
Feel free to mark as 'resolved' if it's sort of a 'not much we can reasonably do' situation.
robertbartel
left a comment
There was a problem hiding this comment.
I see a couple of very small things that, assuming I haven't missed something, strictly speaking should be fixed. But otherwise, this looks good to me. Tweak or explain those, and then I'll come back and approve.
|
|
||
| importTopLevelModule("numpy"); | ||
| py::str runtime_numpy_version = importedTopLevelModules["numpy"].attr("version").attr("version"); | ||
| if(std::string(runtime_numpy_version) != numpy_version) { |
There was a problem hiding this comment.
See comment above: I think there is a need for this, because the implementation is (independent of this code) not always able to handled version mismatches.
8aee3a3 to
1680d81
Compare
1680d81 to
a79ff67
Compare
|
Chris, the rest of your comments that I haven't made changes for concern pre-existing code, that's just being moved from a header file to a source file. So, I think they're out of scope for addressing in this PR. To the extent they ought to be addressed, that is work that can be planned down the line. It's definitely not on our budget or tasking right now. I'll still reply to the comments. |
Which is why I noted that you wouldn't want to make major changes when it comes to just moving implementation to the correct location. The Do you guys want me to go ahead and add a ticket for it or do you guys want to try and figure out the best way to address it? |
We encountered a bug resulting from an exception safety deficiency in the
InterpreterUtil, where it would createNULLmodule map entries when loading something failed, and later attempts would then dereference those invalid entries.Additions
Changes
Notes
I suggest reviewing each commit separately, since the test and fix are small and substantive, while the last is larger and just a structural refactoring.
The bugfix and code reorganization are as in the NGWPC
developmentbranch, while the other commits are new to this PR.Testing
Screenshots
Before:
After:
Checklist