use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::OnceLock;
use pyo3::Python;
use polyplug::error::LoaderError;
use crate::config::PythonConfig;
static PYTHON_INIT: OnceLock<()> = OnceLock::new();
static PYTHON_LOAD_LOCK: Mutex<()> = Mutex::new(());
pub(crate) fn acquire_load_lock() -> MutexGuard<'static, ()> {
PYTHON_LOAD_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
const PYTHON_LIB_NAME: &str = env!("POLYPLUG_PYTHON_LIB_NAME");
#[cfg(unix)]
fn promote_libpython_symbols() {
if PYTHON_LIB_NAME.is_empty() {
return;
}
let candidates: [String; 2] = [
format!("lib{PYTHON_LIB_NAME}.so.1.0"),
format!("lib{PYTHON_LIB_NAME}.so"),
];
for name in &candidates {
let opened: Result<libloading::os::unix::Library, libloading::Error> = unsafe {
libloading::os::unix::Library::open(
Some(name),
libloading::os::unix::RTLD_GLOBAL | libloading::os::unix::RTLD_LAZY,
)
};
if let Ok(lib) = opened {
core::mem::forget(lib);
return;
}
}
}
#[cfg(not(unix))]
fn promote_libpython_symbols() {}
pub(crate) fn ensure_python_initialized(config: &PythonConfig) -> Result<(), LoaderError> {
PYTHON_INIT.get_or_init(|| {
promote_libpython_symbols();
Python::initialize();
});
Python::attach(|py| {
let ver: pyo3::PythonVersionInfo<'_> = py.version_info();
let (req_major, req_minor): (u32, u32) = config.min_version;
if (ver.major as u32, ver.minor as u32) < (req_major, req_minor) {
return Err(LoaderError::InitFailed {
bundle: "python".to_owned(),
error: format!(
"runtime version mismatch: required {}.{}, found {}.{}",
req_major, req_minor, ver.major, ver.minor
),
});
}
Ok(())
})
}