Ask `python3` (or `python`) on PATH where its own libpython lives. * This handles pyenv / uv-managed / framework Pythons whose libdir * isn't on the dyld search path. The call is one-shot and only runs * during the first `LeanPy.Python.init`. */
| 337 | * isn't on the dyld search path. The call is one-shot and only runs |
| 338 | * during the first `LeanPy.Python.init`. */ |
| 339 | static int try_python_subprocess(const char *python_exe) { |
| 340 | char cmd[512]; |
| 341 | int n = snprintf(cmd, sizeof(cmd), |
| 342 | "%s -c 'import os, sysconfig; " |
| 343 | "libdir = sysconfig.get_config_var(\"LIBDIR\"); " |
| 344 | "soname = sysconfig.get_config_var(\"INSTSONAME\") " |
| 345 | "or sysconfig.get_config_var(\"LDLIBRARY\"); " |
| 346 | "print(os.path.join(libdir, soname) " |
| 347 | "if libdir and soname else \"\")' 2>/dev/null", |
| 348 | python_exe); |
| 349 | if (n <= 0 || (size_t)n >= sizeof(cmd)) return 0; |
| 350 | FILE *fp = popen(cmd, "r"); |
| 351 | if (!fp) return 0; |
| 352 | char buf[4096]; |
| 353 | char *line = fgets(buf, sizeof(buf), fp); |
| 354 | pclose(fp); |
| 355 | if (!line) return 0; |
| 356 | size_t len = strlen(line); |
| 357 | while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) line[--len] = '\0'; |
| 358 | if (len == 0) return 0; |
| 359 | py_handle = dlopen(line, RTLD_NOW | RTLD_GLOBAL); |
| 360 | return py_handle != NULL; |
| 361 | } |
| 362 | |
| 363 | static int try_load_python(void) { |
| 364 | /* 1. Prefer libpython already loaded into the process (this is |