Skip to content

Commit 8dffa87

Browse files
authored
Merge pull request ceph#55436 from tchaikov/mgr-python-3.12
mgr: use un-deprecated APIs to initialize Python interpretor Reviewed-by: Ronen Friedman <[email protected]>
2 parents 80374da + 45832de commit 8dffa87

File tree

4 files changed

+129
-80
lines changed

4 files changed

+129
-80
lines changed

src/mgr/PyModule.cc

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ std::string PyModule::mgr_store_prefix = "mgr/";
4747

4848

4949
using std::string;
50-
using std::wstring;
5150

5251
// decode a Python exception into a string
5352
std::string handle_pyerror(
@@ -231,72 +230,6 @@ std::pair<int, std::string> PyModuleConfig::set_config(
231230
}
232231
}
233232

234-
std::string PyModule::get_site_packages()
235-
{
236-
std::stringstream site_packages;
237-
238-
// CPython doesn't auto-add site-packages dirs to sys.path for us,
239-
// but it does provide a module that we can ask for them.
240-
auto site_module = PyImport_ImportModule("site");
241-
ceph_assert(site_module);
242-
243-
auto site_packages_fn = PyObject_GetAttrString(site_module, "getsitepackages");
244-
if (site_packages_fn != nullptr) {
245-
auto site_packages_list = PyObject_CallObject(site_packages_fn, nullptr);
246-
ceph_assert(site_packages_list);
247-
248-
auto n = PyList_Size(site_packages_list);
249-
for (Py_ssize_t i = 0; i < n; ++i) {
250-
if (i != 0) {
251-
site_packages << ":";
252-
}
253-
site_packages << PyUnicode_AsUTF8(PyList_GetItem(site_packages_list, i));
254-
}
255-
256-
Py_DECREF(site_packages_list);
257-
Py_DECREF(site_packages_fn);
258-
} else {
259-
// Fall back to generating our own site-packages paths by imitating
260-
// what the standard site.py does. This is annoying but it lets us
261-
// run inside virtualenvs :-/
262-
263-
auto site_packages_fn = PyObject_GetAttrString(site_module, "addsitepackages");
264-
ceph_assert(site_packages_fn);
265-
266-
auto known_paths = PySet_New(nullptr);
267-
auto pArgs = PyTuple_Pack(1, known_paths);
268-
PyObject_CallObject(site_packages_fn, pArgs);
269-
Py_DECREF(pArgs);
270-
Py_DECREF(known_paths);
271-
Py_DECREF(site_packages_fn);
272-
273-
auto sys_module = PyImport_ImportModule("sys");
274-
ceph_assert(sys_module);
275-
auto sys_path = PyObject_GetAttrString(sys_module, "path");
276-
ceph_assert(sys_path);
277-
278-
dout(1) << "sys.path:" << dendl;
279-
auto n = PyList_Size(sys_path);
280-
bool first = true;
281-
for (Py_ssize_t i = 0; i < n; ++i) {
282-
dout(1) << " " << PyUnicode_AsUTF8(PyList_GetItem(sys_path, i)) << dendl;
283-
if (first) {
284-
first = false;
285-
} else {
286-
site_packages << ":";
287-
}
288-
site_packages << PyUnicode_AsUTF8(PyList_GetItem(sys_path, i));
289-
}
290-
291-
Py_DECREF(sys_path);
292-
Py_DECREF(sys_module);
293-
}
294-
295-
Py_DECREF(site_module);
296-
297-
return site_packages.str();
298-
}
299-
300233
PyObject* PyModule::init_ceph_logger()
301234
{
302235
auto py_logger = PyModule_Create(&ceph_logger_module);
@@ -357,17 +290,6 @@ int PyModule::load(PyThreadState *pMainThreadState)
357290
return -EINVAL;
358291
} else {
359292
pMyThreadState.set(thread_state);
360-
// Some python modules do not cope with an unpopulated argv, so lets
361-
// fake one. This step also picks up site-packages into sys.path.
362-
const wchar_t *argv[] = {L"ceph-mgr"};
363-
PySys_SetArgv(1, (wchar_t**)argv);
364-
// Configure sys.path to include mgr_module_path
365-
string paths = (g_conf().get_val<std::string>("mgr_module_path") + ':' +
366-
get_site_packages() + ':');
367-
wstring sys_path(wstring(begin(paths), end(paths)) + Py_GetPath());
368-
PySys_SetPath(const_cast<wchar_t*>(sys_path.c_str()));
369-
dout(10) << "Computed sys.path '"
370-
<< string(begin(sys_path), end(sys_path)) << "'" << dendl;
371293
}
372294
}
373295
// Environment is all good, import the external module

src/mgr/PyModule.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ class PyModule
5151
mutable ceph::mutex lock = ceph::make_mutex("PyModule::lock");
5252
private:
5353
const std::string module_name;
54-
std::string get_site_packages();
5554
int load_subclass_of(const char* class_name, PyObject** py_class);
5655

5756
// Did the MgrMap identify this module as one that should run?

src/mgr/PyModuleRegistry.cc

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "PyModuleRegistry.h"
1515

1616
#include <filesystem>
17+
#include <boost/scope_exit.hpp>
1718

1819
#include "include/stringify.h"
1920
#include "common/errno.h"
@@ -46,14 +47,74 @@ void PyModuleRegistry::init()
4647

4748
// Set up global python interpreter
4849
#define WCHAR(s) L ## #s
50+
#if PY_VERSION_HEX >= 0x03080000
51+
PyConfig py_config;
52+
// do not enable isolated mode, otherwise we would not be able to have access
53+
// to the site packages. since we cannot import any module before initializing
54+
// the interpreter, we would not be able to use "site" module for retrieving
55+
// the path to site packager. we import "site" module for retrieving
56+
// sitepackages in Python < 3.8 though, this does not apply to the
57+
// initialization with PyConfig.
58+
PyConfig_InitPythonConfig(&py_config);
59+
BOOST_SCOPE_EXIT_ALL(&py_config) {
60+
PyConfig_Clear(&py_config);
61+
};
62+
#if PY_VERSION_HEX >= 0x030b0000
63+
py_config.safe_path = 0;
64+
#endif
65+
py_config.parse_argv = 0;
66+
py_config.configure_c_stdio = 0;
67+
py_config.install_signal_handlers = 0;
68+
py_config.pathconfig_warnings = 0;
69+
// site_import is 1 by default, but set it explicitly as we do import
70+
// site packages manually for Python < 3.8
71+
py_config.site_import = 1;
72+
73+
PyStatus status;
74+
status = PyConfig_SetString(&py_config, &py_config.program_name, WCHAR(MGR_PYTHON_EXECUTABLE));
75+
ceph_assertf(!PyStatus_Exception(status), "PyConfig_SetString: %s:%s", status.func, status.err_msg);
76+
// Some python modules do not cope with an unpopulated argv, so lets
77+
// fake one. This step also picks up site-packages into sys.path.
78+
const wchar_t* argv[] = {L"ceph-mgr"};
79+
status = PyConfig_SetArgv(&py_config, 1, (wchar_t *const *)argv);
80+
ceph_assertf(!PyStatus_Exception(status), "PyConfig_SetArgv: %s:%s", status.func, status.err_msg);
81+
// Add more modules
82+
if (g_conf().get_val<bool>("daemonize")) {
83+
PyImport_AppendInittab("ceph_logger", PyModule::init_ceph_logger);
84+
}
85+
PyImport_AppendInittab("ceph_module", PyModule::init_ceph_module);
86+
// Configure sys.path to include mgr_module_path
87+
auto pythonpath_env = g_conf().get_val<std::string>("mgr_module_path");
88+
if (const char* pythonpath = getenv("PYTHONPATH")) {
89+
pythonpath_env += ":";
90+
pythonpath_env += pythonpath;
91+
}
92+
status = PyConfig_SetBytesString(&py_config, &py_config.pythonpath_env, pythonpath_env.data());
93+
ceph_assertf(!PyStatus_Exception(status), "PyConfig_SetBytesString: %s:%s", status.func, status.err_msg);
94+
dout(10) << "set PYTHONPATH to " << std::quoted(pythonpath_env) << dendl;
95+
status = Py_InitializeFromConfig(&py_config);
96+
ceph_assertf(!PyStatus_Exception(status), "Py_InitializeFromConfig: %s:%s", status.func, status.err_msg);
97+
#else
4998
Py_SetProgramName(const_cast<wchar_t*>(WCHAR(MGR_PYTHON_EXECUTABLE)));
50-
#undef WCHAR
5199
// Add more modules
52100
if (g_conf().get_val<bool>("daemonize")) {
53101
PyImport_AppendInittab("ceph_logger", PyModule::init_ceph_logger);
54102
}
55103
PyImport_AppendInittab("ceph_module", PyModule::init_ceph_module);
56104
Py_InitializeEx(0);
105+
const wchar_t *argv[] = {L"ceph-mgr"};
106+
PySys_SetArgv(1, (wchar_t**)argv);
107+
108+
std::string paths = (g_conf().get_val<std::string>("mgr_module_path") + ':' +
109+
get_site_packages() + ':');
110+
std::wstring sys_path(begin(paths), end(paths));
111+
sys_path += Py_GetPath();
112+
PySys_SetPath(const_cast<wchar_t*>(sys_path.c_str()));
113+
dout(10) << "Computed sys.path '"
114+
<< std::string(begin(sys_path), end(sys_path)) << "'" << dendl;
115+
#endif // PY_VERSION_HEX >= 0x03080000
116+
#undef WCHAR
117+
57118
#if PY_VERSION_HEX < 0x03090000
58119
// Let CPython know that we will be calling it back from other
59120
// threads in future.
@@ -217,6 +278,72 @@ void PyModuleRegistry::active_start(
217278
}
218279
}
219280

281+
std::string PyModuleRegistry::get_site_packages()
282+
{
283+
std::stringstream site_packages;
284+
285+
// CPython doesn't auto-add site-packages dirs to sys.path for us,
286+
// but it does provide a module that we can ask for them.
287+
auto site_module = PyImport_ImportModule("site");
288+
ceph_assert(site_module);
289+
290+
auto site_packages_fn = PyObject_GetAttrString(site_module, "getsitepackages");
291+
if (site_packages_fn != nullptr) {
292+
auto site_packages_list = PyObject_CallObject(site_packages_fn, nullptr);
293+
ceph_assert(site_packages_list);
294+
295+
auto n = PyList_Size(site_packages_list);
296+
for (Py_ssize_t i = 0; i < n; ++i) {
297+
if (i != 0) {
298+
site_packages << ":";
299+
}
300+
site_packages << PyUnicode_AsUTF8(PyList_GetItem(site_packages_list, i));
301+
}
302+
303+
Py_DECREF(site_packages_list);
304+
Py_DECREF(site_packages_fn);
305+
} else {
306+
// Fall back to generating our own site-packages paths by imitating
307+
// what the standard site.py does. This is annoying but it lets us
308+
// run inside virtualenvs :-/
309+
310+
auto site_packages_fn = PyObject_GetAttrString(site_module, "addsitepackages");
311+
ceph_assert(site_packages_fn);
312+
313+
auto known_paths = PySet_New(nullptr);
314+
auto pArgs = PyTuple_Pack(1, known_paths);
315+
PyObject_CallObject(site_packages_fn, pArgs);
316+
Py_DECREF(pArgs);
317+
Py_DECREF(known_paths);
318+
Py_DECREF(site_packages_fn);
319+
320+
auto sys_module = PyImport_ImportModule("sys");
321+
ceph_assert(sys_module);
322+
auto sys_path = PyObject_GetAttrString(sys_module, "path");
323+
ceph_assert(sys_path);
324+
325+
dout(1) << "sys.path:" << dendl;
326+
auto n = PyList_Size(sys_path);
327+
bool first = true;
328+
for (Py_ssize_t i = 0; i < n; ++i) {
329+
dout(1) << " " << PyUnicode_AsUTF8(PyList_GetItem(sys_path, i)) << dendl;
330+
if (first) {
331+
first = false;
332+
} else {
333+
site_packages << ":";
334+
}
335+
site_packages << PyUnicode_AsUTF8(PyList_GetItem(sys_path, i));
336+
}
337+
338+
Py_DECREF(sys_path);
339+
Py_DECREF(sys_module);
340+
}
341+
342+
Py_DECREF(site_module);
343+
344+
return site_packages.str();
345+
}
346+
220347
std::vector<std::string> PyModuleRegistry::probe_modules(const std::string &path) const
221348
{
222349
const auto opt = g_conf().get_val<std::string>("mgr_disabled_modules");

src/mgr/PyModuleRegistry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class PyModuleRegistry
5555
// before ClusterState exists.
5656
MgrMap mgr_map;
5757

58+
static std::string get_site_packages();
5859
/**
5960
* Discover python modules from local disk
6061
*/

0 commit comments

Comments
 (0)