2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "LanguageHook.h"
26 namespace PythonBindings
28 TypeInfo::TypeInfo(const std::type_info& ti) : swigType(NULL), parentType(NULL), typeIndex(ti)
30 static PyTypeObject py_type_object_header = { PyObject_HEAD_INIT(NULL) 0};
31 static int size = (long*)&(py_type_object_header.tp_name) - (long*)&py_type_object_header;
32 memcpy(&(this->pythonType), &py_type_object_header, size);
35 class PyObjectDecrementor
39 inline PyObjectDecrementor(PyObject* pyobj) : obj(pyobj) {}
40 inline ~PyObjectDecrementor() { Py_XDECREF(obj); }
42 inline PyObject* get() { return obj; }
45 void PyXBMCGetUnicodeString(std::string& buf, PyObject* pObject, bool coerceToString,
46 const char* argumentName, const char* methodname) throw (XBMCAddon::WrongTypeException)
48 // TODO: UTF-8: Does python use UTF-16?
49 // Do we need to convert from the string charset to UTF-8
50 // for non-unicode data?
51 if (PyUnicode_Check(pObject))
53 // Python unicode objects are UCS2 or UCS4 depending on compilation
54 // options, wchar_t is 16-bit or 32-bit depending on platform.
55 // Avoid the complexity by just letting python convert the string.
56 PyObject *utf8_pyString = PyUnicode_AsUTF8String(pObject);
60 buf = PyString_AsString(utf8_pyString);
61 Py_DECREF(utf8_pyString);
65 if (PyString_Check(pObject))
67 buf = PyString_AsString(pObject);
71 // if we got here then we need to coerce the value to a string
74 PyObjectDecrementor dec(PyObject_Str(pObject));
75 PyObject* pyStrCast = dec.get();
78 PyXBMCGetUnicodeString(buf,pyStrCast,false,argumentName,methodname);
83 // Object is not a unicode or a normal string.
85 throw XBMCAddon::WrongTypeException("argument \"%s\" for method \"%s\" must be unicode or str", argumentName, methodname);
88 // need to compare the typestring
89 bool isParameterRightType(const char* passedType, const char* expectedType, const char* methodNamespacePrefix, bool tryReverse)
91 if (strcmp(expectedType,passedType) == 0)
94 // well now things are a bit more complicated. We need to see if the passed type
95 // is a subset of the overall type
96 std::string et(expectedType);
97 bool isPointer = (et[0] == 'p' && et[1] == '.');
98 std::string baseType(et,(isPointer ? 2 : 0)); // this may contain a namespace
100 std::string ns(methodNamespacePrefix);
101 // cut off trailing '::'
102 if (ns.size() > 2 && ns[ns.size() - 1] == ':' && ns[ns.size() - 2] == ':')
103 ns = ns.substr(0,ns.size()-2);
110 // now we need to see if the expected type can be munged
111 // into the passed type by tacking on the namespace of
113 std::string check(isPointer ? "p." : "");
118 if (strcmp(check.c_str(),passedType) == 0)
121 // see if the namespace is nested.
122 int posOfScopeOp = ns.find("::");
123 if (posOfScopeOp >= 0)
126 // cur off the outermost namespace
127 ns = ns.substr(posOfScopeOp + 2);
131 // so far we applied the namespace to the expected type. Now lets try
132 // the reverse if we haven't already.
134 return isParameterRightType(expectedType, passedType, methodNamespacePrefix, false);
139 PythonToCppException::PythonToCppException() : XbmcCommons::UncheckedException(" ")
141 setClassname("PythonToCppException");
145 PyObject* exc_traceback;
146 PyObject* pystring = NULL;
150 PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
151 if (exc_type == 0 && exc_value == 0 && exc_traceback == 0)
153 msg = "Strange: No Python exception occured";
157 msg = "-->Python callback/script returned the following error<--\n";
158 msg += " - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS!\n";
159 if (exc_type != NULL && (pystring = PyObject_Str(exc_type)) != NULL && (PyString_Check(pystring)))
161 PyObject *tracebackModule;
163 msg.AppendFormat("Error Type: %s\n", PyString_AsString(pystring));
164 if (PyObject_Str(exc_value))
165 msg.AppendFormat("Error Contents: %s\n", PyString_AsString(PyObject_Str(exc_value)));
167 tracebackModule = PyImport_ImportModule((char*)"traceback");
168 if (tracebackModule != NULL)
170 PyObject *tbList, *emptyString, *strRetval;
172 tbList = PyObject_CallMethod(tracebackModule, (char*)"format_exception", (char*)"OOO", exc_type, exc_value == NULL ? Py_None : exc_value, exc_traceback == NULL ? Py_None : exc_traceback);
173 emptyString = PyString_FromString("");
174 strRetval = PyObject_CallMethod(emptyString, (char*)"join", (char*)"O", tbList);
176 msg.Format("%s%s", msg.c_str(),PyString_AsString(strRetval));
179 Py_DECREF(emptyString);
180 Py_DECREF(strRetval);
181 Py_DECREF(tracebackModule);
183 msg += "-->End of Python script error report<--\n";
188 msg += "<unknown exception type>";
192 Py_XDECREF(exc_type);
193 Py_XDECREF(exc_value); // caller owns all 3
194 Py_XDECREF(exc_traceback); // already NULL'd out
195 Py_XDECREF(pystring);
197 SetMessage("%s",msg.c_str());
200 XBMCAddon::AddonClass* doretrieveApiInstance(const PyHolder* pythonObj, const TypeInfo* typeInfo, const char* expectedType,
201 const char* methodNamespacePrefix, const char* methodNameForErrorString) throw (XBMCAddon::WrongTypeException)
203 if (pythonObj->magicNumber != XBMC_PYTHON_TYPE_MAGIC_NUMBER)
204 throw XBMCAddon::WrongTypeException("Non api type passed to \"%s\" in place of the expected type \"%s.\"",
205 methodNameForErrorString, expectedType);
206 if (!isParameterRightType(typeInfo->swigType,expectedType,methodNamespacePrefix))
208 // maybe it's a child class
209 if (typeInfo->parentType)
210 return doretrieveApiInstance(pythonObj, typeInfo->parentType,expectedType,
211 methodNamespacePrefix, methodNameForErrorString);
213 throw XBMCAddon::WrongTypeException("Incorrect type passed to \"%s\", was expecting a \"%s\" but received a \"%s\"",
214 methodNameForErrorString,expectedType,typeInfo->swigType);
216 return ((PyHolder*)pythonObj)->pSelf;
220 * This method is a helper for the generated API. It's called prior to any API
221 * class constructor being returned from the generated code to Python
223 void prepareForReturn(XBMCAddon::AddonClass* c)
228 PyThreadState* state = PyThreadState_Get();
229 XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp)->RegisterAddonClassInstance(c);
233 static bool handleInterpRegistrationForClean(XBMCAddon::AddonClass* c)
237 XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> lh =
238 XBMCAddon::AddonClass::Ref<XBMCAddon::AddonClass>(c->GetLanguageHook());
242 lh->UnregisterAddonClassInstance(c);
247 PyThreadState* state = PyThreadState_Get();
248 lh = XBMCAddon::Python::PythonLanguageHook::GetIfExists(state->interp);
249 if (lh.isNotNull()) lh->UnregisterAddonClassInstance(c);
257 * This method is a helper for the generated API. It's called prior to any API
258 * class destructor being dealloc-ed from the generated code from Python
260 void cleanForDealloc(XBMCAddon::AddonClass* c)
263 if (handleInterpRegistrationForClean(c))
268 * This method is a helper for the generated API. It's called prior to any API
269 * class destructor being dealloc-ed from the generated code from Python
271 * There is a Catch-22 in the destruction of a Window. 'dispose' needs to be
272 * called on destruction but cannot be called from the destructor.
273 * This overrides the default cleanForDealloc to resolve that.
275 void cleanForDealloc(XBMCAddon::xbmcgui::Window* c)
278 if (handleInterpRegistrationForClean(c))
286 * This method allows for conversion of the native api Type to the Python type.
288 * When this form of the call is used (and pytype isn't NULL) then the
289 * passed type is used in the instance. This is for classes that extend API
290 * classes in python. The type passed may not be the same type that's stored
291 * in the class metadata of the AddonClass of which 'api' is an instance,
292 * it can be a subclass in python.
294 * if pytype is NULL then the type is inferred using the class metadata
295 * stored in the AddonClass instance 'api'.
297 PyObject* makePythonInstance(XBMCAddon::AddonClass* api, PyTypeObject* pytype, bool incrementRefCount)
299 // null api types result in Py_None
306 // retrieve the TypeInfo from the api class
307 const TypeInfo* typeInfo = getTypeInfoForInstance(api);
308 PyTypeObject* typeObj = pytype == NULL ? (PyTypeObject*)(&(typeInfo->pythonType)) : pytype;
310 PyHolder* self = (PyHolder*)typeObj->tp_alloc(typeObj,0);
311 if (!self) return NULL;
312 self->magicNumber = XBMC_PYTHON_TYPE_MAGIC_NUMBER;
313 self->typeInfo = typeInfo;
315 if (incrementRefCount)
316 Py_INCREF((PyObject*)self);
317 return (PyObject*)self;
320 std::map<XbmcCommons::type_index, const TypeInfo*> typeInfoLookup;
322 void registerAddonClassTypeInformation(const TypeInfo* classInfo)
324 typeInfoLookup[classInfo->typeIndex] = classInfo;
327 const TypeInfo* getTypeInfoForInstance(XBMCAddon::AddonClass* obj)
329 XbmcCommons::type_index ti(typeid(*obj));
330 return typeInfoLookup[ti];