python: extend CPythonInvoker to be able to handle interpreter initialization/deiniti...
authormontellese <montellese@xbmc.org>
Wed, 14 Aug 2013 08:00:52 +0000 (10:00 +0200)
committermontellese <montellese@xbmc.org>
Sun, 8 Sep 2013 21:27:32 +0000 (23:27 +0200)
xbmc/interfaces/generic/ILanguageInvoker.h
xbmc/interfaces/generic/LanguageInvokerThread.cpp
xbmc/interfaces/python/PythonInvoker.cpp
xbmc/interfaces/python/PythonInvoker.h

index 9a0c7cd..e939945 100644 (file)
@@ -68,12 +68,12 @@ protected:
   virtual bool execute(const std::string &script, const std::vector<std::string> &arguments) = 0;
   virtual bool stop(bool abort) = 0;
 
-  virtual void onError()
+  virtual void onExectuionFailed()
   {
     if (m_invocationHandler)
       m_invocationHandler->OnScriptEnded(this);
   }
-  virtual void onDone()
+  virtual void onExectuionDone()
   {
     if (m_invocationHandler)
       m_invocationHandler->OnScriptEnded(this);
index 67d28cf..afde68a 100644 (file)
@@ -97,7 +97,7 @@ void CLanguageInvokerThread::OnExit()
   if (m_invoker == NULL)
     return;
 
-  m_invoker->onDone();
+  m_invoker->onExectuionDone();
   m_invocationManager->OnScriptEnded(GetId());
 }
 
@@ -106,6 +106,6 @@ void CLanguageInvokerThread::OnException()
   if (m_invoker == NULL)
     return;
 
-  m_invoker->onError();
+  m_invoker->onExectuionFailed();
   m_invocationManager->OnScriptEnded(GetId());
 }
\ No newline at end of file
index be8b9ff..d324bf9 100644 (file)
@@ -47,6 +47,7 @@
 #include "utils/CharsetConverter.h"
 #endif // defined(TARGET_WINDOWS)
 #include "utils/log.h"
+#include "utils/StringUtils.h"
 #include "utils/URIUtils.h"
 
 #ifdef TARGET_WINDOWS
@@ -73,6 +74,10 @@ extern "C"
   char* dll_getenv(const char* szKey);
 }
 
+#define PythonModulesSize sizeof(PythonModules) / sizeof(PythonModule)
+
+CCriticalSection CPythonInvoker::s_critical;
+
 static const CStdString getListOfAddonClassesAsString(XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook>& languageHook)
 {
   CStdString message;
@@ -177,7 +182,7 @@ bool CPythonInvoker::execute(const std::string &script, const std::vector<std::s
   XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp));
   languageHook->RegisterMe();
 
-  g_pythonParser.InitializeInterpreter(m_addon);
+  onInitialization();
   setState(InvokerStateInitialized);
 
   CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is %s", GetId(), m_source, m_source);
@@ -256,18 +261,7 @@ bool CPythonInvoker::execute(const std::string &script, const std::vector<std::s
         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());
-        }
+        onPythonModuleInitialization(moduleDict);
 
         Py_DECREF(f);
         setState(InvokerStateRunning);
@@ -296,12 +290,14 @@ bool CPythonInvoker::execute(const std::string &script, const std::vector<std::s
   {
     CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_source);
     setState(InvokerStateDone);
+    onSuccess();
   }
   else if (PyErr_ExceptionMatches(PyExc_SystemExit))
   {
     systemExitThrown = true;
     CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_source);
     setState(InvokerStateFailed);
+    onAbort();
   }
   else
   {
@@ -314,33 +310,7 @@ bool CPythonInvoker::execute(const std::string &script, const std::vector<std::s
       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);
-      }
-    }
+    onError();
   }
 
   // no need to do anything else because the script has already stopped
@@ -391,7 +361,7 @@ bool CPythonInvoker::execute(const std::string &script, const std::vector<std::s
   PyEval_AcquireLock();
   PyThreadState_Swap(state);
 
-  g_pythonParser.DeInitializeInterpreter();
+  onDeinitialization();
 
   // run the gc before finishing
   //
@@ -505,7 +475,7 @@ bool CPythonInvoker::stop(bool abort)
   return true;
 }
 
-void CPythonInvoker::onError()
+void CPythonInvoker::onExectuionFailed()
 {
   PyThreadState_Swap(NULL);
   PyEval_ReleaseLock();
@@ -516,7 +486,106 @@ void CPythonInvoker::onError()
   CSingleLock lock(m_critical);
   m_threadState = NULL;
 
-  ILanguageInvoker::onError();
+  ILanguageInvoker::onExectuionFailed();
+}
+
+std::map<std::string, CPythonInvoker::PythonModuleInitialization> CPythonInvoker::getModules() const
+{
+  static std::map<std::string, PythonModuleInitialization> modules;
+  return modules;
+}
+
+void CPythonInvoker::onInitialization()
+{
+  TRACE;
+  {
+    GilSafeSingleLock lock(s_critical);
+    initializeModules(getModules());
+  }
+
+  // get a possible initialization script
+  const char* runscript = getInitializationScript();
+  if (runscript!= NULL && strlen(runscript) > 0)
+  {
+    // redirecting default output to debug console
+    if (PyRun_SimpleString(runscript) == -1)
+      CLog::Log(LOGFATAL, "CPythonInvoker(%d, %s): initialize error", GetId(), m_source);
+  }
+}
+
+void CPythonInvoker::onPythonModuleInitialization(void* moduleDict)
+{
+  if (m_addon.get() == NULL || moduleDict == NULL)
+    return;
+
+  PyObject *moduleDictionary = (PyObject *)moduleDict;
+
+  PyObject *pyaddonid = PyString_FromString(m_addon->ID().c_str());
+  PyDict_SetItemString(moduleDictionary, "__xbmcaddonid__", pyaddonid);
+
+  CStdString version = ADDON::GetXbmcApiVersionDependency(m_addon);
+  PyObject *pyxbmcapiversion = PyString_FromString(version.c_str());
+  PyDict_SetItemString(moduleDictionary, "__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());
+}
+
+void CPythonInvoker::onDeinitialization()
+{
+  TRACE;
+}
+
+void CPythonInvoker::onError()
+{
+  CPyThreadState releaseGil;
+  CSingleLock gc(g_graphicsContext);
+
+  CGUIDialogKaiToast *pDlgToast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
+  if (pDlgToast != NULL)
+  {
+    CStdString desc;
+    CStdString script;
+    if (m_addon.get() != NULL)
+      script = m_addon->Name();
+    else
+    {
+      CStdString path;
+      URIUtils::Split(m_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);
+  }
+}
+
+const char* CPythonInvoker::getInitializationScript() const
+{
+  return NULL;
+}
+
+void CPythonInvoker::initializeModules(const std::map<std::string, PythonModuleInitialization> &modules)
+{
+  for (std::map<std::string, PythonModuleInitialization>::const_iterator module = modules.begin(); module != modules.end(); ++module)
+  {
+    if (!initializeModule(module->second))
+      CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): unable to initialize python module \"%s\"", module->first.c_str(), GetId(), m_source);
+  }
+}
+
+bool CPythonInvoker::initializeModule(PythonModuleInitialization module)
+{
+  if (module == NULL)
+    return false;
+
+  module();
+  return true;
 }
 
 void CPythonInvoker::addPath(const std::string path)
index af694d0..81418bc 100644 (file)
@@ -19,6 +19,7 @@
  *
  */
 
+#include <map>
 #include <string>
 
 #include "interfaces/generic/ILanguageInvoker.h"
@@ -35,21 +36,40 @@ public:
 
   virtual bool IsStopping() const { return m_stop || ILanguageInvoker::IsStopping(); }
 
+  typedef void (*PythonModuleInitialization)();
+  
 protected:
+  // implementation of ILanguageInvoker
   virtual bool execute(const std::string &script, const std::vector<std::string> &arguments);
   virtual bool stop(bool abort);
+  virtual void onExectuionFailed();
 
-  virtual void onError();
+  // custom virtual methods
+  virtual std::map<std::string, PythonModuleInitialization> getModules() const;
+  virtual const char* getInitializationScript() const;
+  virtual void onInitialization();
+  // actually a PyObject* but don't wanna draw Python.h include into the header
+  virtual void onPythonModuleInitialization(void* moduleDict);
+  virtual void onDeinitialization();
 
-private:
-  void addPath(const std::string path);
+  virtual void onSuccess() { }
+  virtual void onAbort() { }
+  virtual void onError();
 
   char *m_source;
   unsigned int  m_argc;
   char **m_argv;
+  CCriticalSection m_critical;
+
+private:
+  void initializeModules(const std::map<std::string, PythonModuleInitialization> &modules);
+  bool initializeModule(PythonModuleInitialization module);
+  void addPath(const std::string path);
+
   std::string m_pythonPath;
   void *m_threadState;
   bool m_stop;
   CEvent m_stoppedEvent;
-  CCriticalSection m_critical;
+
+  static CCriticalSection s_critical;
 };