summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/python/PythonSwig.cpp.template
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/interfaces/python/PythonSwig.cpp.template
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/interfaces/python/PythonSwig.cpp.template')
-rw-r--r--xbmc/interfaces/python/PythonSwig.cpp.template942
1 files changed, 942 insertions, 0 deletions
diff --git a/xbmc/interfaces/python/PythonSwig.cpp.template b/xbmc/interfaces/python/PythonSwig.cpp.template
new file mode 100644
index 0000000..24756ea
--- /dev/null
+++ b/xbmc/interfaces/python/PythonSwig.cpp.template
@@ -0,0 +1,942 @@
+<%
+/*
+ * 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.
+ */
+%>
+<%
+import Helper
+import SwigTypeParser
+import PythonTools
+
+import groovy.xml.XmlUtil
+import groovy.text.SimpleTemplateEngine
+import java.util.regex.Pattern
+
+/**
+ * All of the method nodes and all of the class nodes are used several
+ * times over, so they are pulled out once here.
+ */
+
+// ---------------------------------------------------------
+// initialize the SwigTypeParser with the module's typetables
+module.findAll( { it.name() == 'typetab' } ).each { SwigTypeParser.appendTypeTable(it) }
+// ---------------------------------------------------------
+
+// ---------------------------------------------------------
+// Flatten out all of the method/function nodes, whether inside
+// classes or not, into 'methods'
+List methods = module.depthFirst().findAll { it.name() == 'function' || it.name() == 'constructor' || it.name() == 'destructor' }
+// ---------------------------------------------------------
+
+// ---------------------------------------------------------
+// Flatten out all of the class nodes into 'classes'
+List classes = module.depthFirst().findAll { it.name() == 'class' }
+// ---------------------------------------------------------
+
+// ---------------------------------------------------------
+// Initialize the Helper with the type conversions
+Helper.setup(this,classes,
+ /**
+ * This is meant to contain mini-templates for converting the return type
+ * of the native call to be returned to the python caller.
+ */
+ [ 'void' : 'Py_INCREF(Py_None);\n ${result} = Py_None;',
+ 'long': '${result} = PyLong_FromLong(${api});',
+ 'unsigned long': '${result} = PyLong_FromLong(${api});',
+ 'bool': '${result} = ${api} ? Py_True : Py_False; Py_INCREF(${result});',
+ 'long long': '${result} = Py_BuildValue("L", ${api});',
+ 'int': '${result} = Py_BuildValue("i", ${api});',
+ 'unsigned int': '${result} = Py_BuildValue("I", ${api});',
+ 'double': '${result} = PyFloat_FromDouble(${api});',
+ 'float': '${result} = Py_BuildValue("f", static_cast<double>(${api}));',
+ 'std::string' : new File('typemaps/python.string.outtm'),
+ 'p.q(const).char' : '${result} = PyUnicode_FromString(${api});',
+ (Pattern.compile('''(p.){0,1}XbmcCommons::Buffer''')) : new File('typemaps/python.buffer.outtm'),
+ (Pattern.compile('''std::shared_ptr<\\(.*\\)>''')) : new File('typemaps/python.smart_ptr.outtm'),
+ (Pattern.compile('''std::unique_ptr<\\(.*\\)>''')) : new File('typemaps/python.smart_ptr.outtm'),
+ (Pattern.compile('''(p.){0,1}std::vector<\\(.*\\)>''')) : new File('typemaps/python.vector.outtm'),
+ (Pattern.compile('''(p.){0,1}Tuple<\\(.*\\)>''')) : new File('typemaps/python.Tuple.outtm'),
+ (Pattern.compile('''(p.){0,1}Alternative<\\(.*\\)>''')) : new File('typemaps/python.Alternative.outtm')
+ ], '${result} = makePythonInstance(${api},true);',
+ /**
+ * This is meant to contain mini-templates for converting the parameter types
+ * of the native call to be converted from the python types provided by the caller.
+ *
+ * Note: if the type can be handled by PythonTools.ltypeToFormatChar then it wont
+ * appear here as it gets converted directly within the PyArg_ParseTupleAndKeywords
+ * call.
+ */
+ [
+ 'std::string' : 'if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},false,"${api}","${method.@name}");',
+ (Pattern.compile('''(p.){0,1}std::vector<\\(.*\\)>''')) : new File('typemaps/python.vector.intm'),
+ (Pattern.compile('''(p.){0,1}Tuple(3){0,1}<\\(.*\\)>''')) : new File('typemaps/python.Tuple.intm'),
+ (Pattern.compile('''(p.){0,1}Alternative<\\(.*\\)>''')) : new File('typemaps/python.Alternative.intm'),
+ (Pattern.compile('''(r.){0,1}XbmcCommons::Buffer''')) : new File('typemaps/python.buffer.intm'),
+ (Pattern.compile('''(p.){0,1}std::map<\\(.*\\)>''')) : new File('typemaps/python.map.intm'),
+ (Pattern.compile('''(r.){0,1}XBMCAddon::Dictionary<\\(.*\\)>''')) : new File('typemaps/python.dict.intm'),
+ (Pattern.compile('''p.void''')) : '${api} = (void*)${slarg};',
+ 'bool' : '${api} = (PyLong_AsLong(${slarg}) == 0L ? false : true);',
+ 'long' : '${api} = PyLong_AsLong(${slarg});',
+ 'unsigned long' : '${api} = PyLong_AsUnsignedLong(${slarg});',
+ 'long long' : '${api} = PyLong_AsLongLong(${slarg});',
+ 'unsigned long long' : '${api} = PyLong_AsUnsignedLongLong(${slarg});',
+ 'int' : '${api} = (int)PyLong_AsLong(${slarg});',
+ 'double' : '${api} = PyFloat_AsDouble(${slarg});',
+ 'float' : '${api} = (float)PyFloat_AsDouble(${slarg});',
+ 'XBMCAddon::StringOrInt' : 'if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},PyLong_Check(${slarg}) || PyFloat_Check(${slarg}),"${api}","${method.@name}");'
+ ], '${api} = (${swigTypeParser.SwigType_str(ltype)})retrieveApiInstance(${slarg},"${ltype}","${helper.findNamespace(method)}","${helper.callingName(method)}");')
+// ---------------------------------------------------------
+
+/*******************************************************************************/
+/**
+ * The doMethod will actually write out the CPython method call for
+ * the method/function represented by the provided Node ('method').
+ */
+void doMethod(Node method, MethodType methodType)
+{
+ boolean isOperator = method.@name.startsWith("operator ")
+ boolean doAsMappingIndex = false
+ boolean doAsCallable = false
+
+ if (isOperator)
+ {
+ if("[]" == method.@name.substring(9))
+ doAsMappingIndex = true
+ else if("()" == method.@name.substring(9))
+ doAsCallable = true
+ else
+ return;
+ }
+
+ boolean constructor = methodType == MethodType.constructor
+
+ // if we're a constructor, but we're private, then we're outta here
+ if (constructor && method.@access != null && method.@access != "public")
+ return
+
+ boolean destructor = methodType == MethodType.destructor
+ List params = method?.parm
+ int numParams = params?.size()
+ String clazz = Helper.findFullClassName(method)
+ String returns = constructor ? 'p.' + clazz : (destructor ? 'void' : Helper.getReturnSwigType(method))
+ Node classnode = Helper.findClassNode(method)
+ String classNameAsVariable = clazz == null ? null : PythonTools.getClassNameAsVariable(classnode)
+ boolean useKeywordParsing = !('true' == classnode?.@feature_python_nokwds || 'true' == method?.@feature_python_nokwds)
+
+ // do the docs
+ if (!constructor && !destructor)
+ {
+ if (Helper.hasDoc(method))
+ {
+%>
+ PyDoc_STRVAR(${PythonTools.getPyMethodName(method,methodType)}__doc__,
+ ${PythonTools.makeDocString(method.doc[0])});
+<% }
+ }
+%>
+ static <% if(methodType == MethodType.destructor) { %>void<% } else { %>PyObject*<% } %> ${module.@name}_${PythonTools.getPyMethodName(method,methodType)} (<%= ((clazz == null) ? "PyObject" :
+ (constructor ? "PyTypeObject" : 'PyHolder')) %>* ${constructor ? 'pytype' : 'self'} <%
+ if (doAsMappingIndex) { %>, PyObject* py${params[0].@name}<% }
+ else if (methodType != MethodType.destructor) { %> , PyObject *args, PyObject *kwds <%} %> )
+ {
+ XBMC_TRACE;
+<% if (numParams > 0)
+ {
+ if (useKeywordParsing && !doAsMappingIndex)
+ { %>
+ static const char *keywords[] = {<%
+ params.each { %>
+ "${it.@name}",<% } %>
+ NULL};
+<% }
+ params.each {
+%>
+ ${SwigTypeParser.SwigType_str(SwigTypeParser.convertTypeToLTypeForParam(it.@type))} ${it.@name} ${it.@value != null ? ' = ' + it.@value : SwigTypeParser.SwigType_ispointer(it.@type) ? ' = nullptr' : ''};<%
+ if (!PythonTools.parameterCanBeUsedDirectly(it) && !doAsMappingIndex)
+ { %>
+ PyObject* py${it.@name} = NULL;<%
+ }
+ }
+ if (!doAsMappingIndex)
+ { %>
+ if (!${useKeywordParsing ? 'PyArg_ParseTupleAndKeywords' : 'PyArg_ParseTuple'}(
+ args,
+ <% if (useKeywordParsing) { %>kwds,<% } %>
+ "<%= PythonTools.makeFormatStringFromParameters(method) %>",
+ <% if (useKeywordParsing) { %>const_cast<char**>(keywords),<% } %><% params.eachWithIndex { param,i -> %>
+ &${PythonTools.parameterCanBeUsedDirectly(param) ? '' : 'py'}${param.@name}${i < params.size() - 1 ? "," : ""}<% } %>
+ ))
+ {
+ return NULL;
+ }
+
+<% }
+ }
+ // now actually invoke the method
+ if (returns != "void") { %> ${SwigTypeParser.SwigType_str(returns)} apiResult;<% }
+%>
+ try
+ {
+<%
+ // now do the input conversion if any are necessary
+ params.findAll({ !PythonTools.parameterCanBeUsedDirectly(it) || doAsMappingIndex }).each { %> ${Helper.getInConversion(it.@type, it.@name, 'py' + it.@name, method)} <% println() }
+%>
+<%
+ // check to see if this method is a call to a virtual function on a director class.
+ boolean isDirectorCall = Helper.isDirector(method)
+ if (isDirectorCall)
+ {
+%> // This is a director call coming from python so it explicitly calls the base class method.
+<%
+ }
+ // now do the method call itself
+ if (!destructor) {
+ if (constructor || !clazz) { %> XBMCAddon::SetLanguageHookGuard slhg(XBMCAddon::Python::PythonLanguageHook::GetIfExists(PyThreadState_Get()->interp).get());<% println() }
+%> <%
+ if (returns != "void") { %>apiResult = <% }
+ if (clazz && !constructor) {
+ %>((${clazz}*)retrieveApiInstance((PyObject*)self,&Ty${classNameAsVariable}_Type,"${Helper.callingName(method)}","${clazz}"))-> <%
+ }
+ if (constructor && classnode.@feature_director) {
+ %>(&(Ty${classNameAsVariable}_Type.pythonType) != pytype) ? new ${classNameAsVariable}_Director(<% params.eachWithIndex { param, i -> %> ${param.@name}${i < params.size() - 1 ? "," : ""} <% } %>) : <% }
+
+ // Here is the actual call ... if this is a Director we need to do an upCall (from Python)
+ if (isDirectorCall){ %>${clazz}::<% }
+ %>${Helper.callingName(method)}( <% params.eachWithIndex { param, i -> %> ${param.@name}${i < params.size() - 1 ? "," : ""} <% } %> );
+<%
+ if (constructor) { %> prepareForReturn(apiResult);<% }
+ } // close the 'if method is not a destructor'
+ else { // it is a destructor
+%>
+ ${clazz}* theObj = (${clazz}*)retrieveApiInstance((PyObject*)self,&Ty${classNameAsVariable}_Type,"~${Helper.callingName(method)}","${clazz}");
+ cleanForDealloc(theObj);
+<%
+ }
+%>
+ }
+ catch (const XBMCAddon::WrongTypeException& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_TypeError, e.GetExMessage()); <%
+ if (!destructor) { %>
+ return NULL; <%
+ } %>
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_RuntimeError, e.GetExMessage()); <%
+ if (!destructor) { %>
+ return NULL; <%
+ } %>
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: Unknown exception thrown from the call \"${Helper.callingName(method)}\"");
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception thrown from the call \"${Helper.callingName(method)}\""); <%
+ if (!destructor) { %>
+ return NULL; <%
+ } %>
+ }
+<%
+ if (!destructor) { %>
+ PyObject* result = Py_None;
+
+ // transform the result
+<%
+ if (constructor) {
+ %> result = makePythonInstance(apiResult,pytype,false);<%
+ }
+ else {
+%> ${Helper.getOutConversion(returns,'result',method)}<%
+ }
+ if (constructor && method.@feature_director) { %>
+ if (&(Ty${classNameAsVariable}_Type.pythonType) != pytype)
+ ((${classNameAsVariable}_Director*)apiResult)->setPyObjectForDirector(result);<%
+ }
+ %>
+
+ return result; <% }
+ else { %>
+ (((PyObject*)(self))->ob_type)->tp_free((PyObject*)self);
+ <%
+ }
+ %>
+ } <%
+}
+/*******************************************************************************/
+
+/**
+ * This method writes out the instance of a TypeInfo (which includes
+ * The PyTypeObject as a member) for the class node provided.
+ *
+ * If classNameAsVariable is not null then the class name as a
+ * variable will be appended to it.
+ */
+void doClassTypeInfo(Node clazz, List classNameAsVariables = null)
+{
+ String classNameAsVariable = PythonTools.getClassNameAsVariable(clazz)
+ String fullClassName = Helper.findFullClassName(clazz)
+ classNameAsVariables?.add(classNameAsVariable)
+%>
+ //=========================================================================
+ // These variables will hold the Python Type information for ${fullClassName}
+ TypeInfo Ty${classNameAsVariable}_Type(typeid(${fullClassName}));<%
+%>
+ //=========================================================================
+<%
+}
+
+/**
+ * This method will take the name of an API class from another module and
+ * create an external reference to its TypeInfo instance.
+ */
+void doExternClassTypeInfo(String knownType)
+{
+ String classNameAsVariable = knownType.replaceAll('::','_')
+%>
+ //=========================================================================
+ // These variables define the type ${knownType} from another module
+ extern TypeInfo Ty${classNameAsVariable}_Type;
+ //=========================================================================
+<%
+}
+
+/*******************************************************************************/
+/**
+ * This method takes the class node and outputs all of the python meta-data
+ * and class oddities (like comparators, as_mapping, etc.). These include:
+ *
+ * 1) comparator *_cmp python method as long as there's an operator==, an
+ * operator>, AND an operator<.
+ * 2) it will create a python "as_mapping" method as long as there's both
+ * an operator[], AND a .size() method on the class.
+ * 3) it will handle the explicitly defined rich compare (_rcmp) if the
+ * feature is included in the .i file using %feature("python:rcmp")
+ * 4) The array of PyMethodDefs for the class definition
+ * 5) It will handle public fields as if the were python properties by:
+ * a) Creating a get/set_member if there are read/write properties.
+ * b) Creating only a get if there are only read-only properties.
+ * 6) It will write the init[Classname] method for the class which will
+ * initialize the TypeInfo and PyTypeObject structs.
+ *
+ * If initTypeCalls is not null then the method name for the generated init
+ * method (see #6 above) will be appended to it.
+ */
+void doClassMethodInfo(Node clazz, List initTypeCalls)
+{
+ String classNameAsVariable = PythonTools.getClassNameAsVariable(clazz)
+ String fullClassName = Helper.findFullClassName(clazz)
+ String initTypeCall = "initPy${classNameAsVariable}_Type"
+ initTypeCalls?.add(initTypeCall)
+
+ // see if we have any valid (or invalid) operators
+ boolean doComparator = false
+ boolean doAsMapping = false
+ boolean hasEquivalenceOp = false
+ boolean hasLtOp = false
+ boolean hasGtOp = false
+ Node indexOp = null
+ Node callableOp = null
+ Node sizeNode = null
+
+ List normalMethods = clazz.function.findAll { !it.@name.startsWith("operator ") }
+ List operators = clazz.function.findAll { it.@name.startsWith("operator ") }
+ List properties = clazz.variable.findAll { it.@access != null && it.@access == "public" }
+ List properties_set = properties.findAll { it.@feature_immutable == null || it.@feature_immutable == 0 }
+
+ operators.each {
+ // we have an operator. The only one we can handle is ==
+ if (it.@name.substring(9).startsWith("=="))
+ hasEquivalenceOp = true
+ else if (it.@name.substring(9) == "<")
+ hasLtOp = true
+ else if (it.@name.substring(9) == ">")
+ hasGtOp = true
+ else if (it.@name.substring(9) == "[]")
+ indexOp = it
+ else if (it.@name.substring(9) == "()")
+ callableOp = it
+ else
+ System.err.println ("Warning: class ${fullClassName} has an operator \"${it.@name}\" that is being ignored.");
+ }
+
+ if (hasGtOp || hasLtOp || hasEquivalenceOp)
+ {
+ if (!(hasLtOp && hasGtOp && hasEquivalenceOp))
+ System.err.println ("Warning: class ${fullClassName} has an inconsistent operator set. To get a comparator you must implement all 3 operators >,<,==.")
+ else
+ doComparator = true
+ }
+
+ if (indexOp)
+ {
+ sizeNode = clazz.function.find { it.@name == "size" }
+ if (sizeNode)
+ doAsMapping = true
+ else
+ System.err.println ("Warning: class ${fullClassName} has an inconsistent operator set. To get a as_mapping you must implement 'size' as well as operator[]")
+ }
+
+ if (doAsMapping)
+ {
+%>
+ static Py_ssize_t ${module.@name}_${classNameAsVariable}_size_(PyObject* self)
+ {
+ return (Py_ssize_t)((${fullClassName}*)retrieveApiInstance(self,&Ty${classNameAsVariable}_Type,"${Helper.callingName(indexOp)}","${fullClassName}"))-> size();
+ }
+
+ //=========================================================================
+ // tp_as_mapping struct for ${fullClassName}
+ //=========================================================================
+ PyMappingMethods ${module.@name}_${classNameAsVariable}_as_mapping = {
+ ${module.@name}_${classNameAsVariable}_size_, /* inquiry mp_length; __len__ */
+ (PyCFunction)${module.@name}_${PythonTools.getPyMethodName(indexOp,MethodType.method)}, /* binaryfunc mp_subscript __getitem__ */
+ 0, /* objargproc mp_ass_subscript; __setitem__ */
+ };
+<%
+ }
+
+ if (clazz.@feature_python_rcmp)
+ { %>
+ static PyObject* ${module.@name}_${classNameAsVariable}_rcmp(PyObject* obj1, PyObject *obj2, int method)
+ ${Helper.unescape(clazz.@feature_python_rcmp)}
+<%
+ }
+%>
+ //=========================================================================
+ // This section contains the initialization for the
+ // Python extension for the Api class ${fullClassName}
+ //=========================================================================
+ // All of the methods on this class
+ static PyMethodDef ${classNameAsVariable}_methods[] = { <%
+ normalMethods.each { %>
+ {"${it.@sym_name}", (PyCFunction)${module.@name}_${PythonTools.getPyMethodName(it,MethodType.method)}, METH_VARARGS|METH_KEYWORDS, ${Helper.hasDoc(it) ? PythonTools.getPyMethodName(it,MethodType.method) + '__doc__' : 'NULL'} }, <% }
+
+ // now do all of the explicit feature:python:method's that may be in this class
+ List tmpl = []
+ tmpl.addAll(clazz.attributes().keySet())
+ List newMethodKeys = tmpl.findAll { it.startsWith('feature_python_method_') }
+ newMethodKeys.each { key ->
+ String featureEntry = clazz.attribute(key)
+ String methodName = key.substring('feature_python_method_'.length()) %>
+ {"${methodName}", (PyCFunction)${module.@name}_${PythonTools.getClassNameAsVariable(clazz)}_${methodName}, METH_VARARGS|METH_KEYWORDS, NULL},
+<%
+ }
+%>
+ {NULL, NULL, 0, NULL}
+ };
+
+<%
+ if (properties.size() > 0) {
+%> static PyObject* ${classNameAsVariable}_getMember(PyHolder *self, void *name)
+ {
+ if (self == NULL)
+ return NULL;
+<%
+ String clazzName = Helper.findFullClassName(properties[0])
+%>
+ try
+ {
+ ${clazzName}* theObj = (${clazzName}*)retrieveApiInstance((PyObject*)self, &Ty${classNameAsVariable}_Type, "${classNameAsVariable}_getMember()", "${clazzName}");
+
+ PyObject* result = NULL;
+ <%
+ properties.each {
+ String returns = Helper.getPropertyReturnSwigType(it);
+%> if (strcmp((char*)name, "${it.@sym_name}") == 0)
+ {
+ ${SwigTypeParser.SwigType_lstr(returns)} apiResult = theObj->${it.@sym_name};
+ ${Helper.getOutConversion(returns, 'result', it)}
+ }
+ else<%
+ } %>
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return result;
+ }
+ catch (const XBMCAddon::WrongTypeException& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_TypeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_RuntimeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: Unknown exception thrown from the call \"${classNameAsVariable}_getMember()\"");
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception thrown from the call \"${classNameAsVariable}_getMember()\"");
+ return NULL;
+ }
+
+ return NULL;
+ }
+
+<%
+ if (properties_set.size() > 0) {
+%> int ${classNameAsVariable}_setMember(PyHolder *self, PyObject *value, void *name)
+ {
+ if (self == NULL)
+ return -1;
+
+ ${clazzName}* theObj = NULL;
+ try
+ {
+ theObj = (${clazzName}*)retrieveApiInstance((PyObject*)self, &Ty${classNameAsVariable}_Type, "${classNameAsVariable}_getMember()", "${clazzName}");
+ }
+ catch (const XBMCAddon::WrongTypeException& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_TypeError, e.GetExMessage());
+ return -1;
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_RuntimeError, e.GetExMessage());
+ return -1;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: Unknown exception thrown from the call \"${classNameAsVariable}_getMember()\"");
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception thrown from the call \"${classNameAsVariable}_getMember()\"");
+ return -1;
+ }
+
+<%
+ properties_set.each {
+ String returns = Helper.getPropertyReturnSwigType(it);
+%> if (strcmp((char*)name, "${it.@sym_name}") == 0)
+ {
+ ${SwigTypeParser.SwigType_lstr(returns)} tmp;
+ ${Helper.getInConversion(returns, 'tmp', 'value', it)}
+ if (PyErr_Occurred())
+ throw PythonBindings::PythonToCppException();
+
+ theObj->${it.@sym_name} = tmp;
+ }
+ else<%
+ } %>
+ return -1;
+
+ return 0;
+ } <%
+ }
+%>
+
+ // All of the methods on this class
+ static PyGetSetDef ${classNameAsVariable}_getsets[] = { <%
+ properties.each { %>
+ {(char*)"${it.@sym_name}", (getter)${classNameAsVariable}_getMember, ${(it.@feature_immutable == null || it.@feature_immutable == 0) ? '(setter)' + classNameAsVariable + '_setMember' : 'NULL'}, (char*)${Helper.hasDoc(it) ? PythonTools.makeDocString(it.doc[0]) : 'NULL'}, (char*)"${it.@sym_name}" }, <% }
+%>
+ {NULL}
+ };
+<%
+ }
+
+ if ((clazz.@feature_iterator && clazz.@feature_iterator != '') ||
+ (clazz.@feature_iterable && clazz.@feature_iterable != '')) { %>
+ static PyObject* ${module.@name}_${classNameAsVariable}_iter(PyObject* self)
+ { <%
+ if (clazz.@feature_iterator) { %>
+ return self; <%
+ }
+ else { %>
+ PyObject* result = NULL;
+ try
+ {
+ ${clazz.@feature_iterable}* apiResult = ((${fullClassName}*)retrieveApiInstance(self,&Ty${classNameAsVariable}_Type,"${module.@name}_${classNameAsVariable}_iternext","${fullClassName}"))->begin();
+
+ ${Helper.getOutConversion('p.' + clazz.@feature_iterable,'result',clazz)}
+ }
+ catch (const XBMCAddon::WrongTypeException& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_TypeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_RuntimeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: Unknown exception thrown from the call \"${module.@name}_${classNameAsVariable}_iternext\"");
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception thrown from the call \"${module.@name}_${classNameAsVariable}_iternext\"");
+ return NULL;
+ }
+
+ return result; <%
+ } %>
+ }
+<%
+
+ if (clazz.@feature_iterator) { %>
+ static PyObject* ${module.@name}_${classNameAsVariable}_iternext(PyObject* self)
+ {
+ PyObject* result = NULL;
+ try
+ {
+ ${fullClassName}* iter = (${fullClassName}*)retrieveApiInstance(self,&Ty${classNameAsVariable}_Type,"${module.@name}_${classNameAsVariable}_iternext","${fullClassName}");
+
+ // check if we have reached the end
+ if (!iter->end())
+ {
+ ++(*iter);
+
+ ${clazz.@feature_iterator} apiResult = **iter;
+ ${Helper.getOutConversion(clazz.@feature_iterator,'result',clazz)}
+ }
+ }
+ catch (const XBMCAddon::WrongTypeException& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_TypeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (const XbmcCommons::Exception& e)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: {}",e.GetExMessage());
+ PyErr_SetString(PyExc_RuntimeError, e.GetExMessage());
+ return NULL;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR,"EXCEPTION: Unknown exception thrown from the call \"${module.@name}_${classNameAsVariable}_iternext\"");
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception thrown from the call \"${module.@name}_${classNameAsVariable}_iternext\"");
+ return NULL;
+ }
+
+ return result;
+ }
+<%
+ }
+ }
+%>
+
+ // This method initializes the above mentioned Python Type structure
+ static void ${initTypeCall}()
+ {
+<%
+ if (Helper.hasDoc(clazz))
+ {
+%>
+ PyDoc_STRVAR(${classNameAsVariable}__doc__,
+ ${PythonTools.makeDocString(clazz.doc[0])}
+ );
+<% } %>
+
+ PyTypeObject& pythonType = Ty${classNameAsVariable}_Type.pythonType;
+ pythonType.tp_name = "${module.@name}.${clazz.@sym_name}";
+ pythonType.tp_basicsize = sizeof(PyHolder);
+ pythonType.tp_dealloc = (destructor)${module.@name}_${classNameAsVariable}_Dealloc; <%
+
+ if (clazz.@feature_python_rcmp) { %>
+ pythonType.tp_richcompare=(richcmpfunc)${module.@name}_${classNameAsVariable}_rcmp;<%
+ } %>
+
+ pythonType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+
+ pythonType.tp_doc = ${Helper.hasDoc(clazz) ? (classNameAsVariable + '__doc__') : 'NULL' };
+ pythonType.tp_methods = ${classNameAsVariable}_methods; <%
+ if (properties.size() > 0) { %>
+ pythonType.tp_getset = ${classNameAsVariable}_getsets;
+<%
+ }
+ if (callableOp) { %>
+ pythonType.tp_call = (ternaryfunc)${module.@name}_${PythonTools.getPyMethodName(callableOp,MethodType.method)};
+<%
+ }
+ if (doAsMapping) { %>
+ pythonType.tp_as_mapping = &${module.@name}_${classNameAsVariable}_as_mapping;
+<%
+ }
+
+ if (clazz.@feature_iterator) { %>
+ pythonType.tp_iter = (getiterfunc)${module.@name}_${classNameAsVariable}_iter;
+ pythonType.tp_iternext = (iternextfunc)${module.@name}_${classNameAsVariable}_iternext;
+<%
+ }
+ else if (clazz.@feature_iterable && clazz.@feature_iterable != '') { %>
+ pythonType.tp_iter = (getiterfunc)${module.@name}_${classNameAsVariable}_iter;
+<%
+ }
+
+ Node baseclass = PythonTools.findValidBaseClass(clazz, module)
+%>
+
+ pythonType.tp_base = ${baseclass ? ('&(Ty' + PythonTools.getClassNameAsVariable(baseclass) + '_Type.pythonType)') : "NULL"};
+ pythonType.tp_new = <% !Helper.hasDefinedConstructor(clazz) || Helper.hasHiddenConstructor(clazz) ? print('NULL') : print("${module.@name}_${classNameAsVariable}_New") %>;
+ pythonType.tp_init = dummy_tp_init;
+
+ Ty${classNameAsVariable}_Type.swigType="p.${fullClassName}";<%
+ if (baseclass) { %>
+ Ty${classNameAsVariable}_Type.parentType=&Ty${PythonTools.getClassNameAsVariable(baseclass)}_Type;
+<%}
+
+ if (!Helper.hasHiddenConstructor(clazz)) { %>
+ registerAddonClassTypeInformation(&Ty${classNameAsVariable}_Type);
+<%} %>
+ }
+ //=========================================================================
+<%
+}
+/*******************************************************************************/
+
+
+List getAllVirtualMethods(Node clazz)
+{
+ List ret = []
+ ret.addAll(clazz.findAll({ it.name() == 'function' && it.@storage && it.@storage == 'virtual' }))
+ if (clazz.baselist) {
+ if (clazz.baselist[0].base) clazz.baselist[0].base.each {
+ Node baseclassnode = Helper.findClassNodeByName(module,it.@name,clazz)
+ if (baseclassnode && baseclassnode.@feature_director) ret.addAll(getAllVirtualMethods(baseclassnode))
+ }
+ }
+ return ret;
+}
+
+%>
+/*
+ * 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.
+ */
+
+// ************************************************************************
+// This file was generated by xbmc compile process. DO NOT EDIT!!
+// It was created by running the code generator on the spec file for
+// the module "${module.@name}" on the template file PythonSwig.template.cpp
+// ************************************************************************
+
+<%
+Helper.getInsertNodes(module, 'begin').each { %>${Helper.unescape(it)}<% }
+%>
+
+#include <Python.h>
+#include <string>
+#include "CompileInfo.h"
+#include "interfaces/python/LanguageHook.h"
+#include "interfaces/python/swig.h"
+#include "interfaces/python/PyContext.h"
+
+<%
+Helper.getInsertNodes(module, 'header').each { %>${Helper.unescape(it)}<% }
+%>
+
+namespace PythonBindings
+{
+<%
+ // initTypeCalls is the
+ List initTypeCalls = []
+ List classNameAsVariables = []
+
+ classes.each { clazz -> doClassTypeInfo(clazz, classNameAsVariables) }
+
+ // make sure known api types are declared as externs
+
+ // first, find all of the declared known api types
+ Set<String> knownApiTypes = new HashSet<String>()
+ module.depthFirst().each
+ {
+ String attr = it.attribute('feature_knownapitypes')
+ if (attr != null)
+ {
+ attr.trim().split(',').each { knownApiTypes.add(it) }
+ }
+ }
+
+ // now declare an extern for each one
+ knownApiTypes.each { doExternClassTypeInfo(it) }
+
+%>
+
+<%
+//=========================================================================
+// Do the directors. For every class that can be extended in python, we
+// need to create a Director instance with bridging calls. This chunk of
+// code will generate those classes.
+ classes.findAll({ it.@feature_director != null }).each { clazz ->
+ // find the constructor for this class
+ constructor = clazz.constructor[0]
+%>
+ //=========================================================================
+ // This class is the Director for ${Helper.findFullClassName(clazz)}.
+ // It provides the "reverse bridge" from C++ to Python to support
+ // cross-language polymorphism.
+ //=========================================================================
+ class ${PythonTools.getClassNameAsVariable(clazz)}_Director : public Director, public ${clazz.@name}
+ {
+ public:
+<%
+ if (constructor)
+ {%>
+ inline ${PythonTools.getClassNameAsVariable(clazz)}_Director(<%
+ List params = constructor?.parm
+ params.eachWithIndex { param, i -> %>${SwigTypeParser.SwigType_str(param.@type)} ${param.@name}${i < params.size() - 1 ? "," : ""} <% }
+ %>) : ${Helper.findFullClassName(constructor)}(<%
+ params.eachWithIndex { param, i -> %> ${param.@name}${i < params.size() - 1 ? "," : ""} <% } %>) { } <%
+ }
+%>
+<%
+ getAllVirtualMethods(clazz).each
+ { %>
+ virtual ${SwigTypeParser.SwigType_str(Helper.getReturnSwigType(it))} ${Helper.callingName(it)}( <%
+ List params = it?.parm
+ String paramFormatStr = ''
+ params.each { paramFormatStr += 'O' }
+ params.eachWithIndex { param, i -> %> ${SwigTypeParser.SwigType_str(param.@type)} ${param.@name}${i < params.size() - 1 ? "," : ""} <% }
+ %> )
+ { <%
+ params.each
+ { param ->
+ %>
+ PyObject* py${param.@name} = NULL;
+ ${Helper.getOutConversion(param.@type,'result',it,['result' : 'py' + param.@name, 'api' : param.@name])}<%
+ }
+%>
+ XBMCAddon::Python::PyContext pyContext;
+ PyObject_CallMethod(self,"${Helper.callingName(it)}","(${paramFormatStr})"<%
+ params.each {
+ %>, py${it.@name} <%
+ }
+ %>);
+ if (PyErr_Occurred())
+ throw PythonBindings::PythonToCppException();
+ }
+<% }
+
+%>
+ };
+<%
+ }
+//=========================================================================
+
+ // types used as method parameter or return values need to be declared
+ // as extern if they are unknown types.
+ methods.each { if (it.name() != 'destructor') { doMethod(it, (it.name() == 'constructor' ? MethodType.constructor : MethodType.method)); println(); } }
+ classes.each { clazz -> doMethod(clazz, MethodType.destructor) }
+
+ // now find any methods that have been added explicitly
+ classes.each { node ->
+ List tmpl = []
+ tmpl.addAll(node.attributes().keySet())
+ List newMethodKeys = tmpl.findAll { it.startsWith('feature_python_method_') }
+ newMethodKeys.each { key ->
+ String featureEntry = node.attribute(key)
+ String methodName = key.substring('feature_python_method_'.length()) %>
+ static PyObject* ${module.@name}_${PythonTools.getClassNameAsVariable(node)}_${methodName}(PyObject* self, PyObject *args, PyObject *kwds)
+ ${Helper.unescape(featureEntry)}
+<%
+ }
+ }
+
+ classes.each { clazz -> doClassMethodInfo(clazz, initTypeCalls) }
+
+%>
+
+ static PyMethodDef ${module.@name}_methods[] = { <%
+ module.depthFirst().findAll({ it.name() == 'function' && Helper.parents(it, { Node lnode -> lnode.name() == 'class'}).size() == 0 }).each { %>
+ {"${it.@sym_name}", (PyCFunction)${module.@name}_${PythonTools.getPyMethodName(it,MethodType.method)}, METH_VARARGS|METH_KEYWORDS, ${Helper.hasDoc(it) ? PythonTools.getPyMethodName(it,MethodType.method) + '__doc__' : 'NULL'} }, <% }
+%>
+ {NULL, NULL, 0, NULL}
+ };
+
+ // This is the call that will call all of the other initializes
+ // for all of the classes in this module
+ static void initTypes()
+ {
+ static bool typesAlreadyInitialized = false;
+ if (!typesAlreadyInitialized)
+ {
+ typesAlreadyInitialized = true;
+<%
+ initTypeCalls.each { %>
+ ${it}();<%
+ }
+
+ classNameAsVariables.each { %>
+ if (PyType_Ready(&(Ty${it}_Type.pythonType)) < 0)
+ return;<%
+ }%>
+ }
+ }
+
+ static struct PyModuleDef createModule
+ {
+ PyModuleDef_HEAD_INIT,
+ "${module.@name}",
+ "",
+ -1,
+ ${module.@name}_methods,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ };
+
+ PyObject *PyInit_Module_${module.@name}()
+ {
+ initTypes();
+
+ // init general ${module.@name} modules
+ PyObject* module;
+
+<% classNameAsVariables.each { %>
+ Py_INCREF(&(Ty${it}_Type.pythonType));<%
+ }%>
+
+ module = PyModule_Create(&createModule);
+ if (module == NULL) return NULL;
+
+<% classes.each { clazz -> %>
+ PyModule_AddObject(module, "${clazz.@sym_name}", (PyObject*)(&(Ty${PythonTools.getClassNameAsVariable(clazz)}_Type.pythonType)));<%
+ }%>
+
+ // constants
+ PyModule_AddStringConstant(module, "__author__", "Team Kodi <http://kodi.tv>");
+ PyModule_AddStringConstant(module, "__date__", CCompileInfo::GetBuildDate().c_str());
+ PyModule_AddStringConstant(module, "__version__", "3.0.1");
+ PyModule_AddStringConstant(module, "__credits__", "Team Kodi");
+ PyModule_AddStringConstant(module, "__platform__", "ALL");
+
+ // need to handle constants
+ // #define constants
+<% module.depthFirst().findAll( { it.name() == 'constant'} ).each {
+ String pyCall =
+ (it.@type == 'int' || it.@type == 'long' || it.@type == 'unsigned int' || it.@type == 'unsigned long' || it.@type == 'bool') ?
+ 'PyModule_AddIntConstant' : 'PyModule_AddStringConstant' %>
+ ${pyCall}(module,"${it.@sym_name}",${it.@value}); <%
+ } %>
+ // constexpr constants
+<% module.depthFirst().findAll( { it.name() == 'variable' && it.@storage && it.@storage == "constexpr" && !it.@error } ).each {
+ String pyCall =
+ ( it.@type == 'q(const).int' || it.@type == 'q(const).long' || it.@type == 'q(const).unsigned int' || it.@type == 'q(const).unsigned long' || it.@type == 'q(const).bool' ) ?
+ 'PyModule_AddIntConstant' : 'PyModule_AddStringConstant' %>
+ ${pyCall}(module,"${it.@sym_name}",${it.@value}); <%
+ } %>
+ return module;
+ }
+
+} // end PythonBindings namespace for python type definitions
+
+<%
+Helper.getInsertNodes(module, 'footer').each { %>${Helper.unescape(it)}<% }
+%>