summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/python/swig.h
blob: 353d968e05587de6d365bc6d52769a941030fa48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
 *  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.
 */

#pragma once

#include "interfaces/legacy/AddonClass.h"
#include "interfaces/legacy/Exception.h"
#include "interfaces/legacy/Window.h"

#include <stdint.h>
#include <string>
#include <typeindex>

#include <Python.h>

namespace PythonBindings
{
  /**
   * This call will convert the python object passed to a string. The object
   * passed must be a python str or unicode object unless coerceToString is
   * true. If coerceToString is true then the type must be castable to a string
   * using the python call str(pObject).
   *
   * This method will handle a 'None' that's passed in. If 'None' is passed then
   * the resulting buf will contain the value of XBMCAddon::emptyString (which
   * is simply a std::string instantiated with the default constructor.
   */
  void PyXBMCGetUnicodeString(std::string& buf, PyObject* pObject, bool coerceToString = false,
                              const char* pos = "unknown",
                              const char* methodname = "unknown");

  struct TypeInfo
  {
    const char* swigType;
    TypeInfo* parentType;
    PyTypeObject pythonType;
    const std::type_index typeIndex;

    explicit TypeInfo(const std::type_info& ti);
  };

  // This will hold the pointer to the api type, whether known or unknown
  struct PyHolder
  {
    PyObject_HEAD
    int32_t magicNumber;
    const TypeInfo* typeInfo;
    XBMCAddon::AddonClass* pSelf;
  };

#define XBMC_PYTHON_TYPE_MAGIC_NUMBER 0x58626D63

  /**
   * This method retrieves the pointer from the PyHolder. The return value should
   * be cast to the appropriate type.
   *
   * Since the calls to this are generated there's no NULL pointer checks
   */
  inline XBMCAddon::AddonClass* retrieveApiInstance(PyObject* pythonObj, const TypeInfo* typeToCheck,
                                   const char* methodNameForErrorString,
                                   const char* typenameForErrorString)
  {
    if (pythonObj == NULL || pythonObj == Py_None)
      return NULL;
    if (reinterpret_cast<PyHolder*>(pythonObj)->magicNumber != XBMC_PYTHON_TYPE_MAGIC_NUMBER || !PyObject_TypeCheck(pythonObj, const_cast<PyTypeObject*>((&(typeToCheck->pythonType)))))
      throw XBMCAddon::WrongTypeException("Incorrect type passed to \"%s\", was expecting a \"%s\".",methodNameForErrorString,typenameForErrorString);
    return reinterpret_cast<PyHolder*>(pythonObj)->pSelf;
  }

  bool isParameterRightType(const char* passedType, const char* expectedType, const char* methodNamespacePrefix, bool tryReverse = true);

  XBMCAddon::AddonClass* doretrieveApiInstance(const PyHolder* pythonObj, const TypeInfo* typeInfo, const char* expectedType,
                              const char* methodNamespacePrefix, const char* methodNameForErrorString);

  /**
   * This method retrieves the pointer from the PyHolder. The return value should
   * be cast to the appropriate type.
   *
   * Since the calls to this are generated there's no NULL pointer checks
   *
   * This method will return NULL if either the pythonObj is NULL or the
   * pythonObj is Py_None.
   */
  inline XBMCAddon::AddonClass* retrieveApiInstance(const PyObject* pythonObj, const char* expectedType, const char* methodNamespacePrefix,
                                   const char* methodNameForErrorString)
  {
    return (pythonObj == NULL || pythonObj == Py_None) ? NULL :
      doretrieveApiInstance(reinterpret_cast<const PyHolder*>(pythonObj),reinterpret_cast<const PyHolder*>(pythonObj)->typeInfo, expectedType, methodNamespacePrefix, methodNameForErrorString);
  }

  /**
   * 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);

  /**
   * 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);

  /**
   * 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);

  /**
   * This method allows for conversion of the native api Type to the Python type.
   *
   * When this form of the call is used (and pythonType 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 pythonType is NULL then the type is inferred using the class metadata
   * stored in the AddonClass instance 'api'.
   */
  PyObject* makePythonInstance(XBMCAddon::AddonClass* api, PyTypeObject* pythonType, bool incrementRefCount);

  /**
   * This method allows for conversion of the native api Type to the Python type.
   *
   * When this form of the call is used then the python type constructed will be the
   * type given by the class metadata in the AddonClass instance 'api'.
   *
   * This is just a helper inline to call the other makePythonInstance with NULL as
   * the pythonType.
   */
  inline PyObject* makePythonInstance(XBMCAddon::AddonClass* api, bool incrementRefCount)
  {
    return makePythonInstance(api,NULL,incrementRefCount);
  }

  void registerAddonClassTypeInformation(const TypeInfo* classInfo);
  const TypeInfo* getTypeInfoForInstance(XBMCAddon::AddonClass* obj);

  int dummy_tp_init(PyObject* self, PyObject* args, PyObject* kwds);

  class Director
  {
  protected:
    PyObject* self;
  public:
    inline Director() : self(NULL) {}
    inline void setPyObjectForDirector(PyObject* pyargself) { self = pyargself; }
  };

  /**
   * This exception is thrown from Director calls that call into python when the
   * Python error is
   */
  class PythonToCppException : public XbmcCommons::UncheckedException
  {
  public:
    /**
     * Assuming a PyErr_Occurred, this will fill the exception message with all
     *  of the appropriate information including the traceback if it can be
     *  obtained. It will also clear the python message.
     */
    PythonToCppException();
    PythonToCppException(const std::string &exceptionType, const std::string &exceptionValue, const std::string &exceptionTraceback);

    static bool ParsePythonException(std::string &exceptionType, std::string &exceptionValue, std::string &exceptionTraceback);

  protected:
    void SetMessage(const std::string &exceptionType, const std::string &exceptionValue, const std::string &exceptionTraceback);
  };

  template<class T> struct PythonCompare
  {
    static inline int compare(PyObject* obj1, PyObject* obj2, const char* swigType, const char* methodNamespacePrefix, const char* methodNameForErrorString)
    {
      XBMC_TRACE;
      try
      {
        T* o1 = (T*)retrieveApiInstance(obj1, swigType, methodNamespacePrefix, methodNameForErrorString);
        T* o2 = (T*)retrieveApiInstance(obj2, swigType, methodNamespacePrefix, methodNameForErrorString);

        return ((*o1) < (*o2) ? -1 :
                ((*o1) > (*o2) ? 1 : 0));
      }
      catch (const XBMCAddon::WrongTypeException& e)
      {
        CLog::Log(LOGERROR, "EXCEPTION: {}", e.GetExMessage());
        PyErr_SetString(PyExc_RuntimeError, e.GetExMessage());
      }
      return -1;
    }
  };
}