PyObject* Player_New(PyTypeObject *type, PyObject *args, PyObject *kwds) { Player *self; int playerCore=EPC_NONE; self = (Player*)type->tp_alloc(type, 0); if (!self) return NULL; if (!PyArg_ParseTuple(args, (char*)"|i", &playerCore)) return NULL; self->iPlayList = PLAYLIST_MUSIC; CPyThreadState pyState; self->pPlayer = new CPythonPlayer(); pyState.Restore(); self->pPlayer->SetCallback(PyThreadState_Get(), (PyObject*)self); self->playerCore = EPC_NONE; if (playerCore == EPC_DVDPLAYER || playerCore == EPC_MPLAYER || playerCore == EPC_PAPLAYER) { self->playerCore = (EPLAYERCORES)playerCore; } return (PyObject*)self; }
PyObject* Addon_SetSetting(Addon *self, PyObject *args, PyObject *kwds) { static const char *keywords[] = { "id", "value", NULL }; char *id = NULL; PyObject *pValue = NULL; if (!PyArg_ParseTupleAndKeywords( args, kwds, (char*)"sO", (char**)keywords, &id, &pValue )) { return NULL; }; CStdString value; if (!id || !PyXBMCGetUnicodeString(value, pValue, 1)) { PyErr_SetString(PyExc_ValueError, "Invalid id or value!"); return NULL; } AddonPtr addon(self->pAddon); CPyThreadState pyState; addon->UpdateSetting(id, value); addon->SaveSettings(); pyState.Restore(); Py_INCREF(Py_None); return Py_None; }
PyObject* vfs_copy(PyObject *self, PyObject *args) { PyObject *f_line; PyObject *d_line; if (!PyArg_ParseTuple( args, (char*)"OO", &f_line, &d_line)) { return NULL; } CStdString strSource; CStdString strDestnation; bool bResult = true; if (!PyXBMCGetUnicodeString(strSource, f_line, 1)) return NULL; if (!PyXBMCGetUnicodeString(strDestnation, d_line, 1)) return NULL; CPyThreadState pyState; bResult = CFile::Cache(strSource, strDestnation); pyState.Restore(); return Py_BuildValue((char*)"b", bResult); }
// rename a file PyObject* vfs_rename(File *self, PyObject *args, PyObject *kwds) { PyObject *f_line; PyObject *d_line; if (!PyArg_ParseTuple( args, (char*)"OO", &f_line, &d_line)) { return NULL; } CStdString strSource; CStdString strDestnation; if (!PyXBMCGetUnicodeString(strSource, f_line, 1)) return NULL; if (!PyXBMCGetUnicodeString(strDestnation, d_line, 1)) return NULL; bool bResult; CPyThreadState pyState; bResult = self->pFile->Rename(strSource,strDestnation); pyState.Restore(); return Py_BuildValue((char*)"b", bResult); }
PyObject* Player_Pause(PyObject *self, PyObject *args) { CPyThreadState pyState; g_application.getApplicationMessenger().MediaPause(); pyState.Restore(); Py_INCREF(Py_None); return Py_None; }
void Player_Dealloc(Player* self) { self->pPlayer->SetCallback(NULL, NULL); CPyThreadState pyState; self->pPlayer->Release(); pyState.Restore(); self->pPlayer = NULL; self->ob_type->tp_free((PyObject*)self); }
PyObject* Addon_OpenSettings(Addon *self, PyObject *args, PyObject *kwds) { // show settings dialog AddonPtr addon(self->pAddon); CPyThreadState pyState; CGUIDialogAddonSettings::ShowAndGetInput(addon); pyState.Restore(); Py_INCREF(Py_None); return Py_None; }
PyObject* Player_PlayPrevious(Player *self, PyObject *args) { // force a playercore before playing g_application.m_eForcedNextPlayer = self->playerCore; CPyThreadState pyState; g_application.getApplicationMessenger().PlayListPlayerPrevious(); pyState.Restore(); Py_INCREF(Py_None); return Py_None; }
PyObject* Monitor_New(PyTypeObject *type, PyObject *args, PyObject *kwds) { Monitor *self; self = (Monitor*)type->tp_alloc(type, 0); if (!self) return NULL; std::string addonId; if (!PyXBMCGetAddonId(addonId) || addonId.empty()) { PyErr_SetString((PyObject*)self, "Unable to identify addon"); return NULL; } CPyThreadState pyState; self->pMonitor = new CPythonMonitor(); pyState.Restore(); self->pMonitor->Id = addonId; self->pMonitor->SetCallback(PyThreadState_Get(), (PyObject*)self); return (PyObject*)self; }
// delete a file PyObject* vfs_delete(File *self, PyObject *args, PyObject *kwds) { PyObject *f_line; if (!PyArg_ParseTuple( args, (char*)"O", &f_line)) { return NULL; } CStdString strSource; if (!PyXBMCGetUnicodeString(strSource, f_line, 1)) return NULL; CPyThreadState pyState; self->pFile->Delete(strSource); pyState.Restore(); Py_INCREF(Py_None); return Py_None; }
// check for a file or folder existance, mimics Pythons os.path.exists() PyObject* vfs_exists(File *self, PyObject *args, PyObject *kwds) { PyObject *f_line; if (!PyArg_ParseTuple( args, (char*)"O", &f_line)) { return NULL; } CStdString strSource; if (!PyXBMCGetUnicodeString(strSource, f_line, 1)) return NULL; bool bResult; CPyThreadState pyState; bResult = self->pFile->Exists(strSource, false); pyState.Restore(); return Py_BuildValue((char*)"b", bResult); }
PyObject* XBMC_subHashAndFileSize(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *f_line; if (!PyArg_ParseTuple( args, (char*)"O", &f_line)) { return NULL; } CStdString strSource; if (!PyXBMCGetUnicodeString(strSource, f_line, 1)) return NULL; CStdString strSize; CStdString strHash; CPyThreadState pyState; CFileUtils::SubtitleFileSizeAndHash(strSource, strSize, strHash); pyState.Restore(); return Py_BuildValue((char*)"ss",strSize.c_str(), strHash.c_str()); }
PyObject* XBMC_ExecuteHttpApi(PyObject *self, PyObject *args) { char *cLine = NULL; if (!PyArg_ParseTuple(args, (char*)"s", &cLine)) return NULL; CPyThreadState pyLock; if (!m_pXbmcHttp) m_pXbmcHttp = new CXbmcHttp(); CStdString method = cLine; int open, close; CStdString parameter="", cmd=cLine, execute; open = cmd.Find("("); if (open>0) { close=cmd.length(); while (close>open && cmd.Mid(close,1)!=")") close--; if (close>open) { parameter = cmd.Mid(open + 1, close - open - 1); parameter.Replace(",",";"); execute = cmd.Left(open); } else //open bracket but no close { pyLock.Restore(); return PyString_FromString(""); } } else //no parameters execute = cmd; CURL::Decode(parameter); pyLock.Restore(); return PyString_FromString(CHttpApi::MethodCall(execute, parameter).c_str()); }
PyObject* Player_PlaySelected(Player *self, PyObject *args) { int iItem; if (!PyArg_ParseTuple(args, (char*)"i", &iItem)) return NULL; // force a playercore before playing g_application.m_eForcedNextPlayer = self->playerCore; if (g_playlistPlayer.GetCurrentPlaylist() != self->iPlayList) { g_playlistPlayer.SetCurrentPlaylist(self->iPlayList); } g_playlistPlayer.SetCurrentSong(iItem); CPyThreadState pyState; g_application.getApplicationMessenger().PlayListPlayerPlay(iItem); pyState.Restore(); //g_playlistPlayer.Play(iItem); //CLog::Log(LOGNOTICE, "Current Song After Play: %i", g_playlistPlayer.GetCurrentSong()); Py_INCREF(Py_None); return Py_None; }
PyObject* XBMC_Sleep(PyObject *self, PyObject *args) { PyObject *pObject; if (!PyArg_ParseTuple(args, (char*)"O", &pObject)) return NULL; if (!PyInt_Check(pObject)) { PyErr_Format(PyExc_TypeError, "argument must be a bool(integer) value"); return NULL; } long i = PyInt_AsLong(pObject); //while(i != 0) //{ CPyThreadState pyState; Sleep(i);//(500); pyState.Restore(); PyXBMC_MakePendingCalls(); //i = PyInt_AsLong(pObject); //} Py_INCREF(Py_None); return Py_None; }
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(); }
PyObject* Dialog_Browse(PyObject *self, PyObject *args) { int browsetype = 0; char useThumbs = false; char useFileDirectories = false; char enableMultiple = false; CStdString value; CStdStringArray valuelist; PyObject* unicodeLine[3]; string utf8Line[3]; char *cDefault = NULL; PyObject *result; for (int i = 0; i < 3; i++) unicodeLine[i] = NULL; if (!PyArg_ParseTuple(args, (char*)"iOO|Obbsb", &browsetype , &unicodeLine[0], &unicodeLine[1], &unicodeLine[2], &useThumbs, &useFileDirectories, &cDefault, &enableMultiple)) { return NULL; } for (int i = 0; i < 3; i++) { if (unicodeLine[i] && !PyXBMCGetUnicodeString(utf8Line[i], unicodeLine[i], i+1)) return NULL; } VECSOURCES *shares = g_settings.GetSourcesFromType(utf8Line[1]); if (!shares) return NULL; if (useFileDirectories && !utf8Line[2].size() == 0) utf8Line[2] += "|.rar|.zip"; value = cDefault; CPyThreadState pyState; if (browsetype == 1) { if (enableMultiple) CGUIDialogFileBrowser::ShowAndGetFileList(*shares, utf8Line[2], utf8Line[0], valuelist, 0 != useThumbs, 0 != useFileDirectories); else CGUIDialogFileBrowser::ShowAndGetFile(*shares, utf8Line[2], utf8Line[0], value, 0 != useThumbs, 0 != useFileDirectories); } else if (browsetype == 2) { if (enableMultiple) CGUIDialogFileBrowser::ShowAndGetImageList(*shares, utf8Line[0], valuelist); else CGUIDialogFileBrowser::ShowAndGetImage(*shares, utf8Line[0], value); } else CGUIDialogFileBrowser::ShowAndGetDirectory(*shares, utf8Line[0], value, browsetype != 0); pyState.Restore(); if (enableMultiple && (browsetype == 1 || browsetype == 2)) { result = PyTuple_New(valuelist.size()); if (!result) return NULL; for (unsigned int i = 0; i < valuelist.size(); i++) PyTuple_SetItem(result, i, PyString_FromString(valuelist.at(i).c_str())); return result; } else return Py_BuildValue((char*)"s", value.c_str()); }
bool CPythonInvoker::execute(const std::string &script, const std::vector<std::string> &arguments) { // copy the code/script into a local string buffer m_sourceFile = script; // 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_sourceFile.c_str()); 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_sourceFile.c_str()); return false; } // swap in my thread state PyThreadState_Swap(state); XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> languageHook(new XBMCAddon::Python::PythonLanguageHook(state->interp)); languageHook->RegisterMe(); onInitialization(); setState(InvokerStateInitialized); std::string realFilename(CSpecialProtocol::TranslatePath(m_sourceFile)); if (realFilename == m_sourceFile) CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\"", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str()); else CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\" (\"%s\")", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), realFilename.c_str()); // 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 std::string scriptDir = URIUtils::GetDirectory(realFilename); URIUtils::RemoveSlashAtEnd(scriptDir); addPath(scriptDir); // add all addon module dependecies to path if (m_addon) { std::set<std::string> paths; getAddonModuleDeps(m_addon, paths); for (std::set<std::string>::const_iterator it = paths.begin(); it != paths.end(); ++it) addPath(*it); } else { // for backwards compatibility. // we don't have any addon so just add all addon modules installed CLog::Log(LOGWARNING, "CPythonInvoker(%d): Script invoked without an addon. Adding all addon " "modules installed to python path as fallback. This behaviour will be removed in future " "version.", GetId()); 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)) addNativePath(PyString_AsString(e)); // returns internal data, don't delete or modify } } else addNativePath(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); #ifdef TARGET_WINDOWS std::string pyPathUtf8; g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), pyPathUtf8.c_str()); #else // ! TARGET_WINDOWS CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), m_pythonPath.c_str()); #endif // ! TARGET_WINDOWS PySys_SetPath((char *)m_pythonPath.c_str()); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_sourceFile.c_str(), 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. std::string nativeFilename(realFilename); // filename in system encoding #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()); return false; } #endif PyObject* file = PyFile_FromString((char *)nativeFilename.c_str(), (char*)"r"); FILE *fp = PyFile_AsFile(file); if (fp != NULL) { PyObject *f = PyString_FromString(nativeFilename.c_str()); PyDict_SetItemString(moduleDict, "__file__", f); onPythonModuleInitialization(moduleDict); 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, nativeFilename.c_str(), m_Py_file_input, moduleDict, moduleDict, 1, NULL); } else CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str()); } 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_sourceFile.c_str()); failed = true; } } bool systemExitThrown = false; InvokerState stateToSet; if (!failed && !PyErr_Occurred()) { CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_sourceFile.c_str()); stateToSet = InvokerStateDone; onSuccess(); } else if (PyErr_ExceptionMatches(PyExc_SystemExit)) { systemExitThrown = true; CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_sourceFile.c_str()); stateToSet = InvokerStateFailed; onAbort(); } else { stateToSet = InvokerStateFailed; // if it failed with an exception we already logged the details if (!failed) { PythonBindings::PythonToCppException e; e.LogThrowMessage(); } onError(); } // no need to do anything else because the script has already stopped if (failed) { setState(stateToSet); 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_sourceFile.c_str()); // 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_sourceFile.c_str(), (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); onDeinitialization(); // 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_sourceFile.c_str()); 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_sourceFile.c_str(), m_sourceFile.c_str(), getListOfAddonClassesAsString(languageHook).c_str()); // unregister the language hook languageHook->UnregisterMe(); PyEval_ReleaseLock(); setState(stateToSet); 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(); }
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; }