std::unique_ptr maybe_initialize()

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>();
}