diff options
-rw-r--r-- | xbmc/Application.cpp | 2 | ||||
-rw-r--r-- | xbmc/ApplicationMessenger.cpp | 2 | ||||
-rw-r--r-- | xbmc/addons/Addon.cpp | 20 | ||||
-rw-r--r-- | xbmc/addons/IAddon.h | 10 | ||||
-rw-r--r-- | xbmc/addons/Service.cpp | 2 | ||||
-rw-r--r-- | xbmc/filesystem/PluginDirectory.cpp | 4 | ||||
-rw-r--r-- | xbmc/interfaces/Builtins.cpp | 2 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPyThread.cpp | 14 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPyThread.h | 4 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPython.cpp | 100 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPython.h | 7 | ||||
-rw-r--r-- | xbmc/interfaces/python/xbmcmodule/PythonAddon.cpp | 81 | ||||
-rw-r--r-- | xbmc/windows/GUIMediaWindow.cpp | 2 | ||||
-rw-r--r-- | xbmc/windows/GUIWindowFileManager.cpp | 2 | ||||
-rw-r--r-- | xbmc/windows/GUIWindowWeather.cpp | 2 |
15 files changed, 197 insertions, 57 deletions
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 9412971e72..4c24c0e89a 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -4651,7 +4651,7 @@ bool CApplication::ExecuteXBMCAction(std::string actionStr) #ifdef HAS_PYTHON if (item.IsPythonScript()) { // a python script - g_pythonParser.evalFile(item.m_strPath.c_str()); + g_pythonParser.evalFile(item.m_strPath.c_str(),ADDON::AddonPtr()); } else #endif diff --git a/xbmc/ApplicationMessenger.cpp b/xbmc/ApplicationMessenger.cpp index fdf69c0c97..cb90cf8892 100644 --- a/xbmc/ApplicationMessenger.cpp +++ b/xbmc/ApplicationMessenger.cpp @@ -505,7 +505,7 @@ case TMSG_POWERDOWN: case TMSG_EXECUTE_SCRIPT: #ifdef HAS_PYTHON - g_pythonParser.evalFile(pMsg->strParam.c_str()); + g_pythonParser.evalFile(pMsg->strParam.c_str(),ADDON::AddonPtr()); #endif break; diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp index 6a202c632e..d682126b4c 100644 --- a/xbmc/addons/Addon.cpp +++ b/xbmc/addons/Addon.cpp @@ -666,5 +666,25 @@ TYPE CAddonLibrary::SetAddonType() return ADDON_UNKNOWN; } +CStdString GetXbmcApiVersionDependency(ADDON::AddonPtr addon) +{ + CStdString version("1.0"); + if (addon.get() != NULL) + { + const ADDON::ADDONDEPS &deps = addon->GetDeps(); + ADDON::ADDONDEPS::const_iterator it; + CStdString key("xbmc.python"); + it = deps.find(key); + if (!(it == deps.end())) + { + const ADDON::AddonVersion * xbmcApiVersion = &(it->second.first); + version = xbmcApiVersion->c_str(); + } + } + + return version; +} + + } /* namespace ADDON */ diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h index 31ee5b1031..bbce986717 100644 --- a/xbmc/addons/IAddon.h +++ b/xbmc/addons/IAddon.h @@ -121,5 +121,15 @@ namespace ADDON virtual bool LoadStrings() =0; virtual void ClearStrings() =0; }; + + // some utilitiy methods + + /** + * This function will extract the Addon's currently assigned xbmc.python + * API version. If addon is NULL, or there is no xbmc.python dependency defined, + * then the version is assumed to be "1.0" + */ + CStdString GetXbmcApiVersionDependency(ADDON::AddonPtr addon); + }; diff --git a/xbmc/addons/Service.cpp b/xbmc/addons/Service.cpp index 8a30a61e27..d054be1a1c 100644 --- a/xbmc/addons/Service.cpp +++ b/xbmc/addons/Service.cpp @@ -50,7 +50,7 @@ bool CService::Start() { #ifdef HAS_PYTHON case PYTHON: - ret = (g_pythonParser.evalFile(LibPath()) != -1); + ret = (g_pythonParser.evalFile(LibPath(),ADDON::AddonPtr(this)) != -1); break; #endif diff --git a/xbmc/filesystem/PluginDirectory.cpp b/xbmc/filesystem/PluginDirectory.cpp index 0dd8d2e7fc..53e025e0ae 100644 --- a/xbmc/filesystem/PluginDirectory.cpp +++ b/xbmc/filesystem/PluginDirectory.cpp @@ -117,7 +117,7 @@ bool CPluginDirectory::StartScript(const CStdString& strPath, bool retrievingDir bool success = false; #ifdef HAS_PYTHON CStdString file = m_addon->LibPath(); - if (g_pythonParser.evalFile(file, argv) >= 0) + if (g_pythonParser.evalFile(file, argv,m_addon) >= 0) { // wait for our script to finish CStdString scriptName = m_addon->Name(); success = WaitOnScriptResult(file, scriptName, retrievingDir); @@ -434,7 +434,7 @@ bool CPluginDirectory::RunScriptWithParams(const CStdString& strPath) // run the script #ifdef HAS_PYTHON CLog::Log(LOGDEBUG, "%s - calling plugin %s('%s','%s','%s')", __FUNCTION__, addon->Name().c_str(), argv[0].c_str(), argv[1].c_str(), argv[2].c_str()); - if (g_pythonParser.evalFile(addon->LibPath(), argv) >= 0) + if (g_pythonParser.evalFile(addon->LibPath(), argv,addon) >= 0) return true; else #endif diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp index e0659288e1..99738e434d 100644 --- a/xbmc/interfaces/Builtins.cpp +++ b/xbmc/interfaces/Builtins.cpp @@ -375,7 +375,7 @@ int CBuiltins::Execute(const CStdString& execString) if (CAddonMgr::Get().GetAddon(params[0], script)) scriptpath = script->LibPath(); - g_pythonParser.evalFile(scriptpath, argv); + g_pythonParser.evalFile(scriptpath, argv,script); } } #endif diff --git a/xbmc/interfaces/python/XBPyThread.cpp b/xbmc/interfaces/python/XBPyThread.cpp index 6c152c8929..d43ec3136f 100644 --- a/xbmc/interfaces/python/XBPyThread.cpp +++ b/xbmc/interfaces/python/XBPyThread.cpp @@ -36,6 +36,7 @@ #include "threads/SingleLock.h" #include "utils/URIUtils.h" #include "addons/AddonManager.h" +#include "addons/Addon.h" #include "XBPyThread.h" #include "XBPython.h" @@ -145,7 +146,7 @@ void XBPyThread::Process() // swap in my thread state PyThreadState_Swap(state); - m_pExecuter->InitializeInterpreter(); + m_pExecuter->InitializeInterpreter(addon); CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source); @@ -233,6 +234,17 @@ void XBPyThread::Process() { PyObject *f = PyString_FromString(_P(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); PyRun_FileExFlags(fp, _P(m_source).c_str(), m_Py_file_input, moduleDict, moduleDict,1,NULL); } diff --git a/xbmc/interfaces/python/XBPyThread.h b/xbmc/interfaces/python/XBPyThread.h index 2ffe1504c8..630ac27738 100644 --- a/xbmc/interfaces/python/XBPyThread.h +++ b/xbmc/interfaces/python/XBPyThread.h @@ -23,6 +23,7 @@ #define XBPYTHREAD_H_ #include "threads/Thread.h" +#include "addons/IAddon.h" class XBPython; @@ -37,6 +38,8 @@ public: bool isStopping(); void stop(); + void setAddon(ADDON::AddonPtr _addon) { addon = _addon; } + protected: XBPython *m_pExecuter; void *m_threadState; @@ -47,6 +50,7 @@ protected: unsigned int m_argc; bool m_stopping; int m_id; + ADDON::AddonPtr addon; virtual void OnStartup(); virtual void Process(); diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp index 487a30d7bb..30f2f7791c 100644 --- a/xbmc/interfaces/python/XBPython.cpp +++ b/xbmc/interfaces/python/XBPython.cpp @@ -39,6 +39,8 @@ #include "utils/TimeUtils.h" #include "Util.h" +#include "addons/Addon.h" + extern "C" HMODULE __stdcall dllLoadLibraryA(LPCSTR file); extern "C" BOOL __stdcall dllFreeLibrary(HINSTANCE hLibModule); @@ -229,48 +231,67 @@ void XBPython::UnloadExtensionLibs() m_extensions.clear(); } -void XBPython::InitializeInterpreter() +#define RUNSCRIPT_PRAMBLE \ + "" \ + "import xbmc\n" \ + "class xbmcout:\n" \ + "\tdef __init__(self, loglevel=xbmc.LOGNOTICE):\n" \ + "\t\tself.ll=loglevel\n" \ + "\tdef write(self, data):\n" \ + "\t\txbmc.output(data,self.ll)\n" \ + "\tdef close(self):\n" \ + "\t\txbmc.output('.')\n" \ + "\tdef flush(self):\n" \ + "\t\txbmc.output('.')\n" \ + "import sys\n" \ + "sys.stdout = xbmcout()\n" \ + "sys.stderr = xbmcout(xbmc.LOGERROR)\n" + +#define RUNSCRIPT_OVERRIDE_HACK \ + "" \ + "import os\n" \ + "def getcwd_xbmc():\n" \ + " import __main__\n" \ + " import warnings\n" \ + " if hasattr(__main__, \"__file__\"):\n" \ + " warnings.warn(\"os.getcwd() currently lies to you so please use addon.getAddonInfo('path') to find the script's root directory and DO NOT make relative path accesses based on the results of 'os.getcwd.' \", DeprecationWarning, stacklevel=2)\n" \ + " return os.path.dirname(__main__.__file__)\n" \ + " else:\n" \ + " return os.getcwd_original()\n" \ + "" \ + "def chdir_xbmc(dir):\n" \ + " raise RuntimeError(\"os.chdir not supported in xbmc\")\n" \ + "" \ + "os_getcwd_original = os.getcwd\n" \ + "os.getcwd = getcwd_xbmc\n" \ + "os.chdir_orignal = os.chdir\n" \ + "os.chdir = chdir_xbmc\n" \ + "" + +#define RUNSCRIPT_POSTSCRIPT \ + "print '-->Python Interpreter Initialized<--'\n" \ + "" + +#define RUNSCRIPT_BWCOMPATIBLE \ + RUNSCRIPT_PRAMBLE RUNSCRIPT_OVERRIDE_HACK RUNSCRIPT_POSTSCRIPT + +#define RUNSCRIPT_COMPLIANT \ + RUNSCRIPT_PRAMBLE RUNSCRIPT_POSTSCRIPT + +void XBPython::InitializeInterpreter(ADDON::AddonPtr addon) { InitXBMCModule(); // init xbmc modules InitPluginModule(); // init xbmcplugin modules InitGUIModule(); // init xbmcgui modules InitAddonModule(); // init xbmcaddon modules InitVFSModule(); // init xbmcvfs modules - + + CStdString addonVer = ADDON::GetXbmcApiVersionDependency(addon); + bool bwcompatMode = (addon.get() == NULL || (ADDON::AddonVersion(addonVer) <= ADDON::AddonVersion("1.0"))); + const char* runscript = bwcompatMode ? RUNSCRIPT_BWCOMPATIBLE : RUNSCRIPT_COMPLIANT; + // redirecting default output to debug console - if (PyRun_SimpleString("" - "import xbmc\n" - "class xbmcout:\n" - "\tdef write(self, data):\n" - "\t\txbmc.output(data)\n" - "\tdef close(self):\n" - "\t\txbmc.output('.')\n" - "\tdef flush(self):\n" - "\t\txbmc.output('.')\n" - "import sys\n" - "sys.stdout = xbmcout()\n" - "sys.stderr = xbmcout()\n" - "" - "import os\n" - "def getcwd_xbmc():\n" - " import __main__\n" - " import warnings\n" - " if hasattr(__main__, \"__file__\"):\n" - " warnings.warn(\"os.getcwd() is depreciated for getting addon directory use os.path.dirname(__main__.__file__)\", DeprecationWarning, stacklevel=2)\n" - " return os.path.dirname(__main__.__file__)\n" - " else:\n" - " return os.getcwd_original()\n" - "" - "def chdir_xbmc(dir):\n" - " raise RuntimeError(\"os.chdir not supported in xbmc\")\n" - "" - "os_getcwd_original = os.getcwd\n" - "os.getcwd = getcwd_xbmc\n" - "os.chdir_orignal = os.chdir\n" - "os.chdir = chdir_xbmc\n" - "" - "print '-->Python Interpreter Initialized<--'\n" - "") == -1) + if (PyRun_SimpleString(runscript) == -1) { CLog::Log(LOGFATAL, "Python Initialize Error"); } @@ -448,7 +469,7 @@ void XBPython::Process() CStdString strAutoExecPy = _P("special://profile/autoexec.py"); if ( XFILE::CFile::Exists(strAutoExecPy) ) - evalFile(strAutoExecPy); + evalFile(strAutoExecPy,ADDON::AddonPtr()); else CLog::Log(LOGDEBUG, "%s - no profile autoexec.py (%s) found, skipping", __FUNCTION__, strAutoExecPy.c_str()); } @@ -492,13 +513,13 @@ bool XBPython::StopScript(const CStdString &path) return false; } -int XBPython::evalFile(const CStdString &src) +int XBPython::evalFile(const CStdString &src, ADDON::AddonPtr addon) { std::vector<CStdString> argv; - return evalFile(src, argv); + return evalFile(src, argv, addon); } // execute script, returns -1 if script doesn't exist -int XBPython::evalFile(const CStdString &src, const std::vector<CStdString> &argv) +int XBPython::evalFile(const CStdString &src, const std::vector<CStdString> &argv, ADDON::AddonPtr addon) { CSingleExit ex(g_graphicsContext); CSingleLock lock(m_critSection); @@ -520,6 +541,7 @@ int XBPython::evalFile(const CStdString &src, const std::vector<CStdString> &arg m_nextid++; XBPyThread *pyThread = new XBPyThread(this, m_nextid); pyThread->setArgv(argv); + pyThread->setAddon(addon); pyThread->evalFile(src); PyElem inf; inf.id = m_nextid; diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h index 81b96352e6..c46039d069 100644 --- a/xbmc/interfaces/python/XBPython.h +++ b/xbmc/interfaces/python/XBPython.h @@ -24,6 +24,7 @@ #include "XBPyThread.h" #include "cores/IPlayer.h" #include "threads/CriticalSection.h" +#include "addons/IAddon.h" #include <vector> @@ -64,8 +65,8 @@ public: int ScriptsSize(); int GetPythonScriptId(int scriptPosition); - int evalFile(const CStdString &src); - int evalFile(const CStdString &src, const std::vector<CStdString> &argv); + int evalFile(const CStdString &src, ADDON::AddonPtr addon); + int evalFile(const CStdString &src, const std::vector<CStdString> &argv, ADDON::AddonPtr addon); int evalString(const CStdString &src, const std::vector<CStdString> &argv); bool isRunning(int scriptId); @@ -80,7 +81,7 @@ public: // inject xbmc stuff into the interpreter. // should be called for every new interpreter - void InitializeInterpreter(); + void InitializeInterpreter(ADDON::AddonPtr addon); // remove modules and references when interpreter done void DeInitializeInterpreter(); diff --git a/xbmc/interfaces/python/xbmcmodule/PythonAddon.cpp b/xbmc/interfaces/python/xbmcmodule/PythonAddon.cpp index 9459bb0dc7..72bcf0e74f 100644 --- a/xbmc/interfaces/python/xbmcmodule/PythonAddon.cpp +++ b/xbmc/interfaces/python/xbmcmodule/PythonAddon.cpp @@ -24,6 +24,7 @@ #include "pythreadstate.h" #include "addons/AddonManager.h" #include "addons/GUIDialogAddonSettings.h" +#include "utils/log.h" #ifndef __GNUC__ #pragma code_seg("PY_TEXT") @@ -41,6 +42,35 @@ using ADDON::CAddonMgr; namespace PYXBMC { + + static const char* getDefaultId() + { + const char* id = NULL; + + // 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* pyid = PyDict_GetItemString(global_dict, "__xbmcaddonid__"); + id = PyString_AsString(pyid); + return id; + } + + static CStdString getAddonVersion() + { + // 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* pyversion = PyDict_GetItemString(global_dict, "__xbmcapiversion__"); + CStdString version(PyString_AsString(pyversion)); + return version; + } + PyObject* Addon_New(PyTypeObject *type, PyObject *args, PyObject *kwds) { Addon *self; @@ -49,25 +79,66 @@ namespace PYXBMC if (!self) return NULL; static const char *keywords[] = { "id", NULL }; - char *id = NULL; + const char *id = NULL; // parse arguments if (!PyArg_ParseTupleAndKeywords( args, kwds, - (char*)"s", + (char*)"|s", (char**)keywords, - &id + (char**)&id )) { Py_DECREF(self); return NULL; }; + // if the id wasn't passed then get the id from + // the global dictionary + if (!id) + id = getDefaultId(); + + // if we still don't have an id then bail + if (!id) + { + PyErr_SetString(PyExc_Exception, "No valid addon id could be obtained. None was passed and the script wasn't executed in a normal xbmc manner."); + Py_DECREF(self); + return NULL; + } + + // if we still fail we MAY be able to recover. if (!CAddonMgr::Get().GetAddon(id, self->pAddon)) { - PyErr_SetString(PyExc_Exception, "Could not get AddonPtr!"); - return NULL; + // we need to check the version prior to trying a bw compatibility trick + ADDON::AddonVersion version(getAddonVersion()); + ADDON::AddonVersion allowable("1.0"); + + if (version <= allowable) + { + // try the default ... + id = getDefaultId(); + + if (!CAddonMgr::Get().GetAddon(id, self->pAddon)) + { + PyErr_SetString(PyExc_Exception, "Could not get AddonPtr!"); + Py_DECREF(self); + return NULL; + } + else + CLog::Log(LOGERROR,"Use of deprecated functionality. Please to not assume that \"os.getcwd\" will return the script directory."); + } + else + { + CStdString errorMessage ("Could not get AddonPtr given a script id of "); + errorMessage += id; + errorMessage += ". If you are trying to use 'os.getcwd' to set the path, you cannot do that in a "; + errorMessage += version.Print(); + errorMessage += " plugin."; + PyErr_SetString(PyExc_Exception, errorMessage.c_str()); + Py_DECREF(self); + return NULL; + } } return (PyObject*)self; diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp index ee50bc0242..a1a3dc5086 100644 --- a/xbmc/windows/GUIMediaWindow.cpp +++ b/xbmc/windows/GUIMediaWindow.cpp @@ -882,7 +882,7 @@ bool CGUIMediaWindow::OnClick(int iItem) { #ifdef HAS_PYTHON if (!g_pythonParser.StopScript(addon->LibPath())) - g_pythonParser.evalFile(addon->LibPath()); + g_pythonParser.evalFile(addon->LibPath(),addon); #endif return true; } diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp index 4f56a7fcea..a058be4a33 100644 --- a/xbmc/windows/GUIWindowFileManager.cpp +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -621,7 +621,7 @@ void CGUIWindowFileManager::OnStart(CFileItem *pItem) #ifdef HAS_PYTHON if (pItem->IsPythonScript()) { - g_pythonParser.evalFile(pItem->m_strPath.c_str()); + g_pythonParser.evalFile(pItem->m_strPath.c_str(),ADDON::AddonPtr()); return ; } #endif diff --git a/xbmc/windows/GUIWindowWeather.cpp b/xbmc/windows/GUIWindowWeather.cpp index 02d48586d2..402c7e0eb8 100644 --- a/xbmc/windows/GUIWindowWeather.cpp +++ b/xbmc/windows/GUIWindowWeather.cpp @@ -332,7 +332,7 @@ void CGUIWindowWeather::CallScript() argv.push_back(CWeather::GetAreaCode(g_guiSettings.GetString(strSetting))); // call our script, passing the areacode - g_pythonParser.evalFile(argv[0], argv); + g_pythonParser.evalFile(argv[0], argv,addon); CLog::Log(LOGDEBUG, "%s - Weather script called: %s (%s)", __FUNCTION__, argv[0].c_str(), argv[1].c_str()); } |