diff options
author | Jim Carroll <thecarrolls@jiminger.com> | 2011-04-14 10:10:03 -0400 |
---|---|---|
committer | Jim Carroll <thecarrolls@jiminger.com> | 2011-04-14 10:10:03 -0400 |
commit | 17ad70e095c1a57c39fd7c85a082940f79f75397 (patch) | |
tree | 9e095da7fec21659e19256be4596a74879da549b | |
parent | 492687b4cdf0994dbe02241fa3d86fb5fcf381a6 (diff) | |
parent | 66683a8f2f221c2705b24375f9da7d54503b38c5 (diff) |
Merge remote branch 'jcarroll/default-addon-id'
* jcarroll/default-addon-id:
Removed modest attempt to introduce an industry standard to coding naming conventions. :)
This applies elupus' os.getcwd override but only for scripts that are dependent on the xbmc.python version 1.0 (or less) interface.
Cleanup the potential memory leak when the Addon constructor fails.
The Addon constructor will now check the version of the xbmc python api that the script is dependent upon and will only apply the backward compatibility hack if the version is 1.0 or less. Otherwise it will assume the script has been updated for eden and will not try to get the id from the addon.xml file as a recovery from a failed Addon instantiation.
This will allow scripts to run in a backward compatible way without requiring modifications to the core python behavior.
Added the ability for a python script to instantiate an Addon instance without the need to pass an addon id. The default behavior is now to obtain the addon id from the Addon::ID method which is passed through python via the global dictionary. The ability to provide an id still exists if the Addon being instantiated is different from the one that's running.
-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()); } |