From ab21c7e5b098170ca468edc490c2d6705c65e021 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Wed, 13 Jul 2022 21:49:27 +0200 Subject: [PATCH] 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). --- .../obs-scripting-python-import.c | 58 +++++++++++++++---- .../obs-scripting-python-import.h | 10 ++-- deps/obs-scripting/obs-scripting-python.c | 9 +++ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/deps/obs-scripting/obs-scripting-python-import.c b/deps/obs-scripting/obs-scripting-python-import.c index 942a691dd..db926fc39 100644 --- a/deps/obs-scripting/obs-scripting-python-import.c +++ b/deps/obs-scripting/obs-scripting-python-import.c @@ -27,22 +27,26 @@ #endif #ifdef _WIN32 -#define SO_EXT ".dll" +#include +#include +#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: diff --git a/deps/obs-scripting/obs-scripting-python-import.h b/deps/obs-scripting/obs-scripting-python-import.h index 82f9472be..2fe99b55a 100644 --- a/deps/obs-scripting/obs-scripting-python-import.h +++ b/deps/obs-scripting/obs-scripting-python-import.h @@ -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 diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 2603fd704..947e34838 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -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=");