summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/python/swig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/interfaces/python/swig.cpp')
-rw-r--r--xbmc/interfaces/python/swig.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/xbmc/interfaces/python/swig.cpp b/xbmc/interfaces/python/swig.cpp
new file mode 100644
index 0000000..0c49f87
--- /dev/null
+++ b/xbmc/interfaces/python/swig.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "swig.h"
+
+#include "LanguageHook.h"
+#include "interfaces/legacy/AddonString.h"
+#include "utils/StringUtils.h"
+
+#include <string>
+
+namespace PythonBindings
+{
+ TypeInfo::TypeInfo(const std::type_info& ti) : swigType(NULL), parentType(NULL), typeIndex(ti)
+ {
+ static PyTypeObject py_type_object_header = {
+ PyVarObject_HEAD_INIT(nullptr, 0) 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+#if PY_VERSION_HEX > 0x03080000
+ 0,
+ 0,
+#endif
+#if PY_VERSION_HEX < 0x03090000
+ 0,
+#endif
+#if PY_VERSION_HEX >= 0x030C00A1
+ 0,
+#endif
+ };
+
+ static int size = (long*)&(py_type_object_header.tp_name) - (long*)&py_type_object_header;
+ memcpy(&(this->pythonType), &py_type_object_header, size);
+ }
+
+ class PyObjectDecrementor
+ {
+ PyObject* obj;
+ public:
+ inline explicit PyObjectDecrementor(PyObject* pyobj) : obj(pyobj) {}
+ inline ~PyObjectDecrementor() { Py_XDECREF(obj); }
+
+ inline PyObject* get() { return obj; }
+ };
+
+ void PyXBMCGetUnicodeString(std::string& buf, PyObject* pObject, bool coerceToString,
+ const char* argumentName, const char* methodname)
+ {
+ // It's okay for a string to be "None". In this case the buf returned
+ // will be the emptyString.
+ if (pObject == Py_None)
+ {
+ buf = XBMCAddon::emptyString;
+ return;
+ }
+
+ //! @todo UTF-8: Does python use UTF-16?
+ //! Do we need to convert from the string charset to UTF-8
+ //! for non-unicode data?
+ if (PyUnicode_Check(pObject))
+ {
+ // Python unicode objects are UCS2 or UCS4 depending on compilation
+ // options, wchar_t is 16-bit or 32-bit depending on platform.
+ // Avoid the complexity by just letting python convert the string.
+
+ buf = PyUnicode_AsUTF8(pObject);
+ return;
+ }
+
+ if (PyBytes_Check(pObject)) // If pobject is of type Bytes
+ {
+ buf = PyBytes_AsString(pObject);
+ return;
+ }
+
+ // if we got here then we need to coerce the value to a string
+ if (coerceToString)
+ {
+ PyObjectDecrementor dec(PyObject_Str(pObject));
+ PyObject* pyStrCast = dec.get();
+ if (pyStrCast)
+ {
+ PyXBMCGetUnicodeString(buf,pyStrCast,false,argumentName,methodname);
+ return;
+ }
+ }
+
+ // Object is not a unicode or a normal string.
+ buf = "";
+ throw XBMCAddon::WrongTypeException("argument \"%s\" for method \"%s\" must be unicode or str", argumentName, methodname);
+ }
+
+ // need to compare the typestring
+ bool isParameterRightType(const char* passedType, const char* expectedType, const char* methodNamespacePrefix, bool tryReverse)
+ {
+ if (strcmp(expectedType,passedType) == 0)
+ return true;
+
+ // well now things are a bit more complicated. We need to see if the passed type
+ // is a subset of the overall type
+ std::string et(expectedType);
+ bool isPointer = (et[0] == 'p' && et[1] == '.');
+ std::string baseType(et,(isPointer ? 2 : 0)); // this may contain a namespace
+
+ std::string ns(methodNamespacePrefix);
+ // cut off trailing '::'
+ if (ns.size() > 2 && ns[ns.size() - 1] == ':' && ns[ns.size() - 2] == ':')
+ ns = ns.substr(0,ns.size()-2);
+
+ bool done = false;
+ while(! done)
+ {
+ done = true;
+
+ // now we need to see if the expected type can be munged
+ // into the passed type by tacking on the namespace of
+ // of the method.
+ std::string check(isPointer ? "p." : "");
+ check += ns;
+ check += "::";
+ check += baseType;
+
+ if (strcmp(check.c_str(),passedType) == 0)
+ return true;
+
+ // see if the namespace is nested.
+ int posOfScopeOp = ns.find("::");
+ if (posOfScopeOp >= 0)
+ {
+ done = false;
+ // cur off the outermost namespace
+ ns = ns.substr(posOfScopeOp + 2);
+ }
+ }
+
+ // so far we applied the namespace to the expected type. Now lets try
+ // the reverse if we haven't already.
+ if (tryReverse)
+ return isParameterRightType(expectedType, passedType, methodNamespacePrefix, false);
+
+ return false;
+ }
+
+ PythonToCppException::PythonToCppException() : XbmcCommons::UncheckedException(" ")
+ {
+ setClassname("PythonToCppException");
+
+ std::string msg;
+ std::string type, value, traceback;
+ if (!ParsePythonException(type, value, traceback))
+ UncheckedException::SetMessage("Strange: No Python exception occurred");
+ else
+ SetMessage(type, value, traceback);
+ }
+
+ PythonToCppException::PythonToCppException(const std::string &exceptionType, const std::string &exceptionValue, const std::string &exceptionTraceback) : XbmcCommons::UncheckedException(" ")
+ {
+ setClassname("PythonToCppException");
+
+ SetMessage(exceptionType, exceptionValue, exceptionTraceback);
+ }
+
+ bool PythonToCppException::ParsePythonException(std::string &exceptionType, std::string &exceptionValue, std::string &exceptionTraceback)
+ {
+ PyObject* exc_type;
+ PyObject* exc_value;
+ PyObject* exc_traceback;
+ PyObject* pystring = NULL;
+
+ PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
+ if (exc_type == NULL && exc_value == NULL && exc_traceback == NULL)
+ return false;
+
+ // See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_NormalizeException
+ PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback);
+ if (exc_traceback != NULL) {
+ PyException_SetTraceback(exc_value, exc_traceback);
+ }
+
+ exceptionType.clear();
+ exceptionValue.clear();
+ exceptionTraceback.clear();
+
+ if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && PyUnicode_Check(pystring))
+ {
+ const char* str = PyUnicode_AsUTF8(pystring);
+ if (str != NULL)
+ exceptionType = str;
+
+ pystring = PyObject_Str(exc_value);
+ if (pystring != NULL)
+ {
+ str = PyUnicode_AsUTF8(pystring);
+ exceptionValue = str;
+ }
+
+ PyObject *tracebackModule = PyImport_ImportModule("traceback");
+ if (tracebackModule != NULL)
+ {
+ char method[] = "format_exception";
+ char format[] = "OOO";
+ PyObject *tbList = PyObject_CallMethod(tracebackModule, method, format, exc_type, exc_value == NULL ? Py_None : exc_value, exc_traceback == NULL ? Py_None : exc_traceback);
+
+ if (tbList)
+ {
+ PyObject* emptyString = PyUnicode_FromString("");
+ char method[] = "join";
+ char format[] = "O";
+ PyObject *strRetval = PyObject_CallMethod(emptyString, method, format, tbList);
+ Py_DECREF(emptyString);
+
+ if (strRetval)
+ {
+ str = PyUnicode_AsUTF8(strRetval);
+ if (str != NULL)
+ exceptionTraceback = str;
+ Py_DECREF(strRetval);
+ }
+ Py_DECREF(tbList);
+ }
+ Py_DECREF(tracebackModule);
+
+ }
+ }
+
+ Py_XDECREF(exc_type);
+ Py_XDECREF(exc_value);
+ Py_XDECREF(exc_traceback);
+ Py_XDECREF(pystring);
+
+ return true;
+ }
+
+ void PythonToCppException::SetMessage(const std::string &exceptionType, const std::string &exceptionValue, const std::string &exceptionTraceback)
+ {
+ std::string msg = "-->Python callback/script returned the following error<--\n";
+ msg += " - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS!\n";
+
+ if (!exceptionType.empty())
+ {
+ msg += StringUtils::Format("Error Type: {}\n", exceptionType);
+
+ if (!exceptionValue.empty())
+ msg += StringUtils::Format("Error Contents: {}\n", exceptionValue);
+
+ if (!exceptionTraceback.empty())
+ msg += exceptionTraceback;
+
+ msg += "-->End of Python script error report<--\n";
+ }
+ else
+ msg += "<unknown exception type>";
+
+ UncheckedException::SetMessage("%s", msg.c_str());
+ }
+
+ XBMCAddon::AddonClass* doretrieveApiInstance(const PyHolder* pythonObj, const TypeInfo* typeInfo, const char* expectedType,
+ const char* methodNamespacePrefix, const char* methodNameForErrorString)
+ {
+ if (pythonObj->magicNumber != XBMC_PYTHON_TYPE_MAGIC_NUMBER)
+ throw XBMCAddon::WrongTypeException("Non api type passed to \"%s\" in place of the expected type \"%s.\"",
+ methodNameForErrorString, expectedType);
+ if (!isParameterRightType(typeInfo->swigType,expectedType,methodNamespacePrefix))
+ {
+ // maybe it's a child class
+ if (typeInfo->parentType)
+ return doretrieveApiInstance(pythonObj, typeInfo->parentType,expectedType,
+ methodNamespacePrefix, methodNameForErrorString);
+ else
+ throw XBMCAddon::WrongTypeException("Incorrect type passed to \"%s\", was expecting a \"%s\" but received a \"%s\"",
+ methodNameForErrorString,expectedType,typeInfo->swigType);
+ }
+ return const_cast<XBMCAddon::AddonClass*>(pythonObj->pSelf);
+ }
+
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class constructor being returned from the generated code to Python
+ */
+ void prepareForReturn(XBMCAddon::AddonClass* c)
+ {
+ XBMC_TRACE;
+ if(c) {
+ c->Acquire();
+ PyThreadState* state = PyThreadState_Get();
+ XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp)->RegisterAddonClassInstance(c);
+ }
+ }
+
+ static bool handleInterpRegistrationForClean(XBMCAddon::AddonClass* c)
+ {
+ XBMC_TRACE;
+ if(c){
+ XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> lh =
+ XBMCAddon::AddonClass::Ref<XBMCAddon::AddonClass>(c->GetLanguageHook());
+
+ if (lh.isNotNull())
+ {
+ lh->UnregisterAddonClassInstance(c);
+ return true;
+ }
+ else
+ {
+ PyThreadState* state = PyThreadState_Get();
+ lh = XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp);
+ if (lh.isNotNull()) lh->UnregisterAddonClassInstance(c);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ */
+ void cleanForDealloc(XBMCAddon::AddonClass* c)
+ {
+ XBMC_TRACE;
+ if (handleInterpRegistrationForClean(c))
+ c->Release();
+ }
+
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ *
+ * There is a Catch-22 in the destruction of a Window. 'dispose' needs to be
+ * called on destruction but cannot be called from the destructor.
+ * This overrides the default cleanForDealloc to resolve that.
+ */
+ void cleanForDealloc(XBMCAddon::xbmcgui::Window* c)
+ {
+ XBMC_TRACE;
+ if (handleInterpRegistrationForClean(c))
+ {
+ c->dispose();
+ c->Release();
+ }
+ }
+
+ /**
+ * This method allows for conversion of the native api Type to the Python type.
+ *
+ * When this form of the call is used (and pytype isn't NULL) then the
+ * passed type is used in the instance. This is for classes that extend API
+ * classes in python. The type passed may not be the same type that's stored
+ * in the class metadata of the AddonClass of which 'api' is an instance,
+ * it can be a subclass in python.
+ *
+ * if pytype is NULL then the type is inferred using the class metadata
+ * stored in the AddonClass instance 'api'.
+ */
+ PyObject* makePythonInstance(XBMCAddon::AddonClass* api, PyTypeObject* pytype, bool incrementRefCount)
+ {
+ // null api types result in Py_None
+ if (!api)
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ // retrieve the TypeInfo from the api class
+ const TypeInfo* typeInfo = getTypeInfoForInstance(api);
+ PyTypeObject* typeObj = pytype == NULL ? const_cast<PyTypeObject*>(&(typeInfo->pythonType)) : pytype;
+
+ PyHolder* self = reinterpret_cast<PyHolder*>(typeObj->tp_alloc(typeObj,0));
+ if (!self) return NULL;
+ self->magicNumber = XBMC_PYTHON_TYPE_MAGIC_NUMBER;
+ self->typeInfo = typeInfo;
+ self->pSelf = api;
+ if (incrementRefCount)
+ Py_INCREF((PyObject*)self);
+ return (PyObject*)self;
+ }
+
+ std::map<std::type_index, const TypeInfo*> typeInfoLookup;
+
+ void registerAddonClassTypeInformation(const TypeInfo* classInfo)
+ {
+ typeInfoLookup[classInfo->typeIndex] = classInfo;
+ }
+
+ const TypeInfo* getTypeInfoForInstance(XBMCAddon::AddonClass* obj)
+ {
+ std::type_index ti(typeid(*obj));
+ return typeInfoLookup[ti];
+ }
+
+ int dummy_tp_init(PyObject* self, PyObject* args, PyObject* kwds)
+ {
+ return 0;
+ }
+}
+