4#include <pybind11/embed.h>
11#include "VersionInfo.h"
14using namespace py::literals;
21 struct __attribute__((visibility(
"hidden"))) PythonModuleManagerStatics {
22 py::scoped_interpreter pyint{
false};
23 py::exception<boost::thread_interrupted> exceptionObject;
24 std::function<void(
const std::unique_ptr<PyModuleGroup>&)> onMainGroupChangeCallback;
25 PythonModuleManagerStatics();
30 struct __attribute__((visibility(
"hidden"))) PythonModuleManagerImpl {
31 static std::unique_ptr<PythonModuleManagerStatics> statics;
32 std::unique_ptr<PyModuleGroup> mainGroup;
33 std::list<py::object> modules;
34 std::unique_ptr<py::gil_scoped_release> release;
38 std::unique_ptr<detail::PythonModuleManagerStatics> PythonModuleManagerImpl::statics;
43 __attribute__((visibility(
"hidden"))) PythonModuleManagerStatics::PythonModuleManagerStatics() {
44 py::gil_scoped_acquire gil;
46 auto locals = py::dict(
"so_version"_a = ChimeraTK::VersionInfo::soVersion);
53 new_paths.append(os.path.join(p, 'ChimeraTK', 'ApplicationCore'+so_version))
55 sys.path = new_paths + sys.path # prepend so old system libraries are not found first
57 py::globals(), locals);
63 void PythonModuleManager::init() {
68 _impl = std::make_unique<detail::PythonModuleManagerImpl>();
75 _impl->statics = std::make_unique<detail::PythonModuleManagerStatics>();
85 _impl->statics->exceptionObject = {py::module::import(
"__main__"),
"ThreadInterrupted"};
87 py::register_exception_translator([](std::exception_ptr p) {
89 if(p) std::rethrow_exception(p);
91 catch(
const boost::thread_interrupted& e) {
92 detail::PythonModuleManagerImpl::statics->exceptionObject(
"Thread Interrupted");
97 py::exec(
"import threading, traceback, sys, gc");
107 if(_impl->statics->onMainGroupChangeCallback) {
108 _impl->statics->onMainGroupChangeCallback(_impl->mainGroup);
115 _impl->release = std::make_unique<py::gil_scoped_release>();
130 for(
auto* mod : _impl->mainGroup->getSubmoduleListRecursive()) {
135 _impl->release.reset();
138 if(_impl->statics->onMainGroupChangeCallback) {
139 _impl->statics->onMainGroupChangeCallback(std::unique_ptr<PyModuleGroup>{
nullptr});
144 for(
auto& mod : _impl->modules) {
145 auto modname = py::cast<std::string>(mod.attr(
"__name__"));
146 py::exec(
"sys.modules.pop('" + modname +
"')");
148 _impl->modules.clear();
149 py::exec(
"gc.collect()");
164 for(
auto& module : config.getModules(
"PythonModules")) {
167 auto name = config.get<std::string>(
"PythonModules/" +
module + "/path");
168 py::gil_scoped_acquire gil;
170 std::cout <<
"PythonModuleManager: Loading module " << name << std::endl;
173 py::object themod = py::module::import(name.c_str());
174 _impl->modules.emplace_back(std::move(themod));
176 catch(py::error_already_set& err) {
177 throw ChimeraTK::logic_error(
"Error loading Python module from " + name +
": " + err.what());
185 _impl->statics->onMainGroupChangeCallback = std::move(callback);
186 _impl->statics->onMainGroupChangeCallback(_impl->mainGroup);
ConfigReader & getConfigReader()
static Application & getInstance()
Obtain instance of the application.
void createModules(Application &app)
called by Application to load all Python modules specified in the ConfigReader XML file
void setOnMainGroupChange(std::function< void(const std::unique_ptr< PyModuleGroup > &)> callback)
Register callback function to get informed about the main PyModuleGroup which is created by the Pytho...
void deinit()
clean up detail::PythonModuleManagerImpl, in particular py::gil_scoped_release
~PythonModuleManager()
need non-default destructor due to incomplete type detail::PythonModuleManagerImpl
PythonModuleManager()
need non-default constructor due to incomplete type detail::PythonModuleManagerImpl
InvalidityTracer application module.