2 * Copyright (C) 2005-2009 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
26 // python.h should always be included first before any other includes
31 #include "XBPythonDll.h"
32 #include "filesystem/SpecialProtocol.h"
33 #include "guilib/GUIWindowManager.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "guilib/LocalizeStrings.h"
36 #include "utils/log.h"
37 #include "threads/SingleLock.h"
38 #include "utils/URIUtils.h"
39 #include "addons/AddonManager.h"
41 #include "XBPyThread.h"
44 #include "xbmcmodule/pyutil.h"
45 #include "xbmcmodule/pythreadstate.h"
48 #pragma code_seg("PY_TEXT")
49 #pragma data_seg("PY_DATA")
50 #pragma bss_seg("PY_BSS")
51 #pragma const_seg("PY_RDATA")
55 extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
57 #define fopen_utf8 fopen
60 #define PY_PATH_SEP DELIM
64 int xbp_chdir(const char *dirname);
65 char* dll_getenv(const char* szKey);
68 XBPyThread::XBPyThread(XBPython *pExecuter, int id)
70 CLog::Log(LOGDEBUG,"new python thread created. id=%d", id);
71 m_pExecuter = pExecuter;
80 XBPyThread::~XBPyThread()
83 g_pythonParser.PulseGlobalEvent();
84 CLog::Log(LOGDEBUG,"waiting for python thread %d to stop", m_id);
86 CLog::Log(LOGDEBUG,"python thread %d destructed", m_id);
90 for (unsigned int i = 0; i < m_argc; i++)
96 int XBPyThread::evalFile(const CStdString &src)
99 m_source = new char[src.GetLength()+1];
100 strcpy(m_source, src);
105 int XBPyThread::evalString(const CStdString &src)
108 m_source = new char[src.GetLength()+1];
109 strcpy(m_source, src);
114 int XBPyThread::setArgv(const std::vector<CStdString> &argv)
116 m_argc = argv.size();
117 m_argv = new char*[m_argc];
118 for(unsigned int i = 0; i < m_argc; i++)
120 m_argv[i] = new char[argv[i].GetLength()+1];
121 strcpy(m_argv[i], argv[i].c_str());
126 void XBPyThread::OnStartup()
128 CThread::SetName("Python Thread");
131 void XBPyThread::Process()
133 CLog::Log(LOGDEBUG,"Python thread: start processing");
135 int m_Py_file_input = Py_file_input;
137 // get the global lock
138 PyEval_AcquireLock();
139 PyThreadState* state = Py_NewInterpreter();
142 PyEval_ReleaseLock();
143 CLog::Log(LOGERROR,"Python thread: FAILED to get thread state!");
146 // swap in my thread state
147 PyThreadState_Swap(state);
149 m_pExecuter->InitializeInterpreter();
151 CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source);
153 // get path from script file name and add python path's
154 // this is used for python so it will search modules from script path first
155 CStdString scriptDir;
156 URIUtils::GetDirectory(_P(m_source), scriptDir);
157 URIUtils::RemoveSlashAtEnd(scriptDir);
158 CStdString path = scriptDir;
160 // add on any addon modules the user has installed
161 ADDON::VECADDONS addons;
162 ADDON::CAddonMgr::Get().GetAddons(ADDON::ADDON_SCRIPT_MODULE, addons);
163 for (unsigned int i = 0; i < addons.size(); ++i)
164 path += PY_PATH_SEP + _P(addons[i]->LibPath());
166 // and add on whatever our default path is
169 #if (defined USE_EXTERNAL_PYTHON)
171 // we want to use sys.path so it includes site-packages
172 // if this fails, default to using Py_GetPath
173 PyObject *sysMod(PyImport_ImportModule((char*)"sys")); // must call Py_DECREF when finished
174 PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
175 PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete
177 if( pathObj && PyList_Check(pathObj) )
179 for( int i = 0; i < PyList_Size(pathObj); i++ )
181 PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
182 if( e && PyString_Check(e) )
184 path += PyString_AsString(e); // returns internal data, don't delete or modify
191 path += Py_GetPath();
193 Py_DECREF(sysMod); // release ref to sysMod
196 path += Py_GetPath();
199 // set current directory and python's path.
201 PySys_SetArgv(m_argc, m_argv);
203 CLog::Log(LOGDEBUG, "%s - Setting the Python path to %s", __FUNCTION__, path.c_str());
205 PySys_SetPath((char *)path.c_str());
207 CLog::Log(LOGDEBUG, "%s - Entering source directory %s", __FUNCTION__, scriptDir.c_str());
209 PyObject* module = PyImport_AddModule((char*)"__main__");
210 PyObject* moduleDict = PyModule_GetDict(module);
212 // when we are done initing we store thread state so we can be aborted
213 PyThreadState_Swap(NULL);
214 PyEval_ReleaseLock();
216 // we need to check if we was asked to abort before we had inited
217 bool stopping = false;
218 { CSingleLock lock(m_pExecuter->m_critSection);
219 m_threadState = state;
220 stopping = m_stopping;
223 PyEval_AcquireLock();
224 PyThreadState_Swap(state);
226 xbp_chdir(scriptDir.c_str());
232 #ifdef USE_EXTERNAL_PYTHON
233 // run script from file
234 // We need to have python open the file because on Windows the DLL that python
235 // is linked against may not be the DLL that xbmc is linked against so
236 // passing a FILE* to python from an fopen has the potential to crash.
237 PyObject* file = PyFile_FromString((char *) _P(m_source).c_str(), (char*)"r");
238 FILE *fp = PyFile_AsFile(file);
240 FILE *fp = fopen_utf8(_P(m_source).c_str(), "r");
245 PyObject *f = PyString_FromString(_P(m_source).c_str());
246 PyDict_SetItemString(moduleDict, "__file__", f);
248 PyRun_File(fp, _P(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict);
250 #ifdef USE_EXTERNAL_PYTHON
251 // Get a reference to the main module
252 // and global dictionary
253 PyObject* main_module = PyImport_AddModule((char*)"__main__");
254 PyObject* global_dict = PyModule_GetDict(main_module);
256 // Extract a reference to the function "func_name"
257 // from the global dictionary
258 PyObject* expression = PyDict_GetItemString(global_dict, "xbmcclosefilehack");
260 if (!PyObject_CallFunction(expression,(char*)"(O)",file))
261 CLog::Log(LOGERROR,"Failed to close the script file %s",_P(m_source).c_str());
267 CLog::Log(LOGERROR, "%s not found!", m_source);
272 PyRun_String(m_source, m_Py_file_input, moduleDict, moduleDict);
276 if (!PyErr_Occurred())
277 CLog::Log(LOGINFO, "Scriptresult: Success");
278 else if (PyErr_ExceptionMatches(PyExc_SystemExit))
279 CLog::Log(LOGINFO, "Scriptresult: Aborted");
284 PyObject* exc_traceback;
288 PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
289 if (exc_type == 0 && exc_value == 0 && exc_traceback == 0)
291 CLog::Log(LOGINFO, "Strange: No Python exception occured");
295 if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && (PyString_Check(pystring)))
297 PyObject *tracebackModule;
299 CLog::Log(LOGINFO, "-->Python script returned the following error<--");
300 CLog::Log(LOGERROR, "Error Type: %s", PyString_AsString(PyObject_Str(exc_type)));
301 if (PyObject_Str(exc_value))
302 CLog::Log(LOGERROR, "Error Contents: %s", PyString_AsString(PyObject_Str(exc_value)));
304 tracebackModule = PyImport_ImportModule((char*)"traceback");
305 if (tracebackModule != NULL)
307 PyObject *tbList, *emptyString, *strRetval;
309 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);
310 emptyString = PyString_FromString("");
311 strRetval = PyObject_CallMethod(emptyString, (char*)"join", (char*)"O", tbList);
313 CLog::Log(LOGERROR, "%s", PyString_AsString(strRetval));
316 Py_DECREF(emptyString);
317 Py_DECREF(strRetval);
318 Py_DECREF(tracebackModule);
320 CLog::Log(LOGINFO, "-->End of Python script error report<--");
325 CLog::Log(LOGINFO, "<unknown exception type>");
328 CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
334 URIUtils::Split(m_source, path, script);
335 if (script.Equals("default.py"))
338 URIUtils::RemoveSlashAtEnd(path);
339 URIUtils::Split(path, path2, script);
342 desc.Format(g_localizeStrings.Get(2100), script);
343 pDlgToast->QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(257), desc);
347 Py_XDECREF(exc_type);
348 Py_XDECREF(exc_value); // caller owns all 3
349 Py_XDECREF(exc_traceback); // already NULL'd out
350 Py_XDECREF(pystring);
353 PyObject *m = PyImport_AddModule((char*)"xbmc");
354 if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
355 CLog::Log(LOGERROR, "Scriptresult: failed to set abortRequested");
357 // make sure all sub threads have finished
358 for(PyThreadState* s = state->interp->tstate_head, *old = NULL; s;)
367 CLog::Log(LOGINFO, "Scriptresult: Waiting on thread %"PRIu64, (uint64_t)s->thread_id);
371 CPyThreadState pyState;
375 s = state->interp->tstate_head;
378 // pending calls must be cleared out
379 PyXBMC_ClearPendingCalls(state);
381 PyThreadState_Swap(NULL);
382 PyEval_ReleaseLock();
384 { CSingleLock lock(m_pExecuter->m_critSection);
385 m_threadState = NULL;
388 PyEval_AcquireLock();
389 PyThreadState_Swap(state);
391 m_pExecuter->DeInitializeInterpreter();
393 Py_EndInterpreter(state);
394 PyThreadState_Swap(NULL);
396 PyEval_ReleaseLock();
399 void XBPyThread::OnExit()
401 m_pExecuter->setDone(m_id);
404 void XBPyThread::OnException()
406 PyThreadState_Swap(NULL);
407 PyEval_ReleaseLock();
409 CSingleLock lock(m_pExecuter->m_critSection);
410 m_threadState = NULL;
411 CLog::Log(LOGERROR,"%s, abnormally terminating python thread", __FUNCTION__);
412 m_pExecuter->setDone(m_id);
415 bool XBPyThread::isStopping() {
419 void XBPyThread::stop()
421 CSingleLock lock(m_pExecuter->m_critSection);
429 PyEval_AcquireLock();
430 PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
433 m = PyImport_AddModule((char*)"xbmc");
434 if(!m || PyObject_SetAttrString(m, (char*)"abortRequested", PyBool_FromLong(1)))
435 CLog::Log(LOGERROR, "XBPyThread::stop - failed to set abortRequested");
437 for(PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next)
439 Py_XDECREF(state->async_exc);
440 state->async_exc = PyExc_SystemExit;
441 Py_XINCREF(state->async_exc);
444 PyThreadState_Swap(old);
445 PyEval_ReleaseLock();