void CPythonInvoker::onError() { CPyThreadState releaseGil; CSingleLock gc(g_graphicsContext); CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); if (pDlgToast != NULL) { CStdString desc; CStdString script; if (m_addon.get() != NULL) script = m_addon->Name(); else { CStdString path; URIUtils::Split(m_sourceFile.c_str(), path, script); if (script.Equals("default.py")) { CStdString path2; URIUtils::RemoveSlashAtEnd(path); URIUtils::Split(path, path2, script); } } desc = StringUtils::Format(g_localizeStrings.Get(2100).c_str(), script.c_str()); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc); } }
void CPythonInvoker::onError() { CPyThreadState releaseGil; CSingleLock gc(g_graphicsContext); CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); if (pDlgToast != NULL) { std::string message; if (m_addon && !m_addon->Name().empty()) message = StringUtils::Format(g_localizeStrings.Get(2102).c_str(), m_addon->Name().c_str()); else if (m_sourceFile == CSpecialProtocol::TranslatePath("special://profile/autoexec.py")) message = StringUtils::Format(g_localizeStrings.Get(2102).c_str(), "autoexec.py"); else message = g_localizeStrings.Get(2103); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, message, g_localizeStrings.Get(2104)); } }
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); if (pDlgToast != NULL) { std::string message; if (m_addon && !m_addon->Name().empty()) message = StringUtils::Format(g_localizeStrings.Get(2102).c_str(), m_addon->Name().c_str()); else if (m_sourceFile == CSpecialProtocol::TranslatePath("special://profile/autoexec.py")) message = StringUtils::Format(g_localizeStrings.Get(2102).c_str(), "autoexec.py"); else message = g_localizeStrings.Get(2103); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, message, g_localizeStrings.Get(2104)); } }
void XBPyThread::Process() { CLog::Log(LOGDEBUG,"Python thread: start processing"); int m_Py_file_input = Py_file_input; // get the global lock PyEval_AcquireLock(); PyThreadState* state = Py_NewInterpreter(); if (!state) { PyEval_ReleaseLock(); CLog::Log(LOGERROR,"Python thread: FAILED to get thread state!"); return; } // swap in my thread state PyThreadState_Swap(state); XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp)); languageHook->RegisterMe(); m_pExecuter->InitializeInterpreter(addon); CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source); // get path from script file name and add python path's // this is used for python so it will search modules from script path first CStdString scriptDir; URIUtils::GetDirectory(CSpecialProtocol::TranslatePath(m_source), scriptDir); URIUtils::RemoveSlashAtEnd(scriptDir); CStdString path = scriptDir; // add on any addon modules the user has installed ADDON::VECADDONS addons; ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons); for (unsigned int i = 0; i < addons.size(); ++i) #ifdef TARGET_WINDOWS { CStdString strTmp(CSpecialProtocol::TranslatePath(addons[i]->LibPath())); g_charsetConverter.utf8ToSystem(strTmp); path += PY_PATH_SEP + strTmp; } #else path += PY_PATH_SEP + CSpecialProtocol::TranslatePath(addons[i]->LibPath()); #endif // and add on whatever our default path is path += PY_PATH_SEP; // we want to use sys.path so it includes site-packages // if this fails, default to using Py_GetPath PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete if( pathObj && PyList_Check(pathObj) ) { for( int i = 0; i < PyList_Size(pathObj); i++ ) { PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete if( e && PyString_Check(e) ) { path += PyString_AsString(e); // returns internal data, don't delete or modify path += PY_PATH_SEP; } } } else { path += Py_GetPath(); } Py_DECREF(sysMod); // release ref to sysMod // set current directory and python's path. if (m_argv != NULL) PySys_SetArgv(m_argc, m_argv); CLog::Log(LOGDEBUG, "%s - Setting the Python path to %s", __FUNCTION__, path.c_str()); PySys_SetPath((char *)path.c_str()); CLog::Log(LOGDEBUG, "%s - Entering source directory %s", __FUNCTION__, scriptDir.c_str()); PyObject* module = PyImport_AddModule((char*)"__main__"); PyObject* moduleDict = PyModule_GetDict(module); // when we are done initing we store thread state so we can be aborted PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // we need to check if we was asked to abort before we had inited bool stopping = false; { CSingleLock lock(m_pExecuter->m_critSection); m_threadState = state; stopping = m_stopping; } PyEval_AcquireLock(); PyThreadState_Swap(state); if (!stopping) { try { if (m_type == 'F') { // run script from file // We need to have python open the file because on Windows the DLL that python // is linked against may not be the DLL that xbmc is linked against so // passing a FILE* to python from an fopen has the potential to crash. PyObject* file = PyFile_FromString((char *) CSpecialProtocol::TranslatePath(m_source).c_str(), (char*)"r"); FILE *fp = PyFile_AsFile(file); if (fp) { PyObject *f = PyString_FromString(CSpecialProtocol::TranslatePath(m_source).c_str()); PyDict_SetItemString(moduleDict, "__file__", f); if (addon.get() != NULL) { PyObject *pyaddonid = PyString_FromString(addon->ID().c_str()); PyDict_SetItemString(moduleDict, "__xbmcaddonid__", pyaddonid); CStdString version = ADDON::GetXbmcApiVersionDependency(addon); PyObject *pyxbmcapiversion = PyString_FromString(version.c_str()); PyDict_SetItemString(moduleDict, "__xbmcapiversion__", pyxbmcapiversion); CLog::Log(LOGDEBUG,"Instantiating addon using automatically obtained id of \"%s\" dependent on version %s of the xbmc.python api",addon->ID().c_str(),version.c_str()); } Py_DECREF(f); XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this callstack as being in a python context PyRun_FileExFlags(fp, CSpecialProtocol::TranslatePath(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL); } else CLog::Log(LOGERROR, "%s not found!", m_source); } else { //run script PyRun_String(m_source, m_Py_file_input, moduleDict, moduleDict); } } catch (const XbmcCommons::Exception& e) { e.LogThrowMessage(); } catch (...) { CLog::Log(LOGERROR, "failure in %s", m_source); } } if (!PyErr_Occurred()) CLog::Log(LOGINFO, "Scriptresult: Success"); else if (PyErr_ExceptionMatches(PyExc_SystemExit)) CLog::Log(LOGINFO, "Scriptresult: Aborted"); else { PythonBindings::PythonToCppException e; e.LogThrowMessage(); { CPyThreadState releaseGil; CSingleLock gc(g_graphicsContext); CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); if (pDlgToast) { CStdString desc; CStdString path; CStdString script; URIUtils::Split(m_source, path, script); if (script.Equals("default.py")) { CStdString path2; URIUtils::RemoveSlashAtEnd(path); URIUtils::Split(path, path2, script); } desc.Format(g_localizeStrings.Get(2100), script); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc); } } } PyObject *m = PyImport_AddModule((char*)"xbmc"); if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1))) CLog::Log(LOGERROR, "Scriptresult: failed to set abortRequested"); // make sure all sub threads have finished for(PyThreadState* s = state->interp->tstate_head, *old = NULL; s;) { if(s == state) { s = s->next; continue; } if(old != s) { CLog::Log(LOGINFO, "Scriptresult: Waiting on thread %"PRIu64, (uint64_t)s->thread_id); old = s; } CPyThreadState pyState; Sleep(100); pyState.Restore(); s = state->interp->tstate_head; } // pending calls must be cleared out XBMCAddon::RetardedAsynchCallbackHandler::clearPendingCalls(state); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); //set stopped event - this allows ::stop to run and kill remaining threads //this event has to be fired without holding m_pExecuter->m_critSection //before //Also the GIL (PyEval_AcquireLock) must not be held //if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) stoppedEvent.Set(); { CSingleLock lock(m_pExecuter->m_critSection); m_threadState = NULL; } PyEval_AcquireLock(); PyThreadState_Swap(state); m_pExecuter->DeInitializeInterpreter(); // run the gc before finishing if (!m_stopping && languageHook->HasRegisteredAddonClasses() && PyRun_SimpleString(GC_SCRIPT) == -1) CLog::Log(LOGERROR,"Failed to run the gc to clean up after running prior to shutting down the Interpreter %s",m_source); Py_EndInterpreter(state); // This is a total hack. Python doesn't necessarily release // all of the objects associated with the interpreter when // you end the interpreter. As a result there are objects // managed by the windowing system that still receive events // until python decides to clean them up. Python will eventually // clean them up on the creation or ending of a subsequent // interpreter. So we are going to keep creating and ending // interpreters until we have no more python objects hanging // around. if (languageHook->HasRegisteredAddonClasses()) { CLog::Log(LOGDEBUG, "The python script \"%s\" has left several " "classes in memory that we will be attempting to clean up. The classes include: %s", m_source, getListOfAddonClassesAsString(languageHook).c_str()); int countLimit; for (countLimit = 0; languageHook->HasRegisteredAddonClasses() && countLimit < 100; countLimit++) { PyThreadState* tmpstate = Py_NewInterpreter(); PyThreadState* oldstate = PyThreadState_Swap(tmpstate); if (PyRun_SimpleString(GC_SCRIPT) == -1) CLog::Log(LOGERROR,"Failed to run the gc to clean up after running %s",m_source); PyThreadState_Swap(oldstate); Py_EndInterpreter(tmpstate); } // If necessary and successfull, debug log the results. if (countLimit > 0 && !languageHook->HasRegisteredAddonClasses()) CLog::Log(LOGDEBUG,"It took %d Py_NewInterpreter/Py_EndInterpreter calls" " to clean up the classes leftover from running \"%s.\"", countLimit,m_source); // If not successful, produce an error message detailing what's been left behind if (languageHook->HasRegisteredAddonClasses()) CLog::Log(LOGERROR, "The python script \"%s\" has left several " "classes in memory that we couldn't clean up. The classes include: %s", m_source, getListOfAddonClassesAsString(languageHook).c_str()); } // unregister the language hook languageHook->UnregisterMe(); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); }
bool CPythonInvoker::execute(const std::string &script, const std::vector<std::string> &arguments) { // copy the code/script into a local string buffer #ifdef TARGET_WINDOWS CStdString strsrc = script; g_charsetConverter.utf8ToSystem(strsrc); m_source = new char[strsrc.length() + 1]; strcpy(m_source, strsrc); #else m_source = new char[script.length() + 1]; strcpy(m_source, script.c_str()); #endif // copy the arguments into a local buffer m_argc = arguments.size(); m_argv = new char*[m_argc]; for (unsigned int i = 0; i < m_argc; i++) { m_argv[i] = new char[arguments.at(i).length() + 1]; strcpy(m_argv[i], arguments.at(i).c_str()); } CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): start processing", GetId(), m_source); int m_Py_file_input = Py_file_input; // get the global lock PyEval_AcquireLock(); PyThreadState* state = Py_NewInterpreter(); if (state == NULL) { PyEval_ReleaseLock(); CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread state!", GetId(), m_source); return false; } // swap in my thread state PyThreadState_Swap(state); XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp)); languageHook->RegisterMe(); g_pythonParser.InitializeInterpreter(m_addon); setState(InvokerStateInitialized); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is %s", GetId(), m_source, m_source); // get path from script file name and add python path's // this is used for python so it will search modules from script path first CStdString scriptDir; URIUtils::GetDirectory(CSpecialProtocol::TranslatePath(m_source), scriptDir); URIUtils::RemoveSlashAtEnd(scriptDir); addPath(scriptDir); // add on any addon modules the user has installed ADDON::VECADDONS addons; ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons); for (unsigned int i = 0; i < addons.size(); ++i) addPath(CSpecialProtocol::TranslatePath(addons[i]->LibPath())); // we want to use sys.path so it includes site-packages // if this fails, default to using Py_GetPath PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete if (pathObj != NULL && PyList_Check(pathObj)) { for (int i = 0; i < PyList_Size(pathObj); i++) { PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete if (e != NULL && PyString_Check(e)) addPath(PyString_AsString(e)); // returns internal data, don't delete or modify } } else addPath(Py_GetPath()); Py_DECREF(sysMod); // release ref to sysMod // set current directory and python's path. if (m_argv != NULL) PySys_SetArgv(m_argc, m_argv); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_source, m_pythonPath.c_str()); PySys_SetPath((char *)m_pythonPath.c_str()); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_source, scriptDir.c_str()); PyObject* module = PyImport_AddModule((char*)"__main__"); PyObject* moduleDict = PyModule_GetDict(module); // when we are done initing we store thread state so we can be aborted PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // we need to check if we was asked to abort before we had inited bool stopping = false; { CSingleLock lock(m_critical); m_threadState = state; stopping = m_stop; } PyEval_AcquireLock(); PyThreadState_Swap(state); bool failed = false; if (!stopping) { try { // run script from file // We need to have python open the file because on Windows the DLL that python // is linked against may not be the DLL that xbmc is linked against so // passing a FILE* to python from an fopen has the potential to crash. PyObject* file = PyFile_FromString((char *) CSpecialProtocol::TranslatePath(m_source).c_str(), (char*)"r"); FILE *fp = PyFile_AsFile(file); if (fp != NULL) { PyObject *f = PyString_FromString(CSpecialProtocol::TranslatePath(m_source).c_str()); PyDict_SetItemString(moduleDict, "__file__", f); if (m_addon.get() != NULL) { PyObject *pyaddonid = PyString_FromString(m_addon->ID().c_str()); PyDict_SetItemString(moduleDict, "__xbmcaddonid__", pyaddonid); CStdString version = ADDON::GetXbmcApiVersionDependency(m_addon); PyObject *pyxbmcapiversion = PyString_FromString(version.c_str()); PyDict_SetItemString(moduleDict, "__xbmcapiversion__", pyxbmcapiversion); 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_source, m_addon->ID().c_str(), version.c_str()); } Py_DECREF(f); setState(InvokerStateRunning); XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this callstack as being in a python context PyRun_FileExFlags(fp, CSpecialProtocol::TranslatePath(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL); } else CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(), m_source, m_source); } catch (const XbmcCommons::Exception& e) { setState(InvokerStateFailed); e.LogThrowMessage(); failed = true; } catch (...) { setState(InvokerStateFailed); CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failure in script", GetId(), m_source); failed = true; } } bool systemExitThrown = false; if (!failed && !PyErr_Occurred()) { CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_source); setState(InvokerStateDone); } else if (PyErr_ExceptionMatches(PyExc_SystemExit)) { systemExitThrown = true; CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_source); setState(InvokerStateFailed); } else { setState(InvokerStateFailed); // if it failed with an exception we already logged the details if (!failed) { PythonBindings::PythonToCppException e; e.LogThrowMessage(); } { CPyThreadState releaseGil; CSingleLock gc(g_graphicsContext); CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); if (pDlgToast != NULL) { CStdString desc; CStdString script; if (m_addon.get() != NULL) script = m_addon->Name(); else { CStdString path; URIUtils::Split(m_source, path, script); if (script.Equals("default.py")) { CStdString path2; URIUtils::RemoveSlashAtEnd(path); URIUtils::Split(path, path2, script); } } desc.Format(g_localizeStrings.Get(2100), script); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc); } } } // no need to do anything else because the script has already stopped if (failed) return true; PyObject *m = PyImport_AddModule((char*)"xbmc"); if (m == NULL || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1))) CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_source); // make sure all sub threads have finished for (PyThreadState* s = state->interp->tstate_head, *old = NULL; s;) { if (s == state) { s = s->next; continue; } if (old != s) { CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %"PRIu64, GetId(), m_source, (uint64_t)s->thread_id); old = s; } CPyThreadState pyState; Sleep(100); pyState.Restore(); s = state->interp->tstate_head; } // pending calls must be cleared out XBMCAddon::RetardedAsynchCallbackHandler::clearPendingCalls(state); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // set stopped event - this allows ::stop to run and kill remaining threads // this event has to be fired without holding m_critical // also the GIL (PyEval_AcquireLock) must not be held // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) m_stoppedEvent.Set(); { CSingleLock lock(m_critical); m_threadState = NULL; } PyEval_AcquireLock(); PyThreadState_Swap(state); g_pythonParser.DeInitializeInterpreter(); // run the gc before finishing // // if the script exited by throwing a SystemExit excepton then going back // into the interpreter causes this python bug to get hit: // http://bugs.python.org/issue10582 // and that causes major failures. So we are not going to go back in // to run the GC if that's the case. if (!m_stop && languageHook->HasRegisteredAddonClasses() && !systemExitThrown && PyRun_SimpleString(GC_SCRIPT) == -1) CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_source); Py_EndInterpreter(state); // If we still have objects left around, produce an error message detailing what's been left behind if (languageHook->HasRegisteredAddonClasses()) CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several " "classes in memory that we couldn't clean up. The classes include: %s", GetId(), m_source, m_source, getListOfAddonClassesAsString(languageHook).c_str()); // unregister the language hook languageHook->UnregisterMe(); PyEval_ReleaseLock(); return true; }
void XBPyThread::Process() { CLog::Log(LOGDEBUG,"Python thread: start processing"); int m_Py_file_input = Py_file_input; // get the global lock PyEval_AcquireLock(); PyThreadState* state = Py_NewInterpreter(); if (!state) { PyEval_ReleaseLock(); CLog::Log(LOGERROR,"Python thread: FAILED to get thread state!"); return; } // swap in my thread state PyThreadState_Swap(state); m_pExecuter->InitializeInterpreter(); CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source); // get path from script file name and add python path's // this is used for python so it will search modules from script path first CStdString scriptDir; URIUtils::GetDirectory(_P(m_source), scriptDir); URIUtils::RemoveSlashAtEnd(scriptDir); CStdString path = scriptDir; // add on any addon modules the user has installed ADDON::VECADDONS addons; ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons); for (unsigned int i = 0; i < addons.size(); ++i) path += PY_PATH_SEP + _P(addons[i]->LibPath()); // and add on whatever our default path is path += PY_PATH_SEP; { // we want to use sys.path so it includes site-packages // if this fails, default to using Py_GetPath PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete if( pathObj && PyList_Check(pathObj) ) { for( int i = 0; i < PyList_Size(pathObj); i++ ) { PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete if( e && PyString_Check(e) ) { path += PyString_AsString(e); // returns internal data, don't delete or modify path += PY_PATH_SEP; } } } else { path += Py_GetPath(); } Py_DECREF(sysMod); // release ref to sysMod } // set current directory and python's path. if (m_argv != NULL) PySys_SetArgv(m_argc, m_argv); CLog::Log(LOGDEBUG, "%s - Setting the Python path to %s", __FUNCTION__, path.c_str()); PySys_SetPath((char *)path.c_str()); CLog::Log(LOGDEBUG, "%s - Entering source directory %s", __FUNCTION__, scriptDir.c_str()); PyObject* module = PyImport_AddModule((char*)"__main__"); PyObject* moduleDict = PyModule_GetDict(module); // when we are done initing we store thread state so we can be aborted PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // we need to check if we was asked to abort before we had inited bool stopping = false; { CSingleLock lock(m_pExecuter->m_critSection); m_threadState = state; stopping = m_stopping; } PyEval_AcquireLock(); PyThreadState_Swap(state); if (!stopping) { if (m_type == 'F') { // run script from file // We need to have python open the file because on Windows the DLL that python // is linked against may not be the DLL that xbmc is linked against so // passing a FILE* to python from an fopen has the potential to crash. PyObject* file = PyFile_FromString((char *) _P(m_source).c_str(), (char*)"r"); FILE *fp = PyFile_AsFile(file); if (fp) { PyObject *f = PyString_FromString(_P(m_source).c_str()); PyDict_SetItemString(moduleDict, "__file__", f); Py_DECREF(f); PyRun_File(fp, _P(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict); // Get a reference to the main module // and global dictionary PyObject* main_module = PyImport_AddModule((char*)"__main__"); PyObject* global_dict = PyModule_GetDict(main_module); // Extract a reference to the function "func_name" // from the global dictionary PyObject* expression = PyDict_GetItemString(global_dict, "xbmcclosefilehack"); if (!PyObject_CallFunction(expression,(char*)"(O)",file)) CLog::Log(LOGERROR,"Failed to close the script file %s",_P(m_source).c_str()); } else CLog::Log(LOGERROR, "%s not found!", m_source); } else { //run script PyRun_String(m_source, m_Py_file_input, moduleDict, moduleDict); } } if (!PyErr_Occurred()) CLog::Log(LOGINFO, "Scriptresult: Success"); else if (PyErr_ExceptionMatches(PyExc_SystemExit)) CLog::Log(LOGINFO, "Scriptresult: Aborted"); else { PyObject* exc_type; PyObject* exc_value; PyObject* exc_traceback; PyObject* pystring; pystring = NULL; PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); if (exc_type == 0 && exc_value == 0 && exc_traceback == 0) { CLog::Log(LOGINFO, "Strange: No Python exception occured"); } else { if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && (PyString_Check(pystring))) { PyObject *tracebackModule; CLog::Log(LOGINFO, "-->Python script returned the following error<--"); CLog::Log(LOGERROR, "Error Type: %s", PyString_AsString(PyObject_Str(exc_type))); if (PyObject_Str(exc_value)) CLog::Log(LOGERROR, "Error Contents: %s", PyString_AsString(PyObject_Str(exc_value))); tracebackModule = PyImport_ImportModule((char*)"traceback"); if (tracebackModule != NULL) { PyObject *tbList, *emptyString, *strRetval; tbList = PyObject_CallMethod(tracebackModule, (char*)"format_exception", (char*)"OOO", exc_type, exc_value == NULL ? Py_None : exc_value, exc_traceback == NULL ? Py_None : exc_traceback); emptyString = PyString_FromString(""); strRetval = PyObject_CallMethod(emptyString, (char*)"join", (char*)"O", tbList); CLog::Log(LOGERROR, "%s", PyString_AsString(strRetval)); Py_DECREF(tbList); Py_DECREF(emptyString); Py_DECREF(strRetval); Py_DECREF(tracebackModule); } CLog::Log(LOGINFO, "-->End of Python script error report<--"); } else { pystring = NULL; CLog::Log(LOGINFO, "<unknown exception type>"); } CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); if (pDlgToast) { CStdString desc; CStdString path; CStdString script; URIUtils::Split(m_source, path, script); if (script.Equals("default.py")) { CStdString path2; URIUtils::RemoveSlashAtEnd(path); URIUtils::Split(path, path2, script); } desc.Format(g_localizeStrings.Get(2100), script); pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc); } } Py_XDECREF(exc_type); Py_XDECREF(exc_value); // caller owns all 3 Py_XDECREF(exc_traceback); // already NULL'd out Py_XDECREF(pystring); } PyObject *m = PyImport_AddModule((char*)"xbmc"); if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1))) CLog::Log(LOGERROR, "Scriptresult: failed to set abortRequested"); // make sure all sub threads have finished for(PyThreadState* s = state->interp->tstate_head, *old = NULL; s;) { if(s == state) { s = s->next; continue; } if(old != s) { CLog::Log(LOGINFO, "Scriptresult: Waiting on thread %"PRIu64, (uint64_t)s->thread_id); old = s; } CPyThreadState pyState; Sleep(100); pyState.Restore(); s = state->interp->tstate_head; } // pending calls must be cleared out PyXBMC_ClearPendingCalls(state); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); { CSingleLock lock(m_pExecuter->m_critSection); m_threadState = NULL; } PyEval_AcquireLock(); PyThreadState_Swap(state); m_pExecuter->DeInitializeInterpreter(); Py_EndInterpreter(state); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); }