Exemplo n.º 1
0
  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;
  }
Exemplo n.º 2
0
  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;
  }
Exemplo n.º 3
0
    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);
    }
Exemplo n.º 4
0
    // 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);
      
    }  
Exemplo n.º 5
0
  PyObject* Player_Pause(PyObject *self, PyObject *args)
  {
    CPyThreadState pyState;
    g_application.getApplicationMessenger().MediaPause();
    pyState.Restore();

    Py_INCREF(Py_None);
    return Py_None;
  }
Exemplo n.º 6
0
  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);
  }
Exemplo n.º 7
0
  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;
  }
Exemplo n.º 8
0
  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;
  }
Exemplo n.º 9
0
  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;
  }
Exemplo n.º 10
0
 // 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;
   
 }
Exemplo n.º 11
0
    // 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);
    }      
Exemplo n.º 12
0
  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());
  } 
Exemplo n.º 13
0
  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());
  }
Exemplo n.º 14
0
  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;
  }
Exemplo n.º 15
0
  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;
  }
Exemplo n.º 16
0
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();
}
Exemplo n.º 17
0
  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());
  }
Exemplo n.º 18
0
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;
}
Exemplo n.º 19
0
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();
}
Exemplo n.º 20
0
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;
}