Skip to content

Commit 45832de

Browse files
committed
mgr: add site package paths in PyModuleRegistry
before this change, we add the paths of site packages to sys.path when starting subinterpretors for each of the mgr modules. this works just fine. but in Python 3.11, it deprecates `PySys_SetPath()` in favor of PyConfig machinary, which sets the module search paths in PyConfig, before calling `Py_InitializeFromConfig()`. so, to set the module search paths with the new machinary, we need to do this in `PyModuleRegistry`, where we initialize the global Python interpretor using the new PyConfig machinary. and since we've switched to the new PyConfig machinary when compiling with Python 3.8 and up. in this change, to unify the implementation of pre and post Python 3.8, we set the module search paths in PyModuleRegistry. because PyConfig imports the site packages by default, and we are allowed to append a new path to the existing search paths, we just append the configured `mgr_module_path` when compiling with Python 3.8 and up. and when it comes to lower versions of Python, the existing behavior is preserved. this change should silence the compiling warning like: ``` /var/ssd/ceph/src/mgr/PyModule.cc:368:20: warning: ‘void PySys_SetPath(const wchar_t*)’ is deprecated [-Wdeprecated-declarations] 368 | PySys_SetPath(const_cast<wchar_t*>(sys_path.c_str())); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/include/python3.12/sysmodule.h:15:38: note: declared here 15 | Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetPath(const wchar_t *); | ^~~~~~~~~~~~~ ``` Signed-off-by: Kefu Chai <[email protected]>
1 parent e417c1c commit 45832de

File tree

4 files changed

+87
-75
lines changed

4 files changed

+87
-75
lines changed

src/mgr/PyModule.cc

Lines changed: 0 additions & 74 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,13 +290,6 @@ int PyModule::load(PyThreadState *pMainThreadState)
357290
return -EINVAL;
358291
} else {
359292
pMyThreadState.set(thread_state);
360-
// Configure sys.path to include mgr_module_path
361-
string paths = (g_conf().get_val<std::string>("mgr_module_path") + ':' +
362-
get_site_packages() + ':');
363-
wstring sys_path(wstring(begin(paths), end(paths)) + Py_GetPath());
364-
PySys_SetPath(const_cast<wchar_t*>(sys_path.c_str()));
365-
dout(10) << "Computed sys.path '"
366-
<< string(begin(sys_path), end(sys_path)) << "'" << dendl;
367293
}
368294
}
369295
// 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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ void PyModuleRegistry::init()
6666
py_config.configure_c_stdio = 0;
6767
py_config.install_signal_handlers = 0;
6868
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;
6972

7073
PyStatus status;
7174
status = PyConfig_SetString(&py_config, &py_config.program_name, WCHAR(MGR_PYTHON_EXECUTABLE));
@@ -80,6 +83,15 @@ void PyModuleRegistry::init()
8083
PyImport_AppendInittab("ceph_logger", PyModule::init_ceph_logger);
8184
}
8285
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;
8395
status = Py_InitializeFromConfig(&py_config);
8496
ceph_assertf(!PyStatus_Exception(status), "Py_InitializeFromConfig: %s:%s", status.func, status.err_msg);
8597
#else
@@ -92,6 +104,14 @@ void PyModuleRegistry::init()
92104
Py_InitializeEx(0);
93105
const wchar_t *argv[] = {L"ceph-mgr"};
94106
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;
95115
#endif // PY_VERSION_HEX >= 0x03080000
96116
#undef WCHAR
97117

@@ -258,6 +278,72 @@ void PyModuleRegistry::active_start(
258278
}
259279
}
260280

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+
261347
std::vector<std::string> PyModuleRegistry::probe_modules(const std::string &path) const
262348
{
263349
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)