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.
master
PatTheMav 2022-08-02 17:31:33 +02:00 committed by Ryan Foster
parent be68403fa4
commit 24a123119a
3 changed files with 45 additions and 50 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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 */