obs-scripting: Add support for multiple Python 3 versions

This changes the way obs-scripting looks for and loads an available
Python 3 library. It tries to find a best possible version (starting
with Python 3.10) down to and including Python 3.6 by existing file
naming conventions and loads the most recent variant it can find.

User specified search path is either a Python installation directory
(Windows), or a Framework directory containing `Python.framework`
(macOS). The dll or dylib names are composed automatically.
The Python home path is also composed automatically on macOS (where
it has to point inside the Framework directory).
master
PatTheMav 2022-07-13 21:49:27 +02:00 committed by Ryan Foster
parent 47b3ff5e64
commit ab21c7e5b0
3 changed files with 59 additions and 18 deletions

View File

@ -27,22 +27,26 @@
#endif
#ifdef _WIN32
#define SO_EXT ".dll"
#include <windows.h>
#include <io.h>
#define F_OK 0
#define access _access
#define VERSION_PATTERN "%d%d"
#define FILE_PATTERN "python%s.dll"
#define PATH_MAX MAX_PATH
#elif __APPLE__
#define SO_EXT ".dylib"
#define VERSION_PATTERN "%d.%d"
#define FILE_PATTERN "Python.framework/Versions/Current/lib/libpython%s.dylib"
#endif
#ifdef __APPLE__
#define PYTHON_LIB_SUBDIR "lib/"
#else
#define PYTHON_LIB_SUBDIR ""
#endif
#define PY_MAJOR_VERSION_MAX 3
#define PY_MINOR_VERSION_MAX 10
bool import_python(const char *python_path)
{
struct dstr lib_path;
bool success = false;
void *lib;
void *lib = NULL;
if (!python_path)
python_path = "";
@ -50,11 +54,42 @@ bool import_python(const char *python_path)
dstr_init_copy(&lib_path, python_path);
dstr_replace(&lib_path, "\\", "/");
if (!dstr_is_empty(&lib_path)) {
dstr_cat(&lib_path, "/" PYTHON_LIB_SUBDIR);
dstr_cat(&lib_path, "/");
}
dstr_cat(&lib_path, PYTHON_LIB SO_EXT);
lib = os_dlopen(lib_path.array);
struct dstr lib_candidate_path;
dstr_init_copy(&lib_candidate_path, lib_path.array);
char cur_version[5];
char next_version[5];
char temp[PATH_MAX];
sprintf(cur_version, VERSION_PATTERN, PY_MAJOR_VERSION_MAX,
PY_MINOR_VERSION_MAX);
sprintf(temp, FILE_PATTERN, cur_version);
dstr_cat(&lib_candidate_path, temp);
int minor_version = PY_MINOR_VERSION_MAX;
do {
if (access(lib_candidate_path.array, F_OK) == 0) {
lib = os_dlopen(lib_candidate_path.array);
}
if (lib) {
break;
}
sprintf(cur_version, VERSION_PATTERN, PY_MAJOR_VERSION_MAX,
minor_version);
sprintf(next_version, VERSION_PATTERN, PY_MAJOR_VERSION_MAX,
--minor_version);
dstr_replace(&lib_candidate_path, cur_version, next_version);
} while (minor_version > 5);
dstr_free(&lib_candidate_path);
if (!lib) {
blog(LOG_WARNING, "[Python] Could not load library: %s",
lib_path.array);
@ -155,7 +190,6 @@ bool import_python(const char *python_path)
#endif
#undef IMPORT_FUNC
success = true;
fail:

View File

@ -44,7 +44,6 @@
#endif
#if RUNTIME_LINK
#ifdef NO_REDEFS
#define PY_EXTERN
#else
@ -262,7 +261,7 @@ static inline void Import__Py_XDECREF(PyObject *op)
#undef Py_XDECREF
#define Py_XDECREF(op) Import__Py_XDECREF(_PyObject_CAST(op))
#endif
#endif // PY_VERSION_HEX >= 0x030800f0
#if PY_VERSION_HEX >= 0x030900b0
static inline int Import_PyType_HasFeature(PyTypeObject *type,
@ -271,8 +270,7 @@ static inline int Import_PyType_HasFeature(PyTypeObject *type,
return ((PyType_GetFlags(type) & feature) != 0);
}
#define PyType_HasFeature(t, f) Import_PyType_HasFeature(t, f)
#endif
#endif // PY_VERSION_HEX >= 0x030900b0
#endif
#endif
#endif // NO_REDEFS
#endif // RUNTIME_LINK

View File

@ -1624,8 +1624,17 @@ bool obs_scripting_load_python(const char *python_path)
return false;
if (python_path && *python_path) {
#ifdef __APPLE__
char temp[PATH_MAX];
sprintf(temp, "%s/Python.framework/Versions/Current",
python_path);
os_utf8_to_wcs(temp, 0, home_path, PATH_MAX);
Py_SetPythonHome(home_path);
#else
os_utf8_to_wcs(python_path, 0, home_path, 1024);
Py_SetPythonHome(home_path);
#endif
#if 0
dstr_copy(&old_path, getenv("PATH"));
_putenv("PYTHONPATH=");