in source/neuropod/backends/python_bridge/python_bridge.cc [66:169]
std::unique_ptr<py::gil_scoped_release> maybe_initialize()
{
// Make sure our logging is initialized
init_logging();
if (Py_IsInitialized()) // NOLINT(readability-implicit-bool-conversion)
{
return nullptr;
}
#ifndef __APPLE__
// This binary is already linked against `libpython`; the dlopen just
// promotes it to RTLD_GLOBAL.
#define PYTHON_LIB_NAME "libpython" STR(PYTHON_VERSION) ".so.1.0"
#define PYTHON_LIB_M_NAME "libpython" STR(PYTHON_VERSION) "m.so.1.0"
void *libpython = dlopen(PYTHON_LIB_NAME, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
if (libpython == nullptr)
{
libpython = dlopen(PYTHON_LIB_M_NAME, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
}
if (libpython == nullptr)
{
const auto err = dlerror();
if (err == nullptr)
{
NEUROPOD_ERROR("Failed to promote libpython to RTLD_GLOBAL; this likely means the neuropod backend library "
"was not built correctly");
}
else
{
NEUROPOD_ERROR("Failed to promote libpython to RTLD_GLOBAL. Error from dlopen: {}", err);
}
}
#endif
// Get the current backend directory
auto sopath = get_current_so_path();
if (sopath == nullptr)
{
NEUROPOD_ERROR("Error getting path of current shared object. Cannot load python.");
}
const auto sodir = fs::absolute(sopath).parent_path();
// Get the path for pythonhone
#ifdef __APPLE__
const auto pythonhome = (sodir / "Python.framework/Versions/Current").string();
#else
const auto pythonhome =
(sodir / ("opt/python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION))).string();
#endif
if (std::getenv("NEUROPOD_DISABLE_PYTHON_ISOLATION") == nullptr)
{
// Isolate from the environment, set PYTOHNHOME to the packaged python environment
SPDLOG_TRACE("Setting PYTHONHOME to isolated environment at {}", pythonhome);
setenv("PYTHONHOME", pythonhome.c_str(), true); // NOLINT(readability-implicit-bool-conversion)
}
else if (std::getenv("PYTHONHOME") == nullptr)
{
// We're not being asked to isolate from the environment and we don't have pythonhome already set
// Check if we have a virtualenv
if (auto venv_path = std::getenv("VIRTUAL_ENV"))
{
setenv("PYTHONHOME", venv_path, true); // NOLINT(readability-implicit-bool-conversion)
}
}
// Start the interpreter
py::initialize_interpreter();
// pybind11 adds the current working dir to the path and we don't want that
py::exec(R"(
import sys
if sys.path[-1] == ".":
sys.path.pop()
)");
// Add the bootstrap library to the pythonpath
py::module::import("sys").attr("path").cast<py::list>().append((sodir / "bootstrap").string());
// Set the executable path
py::module::import("sys").attr("executable") =
(fs::path(pythonhome) /
("bin/python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION)))
.string();
// For code coverage
if (std::getenv("COVERAGE_PROCESS_START") != nullptr)
{
// Get the coverage dependency
py::module::import("_neuropod_native_bootstrap.pip_utils").attr("_load_deps_internal")("coverage==5.3");
// Start coverage collection
py::module::import("coverage").attr("process_startup")();
}
// TODO: shutdown the interpreter once we know that there are no more python objects left
// atexit(py::finalize_interpreter);
return stdx::make_unique<py::gil_scoped_release>();
}