From 24a123119ace78b58bbc3e852aa69977faf46d90 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 2 Aug 2022 17:31:33 +0200 Subject: [PATCH] obs-scripting: Fix issues between runtime and compile-time versions Calling `PyEval_InitThreads` has been deprecated in Python 3.7 and the function itself will be removed in Python 3.11. The current check guards this function behind a version check that only happens at compile time. This in turn leads to crashes when run on Python 3.6, as the necessary initialization for `PyEval_ReleaseThread` did not take place. This commit ensures the manual initialization takes place based on the runtime version of Python and avoids loading the associated symbols on Python 3.9 or later. --- .../obs-scripting-python-import.c | 34 +++++++++++----- .../obs-scripting-python-import.h | 21 +++++----- deps/obs-scripting/obs-scripting-python.c | 40 ++++++------------- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/deps/obs-scripting/obs-scripting-python-import.c b/deps/obs-scripting/obs-scripting-python-import.c index db926fc39..187990ef0 100644 --- a/deps/obs-scripting/obs-scripting-python-import.c +++ b/deps/obs-scripting/obs-scripting-python-import.c @@ -42,7 +42,7 @@ #define PY_MAJOR_VERSION_MAX 3 #define PY_MINOR_VERSION_MAX 10 -bool import_python(const char *python_path) +bool import_python(const char *python_path, python_version_t *python_version) { struct dstr lib_path; bool success = false; @@ -51,6 +51,12 @@ bool import_python(const char *python_path) if (!python_path) python_path = ""; + if (!python_version) { + blog(LOG_DEBUG, + "[Python] Invalid python_version pointer provided."); + goto fail; + } + dstr_init_copy(&lib_path, python_path); dstr_replace(&lib_path, "\\", "/"); if (!dstr_is_empty(&lib_path)) { @@ -96,6 +102,9 @@ bool import_python(const char *python_path) goto fail; } + python_version->major = PY_MAJOR_VERSION_MAX; + python_version->minor = minor_version; + #define IMPORT_FUNC(x) \ do { \ Import_##x = os_dlsym(lib, #x); \ @@ -133,8 +142,12 @@ bool import_python(const char *python_path) IMPORT_FUNC(Py_Initialize); IMPORT_FUNC(Py_Finalize); IMPORT_FUNC(Py_IsInitialized); - IMPORT_FUNC(PyEval_InitThreads); - IMPORT_FUNC(PyEval_ThreadsInitialized); + + if (python_version->major == 3 && python_version->minor < 7) { + IMPORT_FUNC(PyEval_InitThreads); + IMPORT_FUNC(PyEval_ThreadsInitialized); + } + IMPORT_FUNC(PyEval_ReleaseThread); IMPORT_FUNC(PySys_SetArgv); IMPORT_FUNC(PyImport_ImportModule); @@ -148,9 +161,10 @@ bool import_python(const char *python_path) IMPORT_FUNC(PyDict_GetItemString); IMPORT_FUNC(PyDict_SetItemString); IMPORT_FUNC(PyCFunction_NewEx); -#if PY_VERSION_HEX > 0x030900b0 - IMPORT_FUNC(PyCMethod_New); -#endif + + if (python_version->major == 3 && python_version->minor >= 9) + IMPORT_FUNC(PyCMethod_New); + IMPORT_FUNC(PyModule_GetDict); IMPORT_FUNC(PyModule_GetNameObject); IMPORT_FUNC(PyModule_AddObject); @@ -181,14 +195,12 @@ bool import_python(const char *python_path) IMPORT_FUNC(PyArg_VaParse); IMPORT_FUNC(_Py_NoneStruct); IMPORT_FUNC(PyTuple_New); - + if (python_version->major == 3 && python_version->minor >= 9) { + IMPORT_FUNC(PyType_GetFlags); + } #if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 IMPORT_FUNC(_Py_Dealloc); #endif -#if PY_VERSION_HEX >= 0x030900b0 - IMPORT_FUNC(PyType_GetFlags); -#endif - #undef IMPORT_FUNC success = true; diff --git a/deps/obs-scripting/obs-scripting-python-import.h b/deps/obs-scripting/obs-scripting-python-import.h index 2fe99b55a..bf61bf08e 100644 --- a/deps/obs-scripting/obs-scripting-python-import.h +++ b/deps/obs-scripting/obs-scripting-python-import.h @@ -50,6 +50,11 @@ #define PY_EXTERN extern #endif +typedef struct python_version { + int major; + int minor; +} python_version_t; + PY_EXTERN int (*Import_PyType_Ready)(PyTypeObject *); PY_EXTERN PyObject *(*Import_PyObject_GenericGetAttr)(PyObject *, PyObject *); PY_EXTERN int (*Import_PyObject_IsTrue)(PyObject *); @@ -99,10 +104,8 @@ PY_EXTERN int (*Import_PyDict_SetItemString)(PyObject *dp, const char *key, PyObject *item); PY_EXTERN PyObject *(*Import_PyCFunction_NewEx)(PyMethodDef *, PyObject *, PyObject *); -#if PY_VERSION_HEX > 0x030900b0 PY_EXTERN PyObject *(*Import_PyCMethod_New)(PyMethodDef *, PyObject *, PyObject *, PyTypeObject *); -#endif PY_EXTERN PyObject *(*Import_PyModule_GetDict)(PyObject *); PY_EXTERN PyObject *(*Import_PyModule_GetNameObject)(PyObject *); PY_EXTERN int (*Import_PyModule_AddObject)(PyObject *, const char *, @@ -139,23 +142,17 @@ PY_EXTERN PyObject *(*Import_PyLong_FromUnsignedLongLong)(unsigned long long); PY_EXTERN int (*Import_PyArg_VaParse)(PyObject *, const char *, va_list); PY_EXTERN PyObject(*Import__Py_NoneStruct); PY_EXTERN PyObject *(*Import_PyTuple_New)(Py_ssize_t size); -#if PY_VERSION_HEX >= 0x030900b0 PY_EXTERN int (*Import_PyType_GetFlags)(PyTypeObject *o); -#endif #if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 PY_EXTERN void (*Import__Py_Dealloc)(PyObject *obj); #endif -extern bool import_python(const char *python_path); +extern bool import_python(const char *python_path, + python_version_t *python_version); #ifndef NO_REDEFS #define PyType_Ready Import_PyType_Ready -#if PY_VERSION_HEX >= 0x030900b0 #define PyType_GetFlags Import_PyType_GetFlags -#endif -#if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 -#define _Py_Dealloc Import__Py_Dealloc -#endif #define PyObject_GenericGetAttr Import_PyObject_GenericGetAttr #define PyObject_IsTrue Import_PyObject_IsTrue #define Py_DecRef Import_Py_DecRef @@ -231,6 +228,9 @@ extern bool import_python(const char *python_path); #define PyArg_VaParse Import_PyArg_VaParse #define _Py_NoneStruct (*Import__Py_NoneStruct) #define PyTuple_New Import_PyTuple_New +#if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 +#define _Py_Dealloc Import__Py_Dealloc +#endif #if PY_VERSION_HEX >= 0x030800f0 static inline void Import__Py_DECREF(const char *filename, int lineno, PyObject *op) @@ -262,7 +262,6 @@ static inline void Import__Py_XDECREF(PyObject *op) #undef Py_XDECREF #define Py_XDECREF(op) Import__Py_XDECREF(_PyObject_CAST(op)) #endif // PY_VERSION_HEX >= 0x030800f0 - #if PY_VERSION_HEX >= 0x030900b0 static inline int Import_PyType_HasFeature(PyTypeObject *type, unsigned long feature) diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 947e34838..35f549938 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -49,6 +49,7 @@ sys.stderr = stderr_logger()\n"; #if RUNTIME_LINK static wchar_t home_path[1024] = {0}; +static python_version_t python_version = {0}; #endif DARRAY(char *) python_paths; @@ -1615,12 +1616,7 @@ bool obs_scripting_load_python(const char *python_path) /* Use external python on windows and mac */ #if RUNTIME_LINK -#if 0 - struct dstr old_path = {0}; - struct dstr new_path = {0}; -#endif - - if (!import_python(python_path)) + if (!import_python(python_path, &python_version)) return false; if (python_path && *python_path) { @@ -1634,11 +1630,6 @@ bool obs_scripting_load_python(const char *python_path) 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="); - _putenv("PATH="); #endif } #else @@ -1649,24 +1640,17 @@ bool obs_scripting_load_python(const char *python_path) if (!Py_IsInitialized()) return false; -#if 0 -#ifdef _DEBUG - if (pythondir && *pythondir) { - dstr_printf(&new_path, "PATH=%s", old_path.array); - _putenv(new_path.array); +#if RUNTIME_LINK + if (python_version.major == 3 && python_version.minor < 7) { +#elif PY_VERSION_HEX < 0x030700b0 + if (true) { +#else + if (false) { +#endif + PyEval_InitThreads(); + if (!PyEval_ThreadsInitialized()) + return false; } -#endif - - bfree(pythondir); - dstr_free(&new_path); - dstr_free(&old_path); -#endif - -#if PY_VERSION_HEX < 0x03070000 - PyEval_InitThreads(); - if (!PyEval_ThreadsInitialized()) - return false; -#endif /* ---------------------------------------------- */ /* Must set arguments for guis to work */