Skip to content

Commit a648780

Browse files
committed
[WIP] [211] Initialize module search paths via PyConfig for 3.8+
1 parent 5fe1ced commit a648780

1 file changed

Lines changed: 84 additions & 10 deletions

File tree

src/main.cpp

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ static std::recursive_mutex python_mutex;
103103

104104
namespace
105105
{
106-
const char* const rule_engine_name = "python";
106+
static inline constexpr const char* const rule_engine_name = "python";
107107
using log_re = irods::experimental::log::rule_engine;
108108

109109
// Python thread states and other data from the interpreter
@@ -118,6 +118,13 @@ namespace
118118
static thread_local PyThreadState* ts_thread_old;
119119
// Reference counter for nested python operations
120120
static thread_local uint64_t ts_thread_refct = 0;
121+
122+
#if PY_VERSION_HEX >= 0x03080000
123+
// List of default module search paths
124+
static std::vector<std::wstring> default_module_search_paths;
125+
// Whether or not default_module_search_paths is populated
126+
static bool default_module_search_paths_set = false;
127+
#endif
121128
} //namespace python_state
122129
}
123130

@@ -378,6 +385,52 @@ namespace
378385
bp::class_<CallbackWrapper>("CallbackWrapper", bp::no_init)
379386
.def("__getattribute__", &CallbackWrapper::getAttribute);
380387
}
388+
389+
static inline void initialize_python(const std::string& _instance_name)
390+
{
391+
auto etc_irods_path = irods::get_irods_config_directory();
392+
393+
#if PY_VERSION_HEX >= 0x03080000
394+
if (python_state::default_module_search_paths_set) {
395+
PyConfig py_config;
396+
PyConfig_InitPythonConfig(&py_config);
397+
398+
py_config.install_signal_handlers = 0;
399+
400+
for (auto&& module_search_path : python_state::default_module_search_paths) {
401+
PyWideStringList_Append(&py_config.module_search_paths, module_search_path.c_str());
402+
}
403+
PyWideStringList_Append(&py_config.module_search_paths, etc_irods_path.generic_wstring().c_str());
404+
py_config.module_search_paths_set = 1;
405+
406+
PyStatus py_status = Py_InitializeFromConfig(&py_config);
407+
408+
if (!PyStatus_Exception(py_status)) {
409+
return;
410+
}
411+
412+
// clang-format off
413+
log_re::error({
414+
{"rule_engine_plugin", rule_engine_name},
415+
{"instance_name", _instance_name},
416+
{"log_message", "failed to initialize interpreter with PyConfig; falling back to legacy initialization"},
417+
{"PyStatus.exitcode", fmt::to_string(py_status.exitcode)},
418+
{"PyStatus.err_msg", py_status.err_msg},
419+
{"PyStatus.func", py_status.func},
420+
});
421+
// clang-format on
422+
}
423+
#endif
424+
425+
Py_InitializeEx(0);
426+
#if PY_VERSION_HEX < 0x03070000
427+
PyEval_InitThreads();
428+
#endif
429+
bp::object mod_sys = bp::import("sys");
430+
bp::list sys_path = bp::extract<bp::list>(mod_sys.attr("path"));
431+
sys_path.append(bp::str(etc_irods_path.generic_string().c_str()));
432+
}
433+
381434
} // anonymous namespace
382435

383436
static irods::error start(irods::default_re_ctx&, const std::string& _instance_name)
@@ -391,16 +444,8 @@ static irods::error start(irods::default_re_ctx&, const std::string& _instance_n
391444
PyImport_AppendInittab("plugin_wrappers", &PyInit_plugin_wrappers);
392445
PyImport_AppendInittab("irods_types", &PyInit_irods_types);
393446
PyImport_AppendInittab("irods_errors", &PyInit_irods_errors);
394-
Py_InitializeEx(0);
395-
#if PY_VERSION_HEX < 0x03070000
396-
PyEval_InitThreads();
397-
#endif
398-
boost::filesystem::path etc_irods_path = irods::get_irods_config_directory();
399-
std::string exec_str = "import sys\nsys.path.append('" + etc_irods_path.generic_string() + "')";
400447

401-
bp::object main_module = bp::import("__main__");
402-
bp::object main_namespace = main_module.attr("__dict__");
403-
bp::exec(exec_str.c_str(), main_namespace);
448+
initialize_python(_instance_name);
404449

405450
bp::object plugin_wrappers = bp::import("plugin_wrappers");
406451
bp::object irods_types = bp::import("irods_types");
@@ -1022,5 +1067,34 @@ extern "C" irods::pluggable_rule_engine<irods::default_re_ctx>* plugin_factory(c
10221067
std::function<irods::error(irods::default_re_ctx&, const std::string&, msParamArray_t*, irods::callback)>(
10231068
exec_rule_expression));
10241069

1070+
#if PY_VERSION_HEX >= 0x03080000
1071+
Py_InitializeEx(0);
1072+
try {
1073+
bp::object mod_sys = bp::import("sys");
1074+
bp::list sys_path = bp::extract<bp::list>(mod_sys.attr("path"));
1075+
std::size_t sys_path_len = bp::extract<std::size_t>(sys_path.attr("__len__")());
1076+
python_state::default_module_search_paths.reserve(sys_path_len);
1077+
for (std::size_t i = 0; i < sys_path_len; ++i) {
1078+
python_state::default_module_search_paths.push_back(bp::extract<std::wstring>(sys_path[i]));
1079+
}
1080+
python_state::default_module_search_paths_set = true;
1081+
}
1082+
catch (const bp::error_already_set&) {
1083+
const std::string formatted_python_exception = extract_python_exception();
1084+
// clang-format off
1085+
log_re::error({
1086+
{"rule_engine_plugin", rule_engine_name},
1087+
{"instance_name", _inst_name},
1088+
{"log_message", "caught python exception in plugin factory; will use legacy module search path initialization"},
1089+
{"python_exception", formatted_python_exception},
1090+
});
1091+
// clang-format on
1092+
python_state::default_module_search_paths.clear();
1093+
python_state::default_module_search_paths.shrink_to_fit();
1094+
python_state::default_module_search_paths_set = false;
1095+
}
1096+
Py_Finalize(); // We're not supposed to do this, but let's try it anyway.
1097+
#endif
1098+
10251099
return re;
10261100
}

0 commit comments

Comments
 (0)