Skip to content

Commit 6f0ce7a

Browse files
committed
Revert "Merge pull request ceph#55436 from tchaikov/mgr-python-3.12"
This reverts commit 8dffa87, reversing changes made to 80374da. This commit broke the import of the "mgr_module" module within the python modules in the mgr at least on python 3.6.8 that we currently use in our centos 8 stream based containers Failures would look like (removing beginning of log lines) Loading python module 'alerts' Module not found: 'mgr_module' Class not found in module 'alerts' Error loading module 'alerts': (22) Invalid argument Signed-off-by: Adam King <[email protected]>
1 parent a2cd8d5 commit 6f0ce7a

File tree

4 files changed

+80
-129
lines changed

4 files changed

+80
-129
lines changed

src/mgr/PyModule.cc

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

4848

4949
using std::string;
50+
using std::wstring;
5051

5152
// decode a Python exception into a string
5253
std::string handle_pyerror(
@@ -230,6 +231,72 @@ std::pair<int, std::string> PyModuleConfig::set_config(
230231
}
231232
}
232233

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+
233300
PyObject* PyModule::init_ceph_logger()
234301
{
235302
auto py_logger = PyModule_Create(&ceph_logger_module);
@@ -290,6 +357,17 @@ int PyModule::load(PyThreadState *pMainThreadState)
290357
return -EINVAL;
291358
} else {
292359
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;
293371
}
294372
}
295373
// Environment is all good, import the external module

src/mgr/PyModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ 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();
5455
int load_subclass_of(const char* class_name, PyObject** py_class);
5556

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

src/mgr/PyModuleRegistry.cc

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

1616
#include <filesystem>
17-
#include <boost/scope_exit.hpp>
1817

1918
#include "include/stringify.h"
2019
#include "common/errno.h"
@@ -47,74 +46,14 @@ void PyModuleRegistry::init()
4746

4847
// Set up global python interpreter
4948
#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
9849
Py_SetProgramName(const_cast<wchar_t*>(WCHAR(MGR_PYTHON_EXECUTABLE)));
50+
#undef WCHAR
9951
// Add more modules
10052
if (g_conf().get_val<bool>("daemonize")) {
10153
PyImport_AppendInittab("ceph_logger", PyModule::init_ceph_logger);
10254
}
10355
PyImport_AppendInittab("ceph_module", PyModule::init_ceph_module);
10456
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-
11857
#if PY_VERSION_HEX < 0x03090000
11958
// Let CPython know that we will be calling it back from other
12059
// threads in future.
@@ -278,72 +217,6 @@ void PyModuleRegistry::active_start(
278217
}
279218
}
280219

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-
347220
std::vector<std::string> PyModuleRegistry::probe_modules(const std::string &path) const
348221
{
349222
const auto opt = g_conf().get_val<std::string>("mgr_disabled_modules");

src/mgr/PyModuleRegistry.h

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

58-
static std::string get_site_packages();
5958
/**
6059
* Discover python modules from local disk
6160
*/

0 commit comments

Comments
 (0)