commit 424d94a67f77cda61a0fa3e8e53c1e2ea618e519
Author: Michael Cronenworth <mike(a)cchtml.com>
Date: Thu Oct 29 13:03:34 2020 -0500
Upstream patches for Python 3.9 support found in F33+
kodi-19-python3-0.patch | 216 +++++++++++++++++
kodi-19-python3-1.patch | 345 ++++++++++++++++++++++++++
kodi-19-python3-2.patch | 632 ++++++++++++++++++++++++++++++++++++++++++++++++
kodi-19-python3-3.patch | 82 +++++++
kodi-19-python3-4.patch | 66 +++++
kodi.spec | 17 +-
6 files changed, 1357 insertions(+), 1 deletion(-)
---
diff --git a/kodi-19-python3-0.patch b/kodi-19-python3-0.patch
new file mode 100644
index 0000000..f2a6eb7
--- /dev/null
+++ b/kodi-19-python3-0.patch
@@ -0,0 +1,216 @@
+From 245a1914955409b3ab5361872caf0a96cbc28fc9 Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard(a)kodi.tv>
+Date: Sun, 30 Aug 2020 15:21:38 +1000
+Subject: [PATCH] [Python] Cleanup
+
+---
+ xbmc/interfaces/python/PythonInvoker.cpp | 13 ++--
+ xbmc/interfaces/python/XBPython.cpp | 77 +++++-------------------
+ xbmc/interfaces/python/XBPython.h | 2 -
+ 3 files changed, 19 insertions(+), 73 deletions(-)
+
+diff --git a/xbmc/interfaces/python/PythonInvoker.cpp
b/xbmc/interfaces/python/PythonInvoker.cpp
+index c559723a16f9..e00cc1ff8632 100644
+--- a/xbmc/interfaces/python/PythonInvoker.cpp
++++ b/xbmc/interfaces/python/PythonInvoker.cpp
+@@ -174,11 +174,8 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ {
+ if (!m_threadState)
+ {
+- // TODO: Re-write everything.
+- // this is a TOTAL hack. We need the GIL but we need to borrow a PyThreadState in
order to get it
+- // as of Python 3.2 since PyEval_AcquireLock is deprecated
+- extern PyThreadState* savestate;
+- PyEval_RestoreThread(savestate);
++ PyThreadState* ts = PyThreadState_New(PyInterpreterState_Main());
++ PyEval_RestoreThread(ts);
+ l_threadState = Py_NewInterpreter();
+ PyEval_ReleaseThread(l_threadState);
+ if (l_threadState == NULL)
+@@ -588,7 +585,8 @@ void CPythonInvoker::onExecutionDone()
+ // unregister the language hook
+ m_languageHook->UnregisterMe();
+
+- PyEval_ReleaseLock();
++ PyThreadState_Swap(PyInterpreterState_ThreadHead(PyInterpreterState_Main()));
++ PyEval_SaveThread();
+
+ // set stopped event - this allows ::stop to run and kill remaining threads
+ // this event has to be fired without holding m_critical
+@@ -605,8 +603,7 @@ void CPythonInvoker::onExecutionDone()
+
+ void CPythonInvoker::onExecutionFailed()
+ {
+- PyThreadState_Swap(NULL);
+- PyEval_ReleaseLock();
++ PyEval_SaveThread();
+
+ setState(InvokerStateFailed);
+ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): abnormally terminating python
thread", GetId(), m_sourceFile.c_str());
+diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp
+index 047896ba481c..666ac17d3a47 100644
+--- a/xbmc/interfaces/python/XBPython.cpp
++++ b/xbmc/interfaces/python/XBPython.cpp
+@@ -26,7 +26,6 @@
+ #include "settings/AdvancedSettings.h"
+ #include "settings/SettingsComponent.h"
+
+-#include "threads/SystemClock.h"
+ #include "interfaces/AnnouncementManager.h"
+
+ #include "interfaces/legacy/Monitor.h"
+@@ -35,15 +34,11 @@
+ #include "interfaces/python/PythonInvoker.h"
+ #include "ServiceBroker.h"
+
+-PyThreadState* savestate;
+-
+ XBPython::XBPython()
+ {
+ m_bInitialized = false;
+ m_mainThreadState = NULL;
+ m_iDllScriptCounter = 0;
+- m_endtime = 0;
+- m_pDll = NULL;
+ m_vecPlayerCallbackList.clear();
+ m_vecMonitorCallbackList.clear();
+
+@@ -452,44 +447,6 @@ void XBPython::UnloadExtensionLibs()
+ m_extensions.clear();
+ }
+
+-// Always called with the lock held on m_critSection
+-void XBPython::Finalize()
+-{
+- XBMC_TRACE;
+- if (m_bInitialized)
+- {
+- CLog::Log(LOGINFO, "Python, unloading python shared library because no scripts
are running anymore");
+-
+- // set the m_bInitialized flag before releasing the lock. This will prevent
+- // Other methods that rely on this flag from an incorrect interpretation.
+- m_bInitialized = false;
+- PyThreadState* curTs = (PyThreadState*)m_mainThreadState;
+- m_mainThreadState = NULL; // clear the main thread state before releasing the lock
+- {
+- CSingleExit exit(m_critSection);
+- PyEval_AcquireThread(curTs);
+-
+- Py_Finalize();
+- PyEval_ReleaseLock();
+- }
+-
+-#if !(defined(TARGET_DARWIN) || defined(TARGET_WINDOWS))
+- UnloadExtensionLibs();
+-#endif
+-
+- // first free all dlls loaded by python, after that unload python (this is done by
UnloadPythonDlls
+-#if !(defined(TARGET_DARWIN) || defined(TARGET_WINDOWS))
+- DllLoaderContainer::UnloadPythonDlls();
+-#endif
+-#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) &&
!defined(TARGET_FREEBSD)
+- // we can't release it on windows, as this is done in UnloadPythonDlls() for
win32 (see above).
+- // The implementation for linux needs looking at - UnloadPythonDlls() currently only
searches for "python36.dll"
+- // The implementation for osx can never unload the python dylib.
+- DllLoaderContainer::ReleaseModule(m_pDll);
+-#endif
+- }
+-}
+-
+ void XBPython::Uninitialize()
+ {
+ // don't handle any more announcements as most scripts are probably already
+@@ -504,7 +461,7 @@ void XBPython::Uninitialize()
+ lock.Leave(); //unlock here because the python thread might lock when it exits
+
+ // cleanup threads that are still running
+- tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls
OnScriptFinalized
++ tmpvec.clear();
+ }
+
+ void XBPython::Process()
+@@ -527,13 +484,7 @@ void XBPython::Process()
+ lock.Leave();
+
+ //delete scripts which are done
+- tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls
OnScriptFinalized
+-
+- CSingleLock l2(m_critSection);
+- if(m_iDllScriptCounter == 0 && (XbmcThreads::SystemClockMillis() -
m_endtime) > 10000 )
+- {
+- Finalize();
+- }
++ tmpvec.clear();
+ }
+ }
+
+@@ -557,10 +508,6 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
+ // at
http://docs.python.org/using/cmdline.html#environment-variables
+
+ #if !defined(TARGET_WINDOWS) && !defined(TARGET_ANDROID)
+- /* PYTHONOPTIMIZE is set off intentionally when using external Python.
+- Reason for this is because we cannot be sure what version of Python
+- was used to compile the various Python object files (i.e. .pyo,
+- .pyc, etc.). */
+ // check if we are running as real xbmc.app or just binary
+ if (!CUtil::GetFrameworksPath(true).empty())
+ {
+@@ -591,20 +538,25 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
+
+ Py_Initialize();
+
+- // If this is not the first time we initialize Python, the interpreter
+- // lock already exists and we need to lock it as PyEval_InitThreads
+- // would not do that in that case.
+- if (PyEval_ThreadsInitialized() && !PyGILState_Check())
++#if PY_VERSION_HEX < 0x03070000
++ // Python >= 3.7 Py_Initialize implicitly calls PyEval_InitThreads
++ // Python < 3.7 we have to manually call initthreads.
++ // PyEval_InitThreads is a no-op on subsequent calls, No need to wrap in
++ // PyEval_ThreadsInitialized() check
++ PyEval_InitThreads();
++#endif
++
++ // Acquire GIL if thread doesn't currently hold.
++ if (!PyGILState_Check())
+ PyEval_RestoreThread((PyThreadState*)m_mainThreadState);
+- else
+- PyEval_InitThreads();
++
+ const wchar_t* python_argv[1] = {L""};
+ //! @bug libpython isn't const correct
+ PySys_SetArgv(1, const_cast<wchar_t**>(python_argv));
+
+ if (!(m_mainThreadState = PyThreadState_Get()))
+ CLog::Log(LOGERROR, "Python threadstate is NULL.");
+- savestate = PyEval_SaveThread();
++ PyEval_SaveThread();
+
+ m_bInitialized = true;
+ }
+@@ -674,7 +626,6 @@ void XBPython::OnScriptFinalized(ILanguageInvoker *invoker)
+ m_iDllScriptCounter--;
+ else
+ CLog::Log(LOGERROR, "Python script counter attempted to become
negative");
+- m_endtime = XbmcThreads::SystemClockMillis();
+ }
+
+ ILanguageInvoker* XBPython::CreateInvoker()
+diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h
+index ff4d93990fd0..2126d9011502 100644
+--- a/xbmc/interfaces/python/XBPython.h
++++ b/xbmc/interfaces/python/XBPython.h
+@@ -105,13 +105,11 @@ class XBPython :
+ void* m_mainThreadState;
+ bool m_bInitialized;
+ int m_iDllScriptCounter; // to keep track of the total scripts running
that need the dll
+- unsigned int m_endtime;
+
+ //Vector with list of threads used for running scripts
+ PyList m_vecPyList;
+ PlayerCallbackList m_vecPlayerCallbackList;
+ MonitorCallbackList m_vecMonitorCallbackList;
+- LibraryLoader* m_pDll;
+
+ // any global events that scripts should be using
+ CEvent m_globalEvent;
diff --git a/kodi-19-python3-1.patch b/kodi-19-python3-1.patch
new file mode 100644
index 0000000..1ea0fe2
--- /dev/null
+++ b/kodi-19-python3-1.patch
@@ -0,0 +1,345 @@
+From ba92831983d2c52ba3f5ffc51f18286d52b5c964 Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard(a)kodi.tv>
+Date: Mon, 31 Aug 2020 20:35:46 +1000
+Subject: [PATCH] [clang-format] interface/python/PythonInvoker.*
+
+---
+ xbmc/interfaces/python/PythonInvoker.cpp | 101 +++++++++++++++--------
+ xbmc/interfaces/python/PythonInvoker.h | 17 ++--
+ 2 files changed, 75 insertions(+), 43 deletions(-)
+
+diff --git a/xbmc/interfaces/python/PythonInvoker.cpp
b/xbmc/interfaces/python/PythonInvoker.cpp
+index e00cc1ff8632..6254e892a4dd 100644
+--- a/xbmc/interfaces/python/PythonInvoker.cpp
++++ b/xbmc/interfaces/python/PythonInvoker.cpp
+@@ -6,9 +6,10 @@
+ * See LICENSES/README.md for more information.
+ */
+
++// clang-format off
+ // python.h should always be included first before any other includes
+ #include <Python.h>
+-#include <iterator>
++// clang-format on
+
+ #include "Application.h"
+ #include "PythonInvoker.h"
+@@ -40,9 +41,10 @@
+ // clang-format on
+
+ #include <cassert>
++#include <iterator>
+
+ #ifdef TARGET_WINDOWS
+-extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
++extern "C" FILE* fopen_utf8(const char* _Filename, const char* _Mode);
+ #else
+ #define fopen_utf8 fopen
+ #endif
+@@ -63,7 +65,8 @@ using namespace KODI::MESSAGING;
+
+ CCriticalSection CPythonInvoker::s_critical;
+
+-static const std::string
getListOfAddonClassesAsString(XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook>&
languageHook)
++static const std::string getListOfAddonClassesAsString(
++ XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook>&
languageHook)
+ {
+ std::string message;
+ CSingleLock l(*(languageHook.get()));
+@@ -81,11 +84,13 @@ static const std::string
getListOfAddonClassesAsString(XBMCAddon::AddonClass::Re
+ return message;
+ }
+
+-static std::vector<std::vector<wchar_t>>
storeArgumentsCCompatible(std::vector<std::wstring> const& input)
++static std::vector<std::vector<wchar_t>> storeArgumentsCCompatible(
++ std::vector<std::wstring> const& input)
+ {
+ std::vector<std::vector<wchar_t>> output;
+- std::transform(input.begin(), input.end(), std::back_inserter(output),
+- [](std::wstring const& i) { return
std::vector<wchar_t>(i.c_str(), i.c_str() + i.length() + 1); });
++ std::transform(input.begin(), input.end(), std::back_inserter(output), [](std::wstring
const& i) {
++ return std::vector<wchar_t>(i.c_str(), i.c_str() + i.length() + 1);
++ });
+
+ if (output.empty())
+ output.emplace_back(1u, '\0');
+@@ -101,10 +106,10 @@ static std::vector<wchar_t*>
getCPointersToArguments(std::vector<std::vector<wch
+ return output;
+ }
+
+-CPythonInvoker::CPythonInvoker(ILanguageInvocationHandler *invocationHandler)
+- : ILanguageInvoker(invocationHandler),
+- m_threadState(NULL), m_stop(false)
+-{ }
++CPythonInvoker::CPythonInvoker(ILanguageInvocationHandler* invocationHandler)
++ : ILanguageInvoker(invocationHandler), m_threadState(NULL), m_stop(false)
++{
++}
+
+ CPythonInvoker::~CPythonInvoker()
+ {
+@@ -114,22 +119,25 @@ CPythonInvoker::~CPythonInvoker()
+ return;
+
+ if (GetState() < InvokerStateExecutionDone)
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread
\"%s\" to stop",
+- GetId(), (!m_sourceFile.empty() ? m_sourceFile.c_str() : "unknown
script"));
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread
\"%s\" to stop", GetId(),
++ (!m_sourceFile.empty() ? m_sourceFile.c_str() : "unknown
script"));
+ Stop(true);
+ pulseGlobalEvent();
+
+ onExecutionFinalized();
+ }
+
+-bool CPythonInvoker::Execute(const std::string &script, const
std::vector<std::string> &arguments /* = std::vector<std::string>() */)
++bool CPythonInvoker::Execute(
++ const std::string& script,
++ const std::vector<std::string>& arguments /* =
std::vector<std::string>() */)
+ {
+ if (script.empty())
+ return false;
+
+ if (!CFile::Exists(script))
+ {
+- CLog::Log(LOGERROR, "CPythonInvoker(%d): python script \"%s\" does
not exist", GetId(), CSpecialProtocol::TranslatePath(script).c_str());
++ CLog::Log(LOGERROR, "CPythonInvoker(%d): python script \"%s\" does
not exist", GetId(),
++ CSpecialProtocol::TranslatePath(script).c_str());
+ return false;
+ }
+
+@@ -139,7 +147,7 @@ bool CPythonInvoker::Execute(const std::string &script, const
std::vector<std::s
+ return ILanguageInvoker::Execute(script, arguments);
+ }
+
+-bool CPythonInvoker::execute(const std::string &script, const
std::vector<std::string> &arguments)
++bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::string>& arguments)
+ {
+ std::vector<std::wstring> w_arguments;
+ for (auto argument : arguments)
+@@ -284,7 +292,8 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ // set current directory and python's path.
+ PySys_SetArgv(argc, &argv[0]);
+
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s",
GetId(), m_sourceFile.c_str(), scriptDir.c_str());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s",
GetId(),
++ m_sourceFile.c_str(), scriptDir.c_str());
+ PyObject* module = PyImport_AddModule("__main__");
+ PyObject* moduleDict = PyModule_GetDict(module);
+
+@@ -309,7 +318,9 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ #ifdef TARGET_WINDOWS
+ if (!g_charsetConverter.utf8ToSystem(nativeFilename, true))
+ {
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): can't convert filename
\"%s\" to system encoding", GetId(), m_sourceFile.c_str(),
realFilename.c_str());
++ CLog::Log(LOGERROR,
++ "CPythonInvoker(%d, %s): can't convert filename
\"%s\" to system encoding",
++ GetId(), m_sourceFile.c_str(), realFilename.c_str());
+ return false;
+ }
+ #endif
+@@ -324,11 +335,13 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+
+ Py_DECREF(f);
+ setState(InvokerStateRunning);
+- XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this
callstack as being in a python context
++ XBMCAddon::Python::PyContext
++ pycontext; // this is a guard class that marks this callstack as being in a
python context
+ executeScript(fp, realFilename, moduleDict);
+ }
+ else
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(),
m_sourceFile.c_str(), m_sourceFile.c_str());
++ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(),
m_sourceFile.c_str(),
++ m_sourceFile.c_str());
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+@@ -339,7 +352,8 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ catch (...)
+ {
+ setState(InvokerStateFailed);
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failure in script",
GetId(), m_sourceFile.c_str());
++ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failure in script",
GetId(),
++ m_sourceFile.c_str());
+ failed = true;
+ }
+ }
+@@ -348,7 +362,8 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ InvokerState stateToSet;
+ if (!failed && !PyErr_Occurred())
+ {
+- CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run",
GetId(), m_sourceFile.c_str());
++ CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run",
GetId(),
++ m_sourceFile.c_str());
+ stateToSet = InvokerStateScriptDone;
+ onSuccess();
+ }
+@@ -366,9 +381,11 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ // if it failed with an exception we already logged the details
+ if (!failed)
+ {
+- PythonBindings::PythonToCppException *e = NULL;
+- if (PythonBindings::PythonToCppException::ParsePythonException(exceptionType,
exceptionValue, exceptionTraceback))
+- e = new PythonBindings::PythonToCppException(exceptionType, exceptionValue,
exceptionTraceback);
++ PythonBindings::PythonToCppException* e = NULL;
++ if (PythonBindings::PythonToCppException::ParsePythonException(exceptionType,
exceptionValue,
++
exceptionTraceback))
++ e = new PythonBindings::PythonToCppException(exceptionType, exceptionValue,
++ exceptionTraceback);
+ else
+ e = new PythonBindings::PythonToCppException();
+
+@@ -491,7 +508,9 @@ bool CPythonInvoker::stop(bool abort)
+ {
+ if (timeout.IsTimePast())
+ {
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): script didn't stop in %d
seconds - let's kill it", GetId(), m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT /
1000);
++ CLog::Log(LOGERROR,
++ "CPythonInvoker(%d, %s): script didn't stop in %d seconds -
let's kill it",
++ GetId(), m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT / 1000);
+ break;
+ }
+
+@@ -510,7 +529,8 @@ bool CPythonInvoker::stop(bool abort)
+
+ // Useful for add-on performance metrics
+ if (!timeout.IsTimePast())
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took
%dms", GetId(), m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took
%dms", GetId(),
++ m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft());
+
+ // Since we released the m_critical it's possible that the state is cleaned up
+ // so we need to recheck for m_threadState == NULL
+@@ -606,7 +626,8 @@ void CPythonInvoker::onExecutionFailed()
+ PyEval_SaveThread();
+
+ setState(InvokerStateFailed);
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): abnormally terminating python
thread", GetId(), m_sourceFile.c_str());
++ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): abnormally terminating python
thread", GetId(),
++ m_sourceFile.c_str());
+
+ CSingleLock lock(m_critical);
+ m_threadState = NULL;
+@@ -624,11 +645,12 @@ void CPythonInvoker::onInitialization()
+
+ // get a possible initialization script
+ const char* runscript = getInitializationScript();
+- if (runscript!= NULL && strlen(runscript) > 0)
++ if (runscript != NULL && strlen(runscript) > 0)
+ {
+ // redirecting default output to debug console
+ if (PyRun_SimpleString(runscript) == -1)
+- CLog::Log(LOGFATAL, "CPythonInvoker(%d, %s): initialize error", GetId(),
m_sourceFile.c_str());
++ CLog::Log(LOGFATAL, "CPythonInvoker(%d, %s): initialize error",
GetId(),
++ m_sourceFile.c_str());
+ }
+ }
+
+@@ -637,7 +659,7 @@ void CPythonInvoker::onPythonModuleInitialization(void* moduleDict)
+ if (m_addon.get() == NULL || moduleDict == NULL)
+ return;
+
+- PyObject *moduleDictionary = (PyObject *)moduleDict;
++ PyObject* moduleDictionary = (PyObject*)moduleDict;
+
+ PyObject* pyaddonid = PyUnicode_FromString(m_addon->ID().c_str());
+ PyDict_SetItemString(moduleDictionary, "__xbmcaddonid__", pyaddonid);
+@@ -646,10 +668,12 @@ void CPythonInvoker::onPythonModuleInitialization(void*
moduleDict)
+ PyObject* pyxbmcapiversion = PyUnicode_FromString(version.asString().c_str());
+ PyDict_SetItemString(moduleDictionary, "__xbmcapiversion__",
pyxbmcapiversion);
+
+- PyObject *pyinvokerid = PyLong_FromLong(GetId());
++ PyObject* pyinvokerid = PyLong_FromLong(GetId());
+ PyDict_SetItemString(moduleDictionary, "__xbmcinvokerid__", pyinvokerid);
+
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): instantiating addon using
automatically obtained id of \"%s\" dependent on version %s of the xbmc.python
api",
++ CLog::Log(LOGDEBUG,
++ "CPythonInvoker(%d, %s): instantiating addon using automatically
obtained id of \"%s\" "
++ "dependent on version %s of the xbmc.python api",
+ GetId(), m_sourceFile.c_str(), m_addon->ID().c_str(),
version.asString().c_str());
+ }
+
+@@ -658,12 +682,16 @@ void CPythonInvoker::onDeinitialization()
+ XBMC_TRACE;
+ }
+
+-void CPythonInvoker::onError(const std::string &exceptionType /* = "" */,
const std::string &exceptionValue /* = "" */, const std::string
&exceptionTraceback /* = "" */)
++void CPythonInvoker::onError(const std::string& exceptionType /* = "" */,
++ const std::string& exceptionValue /* = ""
*/,
++ const std::string& exceptionTraceback /* = ""
*/)
+ {
+ CPyThreadState releaseGil;
+ CSingleLock gc(CServiceBroker::GetWinSystem()->GetGfxContext());
+
+- CGUIDialogKaiToast *pDlgToast =
CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogKaiToast>(WINDOW_DIALOG_KAI_TOAST);
++ CGUIDialogKaiToast* pDlgToast =
++
CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogKaiToast>(
++ WINDOW_DIALOG_KAI_TOAST);
+ if (pDlgToast != NULL)
+ {
+ std::string message;
+@@ -698,12 +698,13 @@
+ if (m_addon && !m_addon->Name().empty())
+ message = StringUtils::Format(g_localizeStrings.Get(2102).c_str(),
m_addon->Name().c_str());
+ else
+- message = g_localizeStrings.Get(2103);
++ message = g_localizeStrings.Get(2103);
+ pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, message,
g_localizeStrings.Get(2104));
+ }
+ }
+
+-void CPythonInvoker::initializeModules(const std::map<std::string,
PythonModuleInitialization> &modules)
++void CPythonInvoker::initializeModules(
++ const std::map<std::string, PythonModuleInitialization> &modules)
+ {
+ for (const auto& module : modules)
+ {
+diff --git a/xbmc/interfaces/python/PythonInvoker.h
b/xbmc/interfaces/python/PythonInvoker.h
+index 0ea3bfa15433..87e987d0f75d 100644
+--- a/xbmc/interfaces/python/PythonInvoker.h
++++ b/xbmc/interfaces/python/PythonInvoker.h
+@@ -23,10 +23,11 @@ typedef struct _object PyObject;
+ class CPythonInvoker : public ILanguageInvoker
+ {
+ public:
+- explicit CPythonInvoker(ILanguageInvocationHandler *invocationHandler);
++ explicit CPythonInvoker(ILanguageInvocationHandler* invocationHandler);
+ ~CPythonInvoker() override;
+
+- bool Execute(const std::string &script, const std::vector<std::string>
&arguments = std::vector<std::string>()) override;
++ bool Execute(const std::string& script,
++ const std::vector<std::string>& arguments =
std::vector<std::string>()) override;
+
+ bool IsStopping() const override { return m_stop || ILanguageInvoker::IsStopping(); }
+
+@@ -34,7 +35,7 @@ class CPythonInvoker : public ILanguageInvoker
+
+ protected:
+ // implementation of ILanguageInvoker
+- bool execute(const std::string &script, const std::vector<std::string>
&arguments) override;
++ bool execute(const std::string& script, const std::vector<std::string>&
arguments) override;
+ virtual void executeScript(FILE* fp, const std::string& script, PyObject*
moduleDict);
+ bool stop(bool abort) override;
+ void onExecutionDone() override;
+@@ -48,15 +49,17 @@ class CPythonInvoker : public ILanguageInvoker
+ virtual void onPythonModuleInitialization(void* moduleDict);
+ virtual void onDeinitialization();
+
+- virtual void onSuccess() { }
+- virtual void onAbort() { }
+- virtual void onError(const std::string &exceptionType = "", const
std::string &exceptionValue = "", const std::string &exceptionTraceback
= "");
++ virtual void onSuccess() {}
++ virtual void onAbort() {}
++ virtual void onError(const std::string& exceptionType = "",
++ const std::string& exceptionValue = "",
++ const std::string& exceptionTraceback = "");
+
+ std::string m_sourceFile;
+ CCriticalSection m_critical;
+
+ private:
+- void initializeModules(const std::map<std::string, PythonModuleInitialization>
&modules);
++ void initializeModules(const std::map<std::string,
PythonModuleInitialization>& modules);
+ bool initializeModule(PythonModuleInitialization module);
+ void addPath(const std::string& path); // add path in UTF-8 encoding
+ void getAddonModuleDeps(const ADDON::AddonPtr& addon,
std::set<std::string>& paths);
diff --git a/kodi-19-python3-2.patch b/kodi-19-python3-2.patch
new file mode 100644
index 0000000..0db7cbf
--- /dev/null
+++ b/kodi-19-python3-2.patch
@@ -0,0 +1,632 @@
+From 8cd580089b5da1c4e00fabb1414344d08503d30e Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard(a)kodi.tv>
+Date: Mon, 31 Aug 2020 20:37:34 +1000
+Subject: [PATCH] [clang-format] interfaces/python/XBPython.*
+
+---
+ xbmc/interfaces/python/XBPython.cpp | 193 +++++++++++++++-------------
+ xbmc/interfaces/python/XBPython.h | 80 ++++++------
+ 2 files changed, 148 insertions(+), 125 deletions(-)
+
+diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp
+index 666ac17d3a47..d5e8ff3fac2c 100644
+--- a/xbmc/interfaces/python/XBPython.cpp
++++ b/xbmc/interfaces/python/XBPython.cpp
+@@ -6,38 +6,40 @@
+ * See LICENSES/README.md for more information.
+ */
+
++// clang-format off
+ // python.h should always be included first before any other includes
+ #include <Python.h>
++// clang-format on
+
+-#include <algorithm>
++#include "XBPython.h"
+
++#include "ServiceBroker.h"
++#include "Util.h"
+ #include "cores/DllLoader/DllLoaderContainer.h"
+-#include "XBPython.h"
+ #include "filesystem/File.h"
+ #include "filesystem/SpecialProtocol.h"
++#include "interfaces/AnnouncementManager.h"
++#include "interfaces/legacy/AddonUtils.h"
++#include "interfaces/legacy/Monitor.h"
++#include "interfaces/python/AddonPythonInvoker.h"
++#include "interfaces/python/PythonInvoker.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/SettingsComponent.h"
+ #include "utils/JSONVariantWriter.h"
+-#include "utils/log.h"
+ #include "utils/Variant.h"
+-#include "Util.h"
++#include "utils/log.h"
++
+ #ifdef TARGET_WINDOWS
+ #include "platform/Environment.h"
+ #include "utils/SystemInfo.h"
+ #endif
+-#include "settings/AdvancedSettings.h"
+-#include "settings/SettingsComponent.h"
+
+-#include "interfaces/AnnouncementManager.h"
+-
+-#include "interfaces/legacy/Monitor.h"
+-#include "interfaces/legacy/AddonUtils.h"
+-#include "interfaces/python/AddonPythonInvoker.h"
+-#include "interfaces/python/PythonInvoker.h"
+-#include "ServiceBroker.h"
++#include <algorithm>
+
+ XBPython::XBPython()
+ {
+- m_bInitialized = false;
+- m_mainThreadState = NULL;
++ m_bInitialized = false;
++ m_mainThreadState = NULL;
+ m_iDllScriptCounter = 0;
+ m_vecPlayerCallbackList.clear();
+ m_vecMonitorCallbackList.clear();
+@@ -52,61 +54,70 @@ XBPython::~XBPython()
+ }
+
+ #define LOCK_AND_COPY(type, dest, src) \
+- if (!m_bInitialized) return; \
++ if (!m_bInitialized) \
++ return; \
+ CSingleLock lock(src); \
+ src.hadSomethingRemoved = false; \
+ type dest; \
+ dest = src
+
+-#define CHECK_FOR_ENTRY(l,v) \
+- (l.hadSomethingRemoved ? (std::find(l.begin(),l.end(),v) != l.end()) : true)
++#define CHECK_FOR_ENTRY(l, v) \
++ (l.hadSomethingRemoved ? (std::find(l.begin(), l.end(), v) != l.end()) : true)
+
+-void XBPython::Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const
char *message, const CVariant &data)
++void XBPython::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
++ const char* sender,
++ const char* message,
++ const CVariant& data)
+ {
+ if (flag & ANNOUNCEMENT::VideoLibrary)
+ {
+- if (strcmp(message, "OnScanFinished") == 0)
+- OnScanFinished("video");
+- else if (strcmp(message, "OnScanStarted") == 0)
+- OnScanStarted("video");
+- else if (strcmp(message, "OnCleanStarted") == 0)
+- OnCleanStarted("video");
+- else if (strcmp(message, "OnCleanFinished") == 0)
+- OnCleanFinished("video");
++ if (strcmp(message, "OnScanFinished") == 0)
++ OnScanFinished("video");
++ else if (strcmp(message, "OnScanStarted") == 0)
++ OnScanStarted("video");
++ else if (strcmp(message, "OnCleanStarted") == 0)
++ OnCleanStarted("video");
++ else if (strcmp(message, "OnCleanFinished") == 0)
++ OnCleanFinished("video");
+ }
+ else if (flag & ANNOUNCEMENT::AudioLibrary)
+ {
+- if (strcmp(message, "OnScanFinished") == 0)
+- OnScanFinished("music");
+- else if (strcmp(message, "OnScanStarted") == 0)
+- OnScanStarted("music");
+- else if (strcmp(message, "OnCleanStarted") == 0)
+- OnCleanStarted("music");
+- else if (strcmp(message, "OnCleanFinished") == 0)
+- OnCleanFinished("music");
++ if (strcmp(message, "OnScanFinished") == 0)
++ OnScanFinished("music");
++ else if (strcmp(message, "OnScanStarted") == 0)
++ OnScanStarted("music");
++ else if (strcmp(message, "OnCleanStarted") == 0)
++ OnCleanStarted("music");
++ else if (strcmp(message, "OnCleanFinished") == 0)
++ OnCleanFinished("music");
+ }
+ else if (flag & ANNOUNCEMENT::GUI)
+ {
+- if (strcmp(message, "OnScreensaverDeactivated") == 0)
+- OnScreensaverDeactivated();
+- else if (strcmp(message, "OnScreensaverActivated") == 0)
+- OnScreensaverActivated();
+- else if (strcmp(message, "OnDPMSDeactivated") == 0)
+- OnDPMSDeactivated();
+- else if (strcmp(message, "OnDPMSActivated") == 0)
+- OnDPMSActivated();
++ if (strcmp(message, "OnScreensaverDeactivated") == 0)
++ OnScreensaverDeactivated();
++ else if (strcmp(message, "OnScreensaverActivated") == 0)
++ OnScreensaverActivated();
++ else if (strcmp(message, "OnDPMSDeactivated") == 0)
++ OnDPMSDeactivated();
++ else if (strcmp(message, "OnDPMSActivated") == 0)
++ OnDPMSActivated();
+ }
+
+ std::string jsonData;
+- if (CJSONVariantWriter::Write(data, jsonData,
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonOutputCompact))
+- OnNotification(sender, std::string(ANNOUNCEMENT::AnnouncementFlagToString(flag)) +
"." + std::string(message), jsonData);
++ if (CJSONVariantWriter::Write(
++ data, jsonData,
++
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonOutputCompact))
++ OnNotification(sender,
++ std::string(ANNOUNCEMENT::AnnouncementFlagToString(flag)) +
"." +
++ std::string(message),
++ jsonData);
+ }
+
+ // message all registered callbacks that we started playing
+-void XBPython::OnPlayBackStarted(const CFileItem &file)
++void XBPython::OnPlayBackStarted(const CFileItem& file)
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -115,7 +126,7 @@ void XBPython::OnPlayBackStarted(const CFileItem &file)
+ }
+
+ // message all registered callbacks that we changed stream
+-void XBPython::OnAVStarted(const CFileItem &file)
++void XBPython::OnAVStarted(const CFileItem& file)
+ {
+ XBMC_TRACE;
+ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+@@ -142,7 +153,7 @@ void XBPython::OnAVChange()
+ void XBPython::OnPlayBackPaused()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -154,7 +165,7 @@ void XBPython::OnPlayBackPaused()
+ void XBPython::OnPlayBackResumed()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -166,7 +177,7 @@ void XBPython::OnPlayBackResumed()
+ void XBPython::OnPlayBackEnded()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -178,7 +189,7 @@ void XBPython::OnPlayBackEnded()
+ void XBPython::OnPlayBackStopped()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -190,7 +201,7 @@ void XBPython::OnPlayBackStopped()
+ void XBPython::OnPlayBackError()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -202,7 +213,7 @@ void XBPython::OnPlayBackError()
+ void XBPython::OnPlayBackSpeedChanged(int iSpeed)
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -214,7 +225,7 @@ void XBPython::OnPlayBackSpeedChanged(int iSpeed)
+ void XBPython::OnPlayBackSeek(int64_t iTime, int64_t seekOffset)
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -226,7 +237,7 @@ void XBPython::OnPlayBackSeek(int64_t iTime, int64_t seekOffset)
+ void XBPython::OnPlayBackSeekChapter(int iChapter)
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -238,7 +249,7 @@ void XBPython::OnPlayBackSeekChapter(int iChapter)
+ void XBPython::OnQueueNextItem()
+ {
+ XBMC_TRACE;
+- LOCK_AND_COPY(std::vector<void*>,tmp,m_vecPlayerCallbackList);
++ LOCK_AND_COPY(std::vector<void*>, tmp, m_vecPlayerCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecPlayerCallbackList, it))
+@@ -294,10 +305,10 @@ void
XBPython::UnregisterPythonMonitorCallBack(XBMCAddon::xbmc::Monitor* pCallba
+ }
+ }
+
+-void XBPython::OnSettingsChanged(const std::string &ID)
++void XBPython::OnSettingsChanged(const std::string& ID)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it) && (it->GetId() ==
ID))
+@@ -308,7 +319,7 @@ void XBPython::OnSettingsChanged(const std::string &ID)
+ void XBPython::OnScreensaverActivated()
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -319,7 +330,7 @@ void XBPython::OnScreensaverActivated()
+ void XBPython::OnScreensaverDeactivated()
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -330,7 +341,7 @@ void XBPython::OnScreensaverDeactivated()
+ void XBPython::OnDPMSActivated()
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -341,7 +352,7 @@ void XBPython::OnDPMSActivated()
+ void XBPython::OnDPMSDeactivated()
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -349,10 +360,10 @@ void XBPython::OnDPMSDeactivated()
+ }
+ }
+
+-void XBPython::OnScanStarted(const std::string &library)
++void XBPython::OnScanStarted(const std::string& library)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -360,10 +371,10 @@ void XBPython::OnScanStarted(const std::string &library)
+ }
+ }
+
+-void XBPython::OnScanFinished(const std::string &library)
++void XBPython::OnScanFinished(const std::string& library)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -371,10 +382,10 @@ void XBPython::OnScanFinished(const std::string &library)
+ }
+ }
+
+-void XBPython::OnCleanStarted(const std::string &library)
++void XBPython::OnCleanStarted(const std::string& library)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -382,10 +393,10 @@ void XBPython::OnCleanStarted(const std::string &library)
+ }
+ }
+
+-void XBPython::OnCleanFinished(const std::string &library)
++void XBPython::OnCleanFinished(const std::string& library)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -393,10 +404,12 @@ void XBPython::OnCleanFinished(const std::string &library)
+ }
+ }
+
+-void XBPython::OnNotification(const std::string &sender, const std::string
&method, const std::string &data)
++void XBPython::OnNotification(const std::string& sender,
++ const std::string& method,
++ const std::string& data)
+ {
+ XBMC_TRACE;
+-
LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>,tmp,m_vecMonitorCallbackList);
++ LOCK_AND_COPY(std::vector<XBMCAddon::xbmc::Monitor*>, tmp,
m_vecMonitorCallbackList);
+ for (auto& it : tmp)
+ {
+ if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, it))
+@@ -404,7 +417,7 @@ void XBPython::OnNotification(const std::string &sender, const
std::string &meth
+ }
+ }
+
+-void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
++void XBPython::RegisterExtensionLib(LibraryLoader* pLib)
+ {
+ if (!pLib)
+ return;
+@@ -415,13 +428,13 @@ void XBPython::RegisterExtensionLib(LibraryLoader *pLib)
+ m_extensions.push_back(pLib);
+ }
+
+-void XBPython::UnregisterExtensionLib(LibraryLoader *pLib)
++void XBPython::UnregisterExtensionLib(LibraryLoader* pLib)
+ {
+ if (!pLib)
+ return;
+
+ CSingleLock lock(m_critSection);
+- CLog::Log(LOGDEBUG, "%s, removing %s (0x%p)", __FUNCTION__,
pLib->GetName(), (void *)pLib);
++ CLog::Log(LOGDEBUG, "%s, removing %s (0x%p)", __FUNCTION__,
pLib->GetName(), (void*)pLib);
+ PythonExtensionLibraries::iterator iter = m_extensions.begin();
+ while (iter != m_extensions.end())
+ {
+@@ -441,8 +454,8 @@ void XBPython::UnloadExtensionLibs()
+ PythonExtensionLibraries::iterator iter = m_extensions.begin();
+ while (iter != m_extensions.end())
+ {
+- DllLoaderContainer::ReleaseModule(*iter);
+- ++iter;
++ DllLoaderContainer::ReleaseModule(*iter);
++ ++iter;
+ }
+ m_extensions.clear();
+ }
+@@ -454,7 +467,7 @@ void XBPython::Uninitialize()
+ // would lead to a crash
+ CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
+
+- LOCK_AND_COPY(std::vector<PyElem>,tmpvec,m_vecPyList);
++ LOCK_AND_COPY(std::vector<PyElem>, tmpvec, m_vecPyList);
+ m_vecPyList.clear();
+ m_vecPyList.hadSomethingRemoved = true;
+
+@@ -488,7 +501,7 @@ void XBPython::Process()
+ }
+ }
+
+-bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
++bool XBPython::OnScriptInitialized(ILanguageInvoker* invoker)
+ {
+ if (invoker == NULL)
+ return false;
+@@ -515,8 +528,10 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
+ // so point it to frameworks which is where python3.8 is located
+ setenv("PYTHONHOME",
CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1);
+ setenv("PYTHONPATH",
CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1);
+- CLog::Log(LOGDEBUG, "PYTHONHOME -> %s",
CSpecialProtocol::TranslatePath("special://frameworks").c_str());
+- CLog::Log(LOGDEBUG, "PYTHONPATH -> %s",
CSpecialProtocol::TranslatePath("special://frameworks").c_str());
++ CLog::Log(LOGDEBUG, "PYTHONHOME -> %s",
++
CSpecialProtocol::TranslatePath("special://frameworks").c_str());
++ CLog::Log(LOGDEBUG, "PYTHONPATH -> %s",
++
CSpecialProtocol::TranslatePath("special://frameworks").c_str());
+ }
+ #elif defined(TARGET_WINDOWS)
+ // because the third party build of python is compiled with vs2008 we need
+@@ -540,7 +555,7 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
+
+ #if PY_VERSION_HEX < 0x03070000
+ // Python >= 3.7 Py_Initialize implicitly calls PyEval_InitThreads
+- // Python < 3.7 we have to manually call initthreads.
++ // Python < 3.7 we have to manually call initthreads.
+ // PyEval_InitThreads is a no-op on subsequent calls, No need to wrap in
+ // PyEval_ThreadsInitialized() check
+ PyEval_InitThreads();
+@@ -564,7 +579,7 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker)
+ return m_bInitialized;
+ }
+
+-void XBPython::OnScriptStarted(ILanguageInvoker *invoker)
++void XBPython::OnScriptStarted(ILanguageInvoker* invoker)
+ {
+ if (invoker == NULL)
+ return;
+@@ -573,14 +588,14 @@ void XBPython::OnScriptStarted(ILanguageInvoker *invoker)
+ return;
+
+ PyElem inf;
+- inf.id = invoker->GetId();
+- inf.bDone = false;
+- inf.pyThread = static_cast<CPythonInvoker*>(invoker);
++ inf.id = invoker->GetId();
++ inf.bDone = false;
++ inf.pyThread = static_cast<CPythonInvoker*>(invoker);
+ CSingleLock lock(m_vecPyList);
+ m_vecPyList.push_back(inf);
+ }
+
+-void XBPython::NotifyScriptAborting(ILanguageInvoker *invoker)
++void XBPython::NotifyScriptAborting(ILanguageInvoker* invoker)
+ {
+ XBMC_TRACE;
+
+@@ -617,7 +632,7 @@ void XBPython::OnExecutionEnded(ILanguageInvoker* invoker)
+ }
+ }
+
+-void XBPython::OnScriptFinalized(ILanguageInvoker *invoker)
++void XBPython::OnScriptFinalized(ILanguageInvoker* invoker)
+ {
+ XBMC_TRACE;
+ CSingleLock lock(m_critSection);
+diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h
+index 2126d9011502..0037f67d3015 100644
+--- a/xbmc/interfaces/python/XBPython.h
++++ b/xbmc/interfaces/python/XBPython.h
+@@ -21,41 +21,44 @@
+ class CPythonInvoker;
+ class CVariant;
+
+-typedef struct {
++typedef struct
++{
+ int id;
+ bool bDone;
+ CPythonInvoker* pyThread;
+-}PyElem;
++} PyElem;
+
+ class LibraryLoader;
+
+ namespace XBMCAddon
+ {
+- namespace xbmc
+- {
+- class Monitor;
+- }
++namespace xbmc
++{
++class Monitor;
+ }
++} // namespace XBMCAddon
+
+-template <class T> struct LockableType : public T, public CCriticalSection
+-{ bool hadSomethingRemoved; };
++template<class T>
++struct LockableType : public T, public CCriticalSection
++{
++ bool hadSomethingRemoved;
++};
+
+-typedef LockableType<std::vector<void*> > PlayerCallbackList;
+-typedef LockableType<std::vector<XBMCAddon::xbmc::Monitor*> >
MonitorCallbackList;
+-typedef LockableType<std::vector<PyElem> > PyList;
++typedef LockableType<std::vector<void*>> PlayerCallbackList;
++typedef LockableType<std::vector<XBMCAddon::xbmc::Monitor*>>
MonitorCallbackList;
++typedef LockableType<std::vector<PyElem>> PyList;
+ typedef std::vector<LibraryLoader*> PythonExtensionLibraries;
+
+-class XBPython :
+- public IPlayerCallback,
+- public ANNOUNCEMENT::IAnnouncer,
+- public ILanguageInvocationHandler
++class XBPython : public IPlayerCallback,
++ public ANNOUNCEMENT::IAnnouncer,
++ public ILanguageInvocationHandler
+ {
+ public:
+ XBPython();
+ ~XBPython() override;
+ void OnPlayBackEnded() override;
+- void OnPlayBackStarted(const CFileItem &file) override;
+- void OnAVStarted(const CFileItem &file) override;
++ void OnPlayBackStarted(const CFileItem& file) override;
++ void OnAVStarted(const CFileItem& file) override;
+ void OnAVChange() override;
+ void OnPlayBackPaused() override;
+ void OnPlayBackResumed() override;
+@@ -66,49 +69,54 @@ class XBPython :
+ void OnPlayBackSeekChapter(int iChapter) override;
+ void OnQueueNextItem() override;
+
+- void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char
*message, const CVariant &data) override;
++ void Announce(ANNOUNCEMENT::AnnouncementFlag flag,
++ const char* sender,
++ const char* message,
++ const CVariant& data) override;
+ void RegisterPythonPlayerCallBack(IPlayerCallback* pCallback);
+ void UnregisterPythonPlayerCallBack(IPlayerCallback* pCallback);
+ void RegisterPythonMonitorCallBack(XBMCAddon::xbmc::Monitor* pCallback);
+ void UnregisterPythonMonitorCallBack(XBMCAddon::xbmc::Monitor* pCallback);
+- void OnSettingsChanged(const std::string &strings);
++ void OnSettingsChanged(const std::string& strings);
+ void OnScreensaverActivated();
+ void OnScreensaverDeactivated();
+ void OnDPMSActivated();
+ void OnDPMSDeactivated();
+- void OnScanStarted(const std::string &library);
+- void OnScanFinished(const std::string &library);
+- void OnCleanStarted(const std::string &library);
+- void OnCleanFinished(const std::string &library);
+- void OnNotification(const std::string &sender, const std::string &method,
const std::string &data);
++ void OnScanStarted(const std::string& library);
++ void OnScanFinished(const std::string& library);
++ void OnCleanStarted(const std::string& library);
++ void OnCleanFinished(const std::string& library);
++ void OnNotification(const std::string& sender,
++ const std::string& method,
++ const std::string& data);
+
+ void Process() override;
+ void PulseGlobalEvent() override;
+ void Uninitialize() override;
+- bool OnScriptInitialized(ILanguageInvoker *invoker) override;
+- void OnScriptStarted(ILanguageInvoker *invoker) override;
+- void NotifyScriptAborting(ILanguageInvoker *invoker) override;
++ bool OnScriptInitialized(ILanguageInvoker* invoker) override;
++ void OnScriptStarted(ILanguageInvoker* invoker) override;
++ void NotifyScriptAborting(ILanguageInvoker* invoker) override;
+ void OnExecutionEnded(ILanguageInvoker* invoker) override;
+- void OnScriptFinalized(ILanguageInvoker *invoker) override;
++ void OnScriptFinalized(ILanguageInvoker* invoker) override;
+ ILanguageInvoker* CreateInvoker() override;
+
+ bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds);
+
+- void RegisterExtensionLib(LibraryLoader *pLib);
+- void UnregisterExtensionLib(LibraryLoader *pLib);
++ void RegisterExtensionLib(LibraryLoader* pLib);
++ void UnregisterExtensionLib(LibraryLoader* pLib);
+ void UnloadExtensionLibs();
+
+ private:
+ void Finalize();
+
+- CCriticalSection m_critSection;
+- void* m_mainThreadState;
+- bool m_bInitialized;
+- int m_iDllScriptCounter; // to keep track of the total scripts running
that need the dll
++ CCriticalSection m_critSection;
++ void* m_mainThreadState;
++ bool m_bInitialized;
++ int m_iDllScriptCounter; // to keep track of the total scripts running that need the
dll
+
+ //Vector with list of threads used for running scripts
+- PyList m_vecPyList;
+- PlayerCallbackList m_vecPlayerCallbackList;
++ PyList m_vecPyList;
++ PlayerCallbackList m_vecPlayerCallbackList;
+ MonitorCallbackList m_vecMonitorCallbackList;
+
+ // any global events that scripts should be using
diff --git a/kodi-19-python3-3.patch b/kodi-19-python3-3.patch
new file mode 100644
index 0000000..6a40194
--- /dev/null
+++ b/kodi-19-python3-3.patch
@@ -0,0 +1,82 @@
+From c8b8eea771f03b0a4093ede5122621ac189928b1 Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard(a)kodi.tv>
+Date: Sun, 20 Sep 2020 08:06:53 +1000
+Subject: [PATCH] remove further unused methods
+
+---
+ xbmc/interfaces/python/XBPython.cpp | 43 -----------------------------
+ xbmc/interfaces/python/XBPython.h | 6 ----
+ 2 files changed, 49 deletions(-)
+
+diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp
+index d5e8ff3fac2c..8ece5aacd4d2 100644
+--- a/xbmc/interfaces/python/XBPython.cpp
++++ b/xbmc/interfaces/python/XBPython.cpp
+@@ -417,49 +417,6 @@ void XBPython::OnNotification(const std::string& sender,
+ }
+ }
+
+-void XBPython::RegisterExtensionLib(LibraryLoader* pLib)
+-{
+- if (!pLib)
+- return;
+-
+- CSingleLock lock(m_critSection);
+-
+- CLog::Log(LOGDEBUG, "%s, adding %s (%p)", __FUNCTION__, pLib->GetName(),
(void*)pLib);
+- m_extensions.push_back(pLib);
+-}
+-
+-void XBPython::UnregisterExtensionLib(LibraryLoader* pLib)
+-{
+- if (!pLib)
+- return;
+-
+- CSingleLock lock(m_critSection);
+- CLog::Log(LOGDEBUG, "%s, removing %s (0x%p)", __FUNCTION__,
pLib->GetName(), (void*)pLib);
+- PythonExtensionLibraries::iterator iter = m_extensions.begin();
+- while (iter != m_extensions.end())
+- {
+- if (*iter == pLib)
+- {
+- m_extensions.erase(iter);
+- break;
+- }
+- ++iter;
+- }
+-}
+-
+-void XBPython::UnloadExtensionLibs()
+-{
+- CLog::Log(LOGDEBUG, "%s, clearing python extension libraries",
__FUNCTION__);
+- CSingleLock lock(m_critSection);
+- PythonExtensionLibraries::iterator iter = m_extensions.begin();
+- while (iter != m_extensions.end())
+- {
+- DllLoaderContainer::ReleaseModule(*iter);
+- ++iter;
+- }
+- m_extensions.clear();
+-}
+-
+ void XBPython::Uninitialize()
+ {
+ // don't handle any more announcements as most scripts are probably already
+diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h
+index 0037f67d3015..40077cd58715 100644
+--- a/xbmc/interfaces/python/XBPython.h
++++ b/xbmc/interfaces/python/XBPython.h
+@@ -102,13 +102,7 @@ class XBPython : public IPlayerCallback,
+
+ bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds);
+
+- void RegisterExtensionLib(LibraryLoader* pLib);
+- void UnregisterExtensionLib(LibraryLoader* pLib);
+- void UnloadExtensionLibs();
+-
+ private:
+- void Finalize();
+-
+ CCriticalSection m_critSection;
+ void* m_mainThreadState;
+ bool m_bInitialized;
diff --git a/kodi-19-python3-4.patch b/kodi-19-python3-4.patch
new file mode 100644
index 0000000..6d6a471
--- /dev/null
+++ b/kodi-19-python3-4.patch
@@ -0,0 +1,66 @@
+From 78b8815313e55ee303ce02ff0958ba14a9585370 Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard(a)kodi.tv>
+Date: Tue, 6 Oct 2020 18:26:02 +1000
+Subject: [PATCH] [PythonInvoker] Re-add support for Python 3.5-3.6
+
+---
+ xbmc/interfaces/python/PythonInvoker.cpp | 11 +++++++++++
+ xbmc/interfaces/python/XBPython.cpp | 5 ++++-
+ 2 files changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/interfaces/python/PythonInvoker.cpp
b/xbmc/interfaces/python/PythonInvoker.cpp
+index 6b2697d5135c..07a66794ad08 100644
+--- a/xbmc/interfaces/python/PythonInvoker.cpp
++++ b/xbmc/interfaces/python/PythonInvoker.cpp
+@@ -182,8 +182,15 @@ bool CPythonInvoker::execute(const std::string& script, const
std::vector<std::w
+ {
+ if (!m_threadState)
+ {
++#if PY_VERSION_HEX < 0x03070000
++ // this is a TOTAL hack. We need the GIL but we need to borrow a PyThreadState in
order to get it
++ // as of Python 3.2 since PyEval_AcquireLock is deprecated
++ extern PyThreadState* savestate;
++ PyEval_RestoreThread(savestate);
++#else
+ PyThreadState* ts = PyThreadState_New(PyInterpreterState_Main());
+ PyEval_RestoreThread(ts);
++#endif
+ l_threadState = Py_NewInterpreter();
+ PyEval_ReleaseThread(l_threadState);
+ if (l_threadState == NULL)
+@@ -605,8 +612,12 @@ void CPythonInvoker::onExecutionDone()
+ // unregister the language hook
+ m_languageHook->UnregisterMe();
+
++#if PY_VERSION_HEX < 0x03070000
++ PyEval_ReleaseLock();
++#else
+ PyThreadState_Swap(PyInterpreterState_ThreadHead(PyInterpreterState_Main()));
+ PyEval_SaveThread();
++#endif
+
+ // set stopped event - this allows ::stop to run and kill remaining threads
+ // this event has to be fired without holding m_critical
+diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp
+index 8ece5aacd4d2..3cef76ed24aa 100644
+--- a/xbmc/interfaces/python/XBPython.cpp
++++ b/xbmc/interfaces/python/XBPython.cpp
+@@ -36,6 +36,9 @@
+
+ #include <algorithm>
+
++// Only required for Py3 < 3.7
++PyThreadState* savestate;
++
+ XBPython::XBPython()
+ {
+ m_bInitialized = false;
+@@ -528,7 +531,7 @@ bool XBPython::OnScriptInitialized(ILanguageInvoker* invoker)
+
+ if (!(m_mainThreadState = PyThreadState_Get()))
+ CLog::Log(LOGERROR, "Python threadstate is NULL.");
+- PyEval_SaveThread();
++ savestate = PyEval_SaveThread();
+
+ m_bInitialized = true;
+ }
diff --git a/kodi.spec b/kodi.spec
index b12365e..bad179d 100644
--- a/kodi.spec
+++ b/kodi.spec
@@ -42,7 +42,7 @@
Name: kodi
Version: 19.0
-Release: 0.4.20201005git54be31b%{?dist}
+Release: 0.5.20201005git54be31b%{?dist}
Summary: Media center
License: GPLv2+ and GPLv3+ and LGPLv2+ and BSD and MIT
@@ -85,6 +85,13 @@ Patch3: kodi-18-annobin-workaround.patch
# Workaround for brp-mangle-shebangs behavior (RHBZ#1787088)
Patch4: kodi-18-brp-mangle-shebangs.patch
+# Python 3.9 support
+Patch5: kodi-19-python3-0.patch
+Patch6: kodi-19-python3-1.patch
+Patch7: kodi-19-python3-2.patch
+Patch8: kodi-19-python3-3.patch
+Patch9: kodi-19-python3-4.patch
+
%ifarch x86_64 i686
%global _with_crystalhd 1
%endif
@@ -359,6 +366,11 @@ This package contains the Kodi binary for X11 servers.
%patch2 -p1 -b.trousers
%patch3 -p1 -b.innobinfix
%patch4 -p1 -b.brp-mangle-shebangs
+%patch5 -p1 -b.python39-0
+%patch6 -p1 -b.python39-1
+%patch7 -p1 -b.python39-2
+%patch8 -p1 -b.python39-3
+%patch9 -p1 -b.python39-4
# Fix up Python shebangs
pathfix.py -pni "%{__python3} %{py3_shbang_opts}" \
@@ -522,6 +534,9 @@ rm -f ${RPM_BUILD_ROOT}%{_mandir}/man1/kodi-wiiremote.1
%changelog
+* Thu Oct 29 2020 Michael Cronenworth <mike(a)cchtml.com> -
19.0-0.5.20201005git54be31b
+- Python 3.9 support
+
* Tue Oct 27 2020 Michael Cronenworth <mike(a)cchtml.com> -
19.0-0.4.20201005git54be31b
- Kodi 19.0 alpha 2