4#include <pybind11/embed.h>
11#include "VersionInfo.h"
16using namespace py::literals;
23 struct __attribute__((visibility(
"hidden"))) PythonModuleManagerStatics {
24 py::scoped_interpreter pyint{
false};
25 py::exception<boost::thread_interrupted> exceptionObject;
26 std::function<void(
const std::unique_ptr<PyModuleGroup>&)> onMainGroupChangeCallback;
27 PythonModuleManagerStatics();
32 struct __attribute__((visibility(
"hidden"))) PythonModuleManagerImpl {
33 static std::unique_ptr<PythonModuleManagerStatics> statics;
34 std::unique_ptr<PyModuleGroup> mainGroup;
35 std::list<py::object> modules;
36 std::unique_ptr<py::gil_scoped_release> release;
40 std::unique_ptr<detail::PythonModuleManagerStatics> PythonModuleManagerImpl::statics;
45 PythonModuleManagerStatics::PythonModuleManagerStatics() {
46 py::gil_scoped_acquire gil;
48 auto locals = py::dict(
"so_version"_a = ChimeraTK::VersionInfo::soVersion);
55 new_paths.append(os.path.join(p, 'ChimeraTK', 'ApplicationCore'+so_version))
57 sys.path = new_paths + sys.path # prepend so old system libraries are not found first
59 py::globals(), locals);
65 void PythonModuleManager::init() {
70 _impl = std::make_unique<detail::PythonModuleManagerImpl>();
77 _impl->statics = std::make_unique<detail::PythonModuleManagerStatics>();
87 _impl->statics->exceptionObject = {py::module::import(
"__main__"),
"ThreadInterrupted"};
89 py::register_exception_translator([](std::exception_ptr p) {
91 if(p) std::rethrow_exception(p);
93 catch(
const boost::thread_interrupted& e) {
94 detail::PythonModuleManagerImpl::statics->exceptionObject(
"Thread Interrupted");
99 py::exec(
"import threading, traceback, sys, gc");
109 if(_impl->statics->onMainGroupChangeCallback) {
110 _impl->statics->onMainGroupChangeCallback(_impl->mainGroup);
117 _impl->release = std::make_unique<py::gil_scoped_release>();
132 for(
auto* mod : _impl->mainGroup->getSubmoduleListRecursive()) {
137 _impl->release.reset();
140 if(_impl->statics->onMainGroupChangeCallback) {
141 _impl->statics->onMainGroupChangeCallback(std::unique_ptr<PyModuleGroup>{
nullptr});
146 for(
auto& mod : _impl->modules) {
147 auto modname = py::cast<std::string>(mod.attr(
"__name__"));
148 py::exec(
"sys.modules.pop('" + modname +
"')");
150 _impl->modules.clear();
151 py::exec(
"gc.collect()");
166 for(
auto& module : config.getModules(
"PythonModules")) {
169 auto name = config.get<std::string>(
"PythonModules/" +
module + "/path");
170 py::gil_scoped_acquire gil;
172 std::cout <<
"PythonModuleManager: Loading module " << name << std::endl;
175 py::object themod = py::module::import(name.c_str());
176 _impl->modules.emplace_back(std::move(themod));
178 catch(py::error_already_set& err) {
179 throw ChimeraTK::logic_error(
"Error loading Python module from " + name +
": " + err.what());
187 _impl->statics->onMainGroupChangeCallback = std::move(callback);
188 _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.