WIP: some refactoring of the GSI Python binding (less static objects, new conversion operators, preparations for external use)

This commit is contained in:
Matthias Koefferlein 2018-06-09 01:50:16 +02:00
parent eca8697398
commit 2737e14b13
14 changed files with 106 additions and 85 deletions

View File

@ -19,7 +19,7 @@ QtNetwork.depends += QtCore
QtSql.depends += QtCore
QtWidgets.depends += QtGui
QtDesigner.depends += QtCore
QtMultimedia.depends += QtCore QtWidgets
QtMultimedia.depends += QtCore QtWidgets QtNetwork
QtPrintSupport.depends += QtCore QtWidgets
QtSvg.depends += QtCore QtWidgets
QtXmlPatterns.depends += QtCore

View File

@ -542,7 +542,7 @@ PythonInterpreter::define_variable (const std::string &name, const std::string &
PythonPtr main_module (PyImport_AddModule ("__main__"));
PythonPtr dict (PyModule_GetDict (main_module.get ()));
if (dict) {
PythonRef v (c2python<std::string> (value));
PythonRef v (c2python (value));
PyDict_SetItemString (dict.get (), name.c_str (), v.get ());
}
}

View File

@ -444,24 +444,24 @@ template <>
PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c)
{
if (c.is_double ()) {
return c2python<double> (c.to_double ());
return c2python (c.to_double ());
} else if (c.is_bool ()) {
return c2python<bool> (c.to_bool ());
return c2python (c.to_bool ());
} else if (c.is_a_string ()) {
return c2python<std::string> (c.to_string ());
return c2python (c.to_string ());
} else if (c.is_long ()) {
return c2python<long> (c.to_long ());
return c2python (c.to_long ());
} else if (c.is_ulong ()) {
return c2python<unsigned long> (c.to_ulong ());
return c2python (c.to_ulong ());
} else if (c.is_longlong ()) {
return c2python<long long> (c.to_longlong ());
return c2python (c.to_longlong ());
} else if (c.is_ulonglong ()) {
return c2python<unsigned long long> (c.to_ulonglong ());
return c2python (c.to_ulonglong ());
} else if (c.is_array ()) {
PyObject *ret = PyDict_New ();
for (tl::Variant::const_array_iterator i = c.begin_array (); i != c.end_array (); ++i) {
PyDict_SetItem (ret, c2python<tl::Variant> (i->first), c2python<tl::Variant> (i->second));
PyDict_SetItem (ret, c2python (i->first), c2python (i->second));
}
return ret;
@ -470,7 +470,7 @@ PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c)
PyObject *ret = PyList_New (c.get_list ().size ());
size_t index = 0;
for (tl::Variant::const_iterator i = c.begin (); i != c.end (); ++i, ++index) {
PyList_SetItem (ret, index, c2python<tl::Variant> (*i));
PyList_SetItem (ret, index, c2python (*i));
}
return ret;
@ -545,7 +545,7 @@ PyObject *c2python_func<const QString &>::operator() (const QString &qs)
} else {
// TODO: can be done more efficently
std::string c (tl::to_string (qs));
return c2python<std::string> (c);
return c2python (c);
}
}

View File

@ -24,8 +24,9 @@
#ifndef _HDR_pyaConvert
#define _HDR_pyaConvert
#include "Python.h"
#include <Python.h>
#include "pyaCommon.h"
#include "pyaModule.h"
#include "pyaObject.h"
#include "gsiClassBase.h"
@ -63,14 +64,14 @@ class PYAObjectBase;
* @param can_destroy If true, the Python object can be destroyed explicitly
* @return The Python object
*/
PyObject *
PYA_PUBLIC PyObject *
object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy);
/**
* @brief Translates an object to a Python object (PyObject)
* This version takes it's flags from the atype given.
*/
PyObject *
PYA_PUBLIC PyObject *
object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype);
// -------------------------------------------------------------------
@ -239,7 +240,7 @@ struct test_type_func<T *>
* @param loose If true, the type is checked more loosely. Use for second-pass matching.
*/
template <class T>
inline bool test_type (PyObject * rval, bool loose)
inline bool test_type (PyObject * rval, bool loose = false)
{
return test_type_func<T> () (rval, loose);
}
@ -248,7 +249,7 @@ inline bool test_type (PyObject * rval, bool loose)
* @brief Test a PyObject *for compatibility with a vector of the given type R
*/
template <class R>
inline bool test_vector (PyObject *arr, bool loose)
inline bool test_vector (PyObject *arr, bool loose = false)
{
if (PyList_Check (arr)) {
@ -283,13 +284,16 @@ inline bool test_vector (PyObject *arr, bool loose)
template <class T>
struct python2c_func
{
T operator() (PyObject *rval);
T operator() (PyObject * /*rval*/)
{
tl_assert (false); // type not bound
}
};
template <> long python2c_func<long>::operator() (PyObject *rval);
template <> unsigned long python2c_func<unsigned long>::operator() (PyObject *rval);
template <> bool python2c_func<bool>::operator() (PyObject *rval);
template <> char python2c_func<char>::operator() (PyObject *rval);
template <> PYA_PUBLIC long python2c_func<long>::operator() (PyObject *rval);
template <> PYA_PUBLIC unsigned long python2c_func<unsigned long>::operator() (PyObject *rval);
template <> PYA_PUBLIC bool python2c_func<bool>::operator() (PyObject *rval);
template <> PYA_PUBLIC char python2c_func<char>::operator() (PyObject *rval);
template <class D, class C>
struct python2c_func_cast
@ -308,23 +312,23 @@ template <> struct python2c_func<unsigned short> : public python2c_func_cast<uns
template <> struct python2c_func<int> : public python2c_func_cast<int, long> { };
template <> struct python2c_func<unsigned int> : public python2c_func_cast<unsigned int, long> { };
template <> long long python2c_func<long long>::operator() (PyObject *rval);
template <> unsigned long long python2c_func<unsigned long long>::operator() (PyObject *rval);
template <> PYA_PUBLIC long long python2c_func<long long>::operator() (PyObject *rval);
template <> PYA_PUBLIC unsigned long long python2c_func<unsigned long long>::operator() (PyObject *rval);
#if defined(HAVE_64BIT_COORD)
template <> __int128 python2c_func<__int128>::operator() (PyObject *rval);
#endif
template <> double python2c_func<double>::operator() (PyObject *rval);
template <> PYA_PUBLIC double python2c_func<double>::operator() (PyObject *rval);
template <> struct python2c_func<float> : public python2c_func_cast<float, double> { };
template <> std::string python2c_func<std::string>::operator() (PyObject *rval);
template <> QByteArray python2c_func<QByteArray>::operator() (PyObject *rval);
template <> QString python2c_func<QString>::operator() (PyObject *rval);
template <> PYA_PUBLIC std::string python2c_func<std::string>::operator() (PyObject *rval);
template <> PYA_PUBLIC QByteArray python2c_func<QByteArray>::operator() (PyObject *rval);
template <> PYA_PUBLIC QString python2c_func<QString>::operator() (PyObject *rval);
template <> struct python2c_func<void *> : public python2c_func_cast<void *, size_t> { };
template <> tl::Variant python2c_func<tl::Variant>::operator() (PyObject *rval);
template <> PYA_PUBLIC tl::Variant python2c_func<tl::Variant>::operator() (PyObject *rval);
template <class T> struct python2c_func<T &>
{
@ -593,10 +597,14 @@ struct c2python_func<float>
}
};
template <> PyObject *c2python_func<const char *>::operator() (const char *);
template <> PyObject *c2python_func<const QString &>::operator() (const QString &c);
template <> PyObject *c2python_func<const QByteArray &>::operator() (const QByteArray &c);
template <> PyObject *c2python_func<const std::string &>::operator() (const std::string &c);
template <> PYA_PUBLIC PyObject *c2python_func<const char *>::operator() (const char *);
template <> PYA_PUBLIC PyObject *c2python_func<const QString &>::operator() (const QString &c);
template <> struct c2python_func<QString> : public c2python_func<const QString &> { };
template <> PYA_PUBLIC PyObject *c2python_func<const QByteArray &>::operator() (const QByteArray &c);
template <> struct c2python_func<QByteArray> : public c2python_func<const QByteArray &> { };
template <> PYA_PUBLIC PyObject *c2python_func<const std::string &>::operator() (const std::string &c);
template <> struct c2python_func<std::string> : public c2python_func<const std::string &> { };
template <>
struct c2python_func<void *>
@ -608,7 +616,8 @@ struct c2python_func<void *>
}
};
template <> PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c);
template <> PYA_PUBLIC PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c);
template <> struct c2python_func<tl::Variant> : public c2python_func<const tl::Variant &> { };
/**
* @brief Converts the Python object to the given type

View File

@ -474,25 +474,25 @@ struct reader
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
{
if (arg.is_ref ()) {
*ret = c2python<R> (rr->template read<R &> (*heap));
*ret = c2python (rr->template read<R &> (*heap));
} else if (arg.is_cref ()) {
*ret = c2python<R> (rr->template read<const R &> (*heap));
*ret = c2python (rr->template read<const R &> (*heap));
} else if (arg.is_ptr ()) {
R *p = rr->template read<R *> (*heap);
if (p) {
*ret = c2python<R> (*p);
*ret = c2python (*p);
} else {
*ret = PythonRef (Py_None, false /*borrowed*/);
}
} else if (arg.is_cptr ()) {
const R *p = rr->template read<const R *> (*heap);
if (p) {
*ret = c2python<R> (*p);
*ret = c2python (*p);
} else {
*ret = PythonRef (Py_None, false /*borrowed*/);
}
} else {
*ret = c2python<R> (rr->template read<R> (*heap));
*ret = c2python (rr->template read<R> (*heap));
}
}
};
@ -512,7 +512,7 @@ struct reader<void *>
tl_assert (! arg.is_ref ());
tl_assert (! arg.is_cptr ());
tl_assert (! arg.is_ptr ());
*ret = c2python<void *> (rr->read<void *> (*heap));
*ret = c2python (rr->read<void *> (*heap));
}
};
@ -528,7 +528,7 @@ struct reader<gsi::StringType>
if (!a.get ()) {
*ret = PythonRef (Py_None, false /*borrowed*/);
} else {
*ret = c2python<std::string> (std::string (a->c_str (), a->size ()));
*ret = c2python (std::string (a->c_str (), a->size ()));
}
}
};
@ -582,7 +582,7 @@ PyObject *object_from_variant (const tl::Variant &var, PYAObjectBase *self, cons
return object_to_python ((void *) var.to_user (), self, var.user_cls ()->gsi_cls (), pass_obj, is_const, prefer_copy, can_destroy);
} else {
return c2python<tl::Variant> (var);
return c2python (var);
}
}

View File

@ -35,18 +35,6 @@
namespace pya
{
// -------------------------------------------------------------------
// A metatype object to identify the pya types
struct PYAMetaType : public PyTypeObject { };
static PYAMetaType PYA_MetaType;
struct PYATypeObject
: public PyTypeObject
{
const gsi::ClassBase *cls;
};
// -------------------------------------------------------------------
// The lookup table for the method overload resolution
@ -930,7 +918,7 @@ object_default_ne_impl (PyObject *self, PyObject *args)
if (! res) {
return NULL;
} else {
return c2python<bool> (! python2c<bool> (res.get ()));
return c2python (! python2c<bool> (res.get ()));
}
}
@ -947,7 +935,7 @@ object_default_ge_impl (PyObject *self, PyObject *args)
if (! res) {
return NULL;
} else {
return c2python<bool> (! python2c<bool> (res.get ()));
return c2python (! python2c<bool> (res.get ()));
}
}
@ -971,7 +959,7 @@ object_default_le_impl (PyObject *self, PyObject *args)
if (! lt_res) {
return NULL;
}
return c2python<bool> (python2c<bool> (eq_res.get ()) || python2c<bool> (lt_res.get ()));
return c2python (python2c<bool> (eq_res.get ()) || python2c<bool> (lt_res.get ()));
}
/**
@ -994,7 +982,7 @@ object_default_gt_impl (PyObject *self, PyObject *args)
if (! lt_res) {
return NULL;
}
return c2python<bool> (! (python2c<bool> (eq_res.get ()) || python2c<bool> (lt_res.get ())));
return c2python (! (python2c<bool> (eq_res.get ()) || python2c<bool> (lt_res.get ())));
}
/**
@ -1063,7 +1051,7 @@ object_destroyed (PyObject *self, PyObject *args)
return NULL;
}
return c2python<bool> (((PYAObjectBase *) self)->destroyed ());
return c2python (((PYAObjectBase *) self)->destroyed ());
}
/**
@ -1076,7 +1064,7 @@ object_is_const (PyObject *self, PyObject *args)
return NULL;
}
return c2python<bool> (((PYAObjectBase *) self)->const_ref ());
return c2python (((PYAObjectBase *) self)->const_ref ());
}
static PyObject *
@ -2149,6 +2137,7 @@ property_setter_func (PyObject *self, PyObject *value, void *closure)
// The PythonModule implementation
std::map<const gsi::MethodBase *, std::string> PythonModule::m_python_doc;
std::vector<const gsi::ClassBase *> PythonModule::m_classes;
const std::string pymod_name ("pykl");
@ -2350,16 +2339,10 @@ PythonModule::make_classes (const char *mod_name)
m_base_class_name = m_mod_name + ".__Base";
// Late-initialize PYA_MetaType (we can do this multiple times as the initialization does not
// change anything). PYATypeObject adds one pointer member at the end.
memcpy(&PYA_MetaType, &PyType_Type, sizeof (PyType_Type));
PYA_MetaType.tp_basicsize += sizeof (void *);
PYATypeObject *base_class = (PYATypeObject *) PyType_Type.tp_alloc (&PYA_MetaType, sizeof (PYATypeObject));
PyTypeObject *base_class = (PyTypeObject *) PyType_Type.tp_alloc (&PyType_Type, 0);
tl_assert (base_class != NULL);
mp_base_class = PythonRef ((PyObject *) base_class);
base_class->cls = 0; // base class
base_class->tp_base = &PyBaseObject_Type;
base_class->tp_name = m_base_class_name.c_str ();
base_class->tp_basicsize = sizeof (PYAObjectBase);
@ -2442,6 +2425,11 @@ PythonModule::make_classes (const char *mod_name)
any = true;
// Create the actual class
m_classes.push_back (c.operator-> ());
PythonRef bases (PyTuple_New (1));
PyObject *base = mp_base_class.get ();
if (c->base () != 0) {
@ -2455,16 +2443,21 @@ PythonModule::make_classes (const char *mod_name)
PythonRef dict (PyDict_New ());
PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (m_mod_name)).get ());
PyDict_SetItemString (dict.get (), "__doc__", PythonRef (c2python (c->doc ())).get ());
PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (m_classes.size () - 1)).get ());
PythonRef args (PyTuple_New (3));
PyTuple_SetItem (args.get (), 0, c2python (c->name ()));
PyTuple_SetItem (args.get (), 1, bases.release ());
PyTuple_SetItem (args.get (), 2, dict.release ());
PYATypeObject *type = (PYATypeObject *) PyObject_Call ((PyObject *) &PYA_MetaType, args.get (), NULL);
type->cls = c.operator-> ();
PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL);
if (type == NULL) {
check_error ();
tl_assert (false);
}
PythonClassClientData::initialize (*c, type);
tl_assert (type != NULL);
tl_assert (cls_for_type (type) == c.operator-> ()); // @@@
PyList_Append (all_list.get (), PythonRef (c2python (c->name ())).get ());
PyModule_AddObject (module, c->name ().c_str (), (PyObject *) type);
@ -2888,7 +2881,7 @@ PythonModule::make_classes (const char *mod_name)
} else {
PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class);
PythonRef name (c2python<std::string> (*a));
PythonRef name (c2python (*a));
// Note: we use GenericSetAttr since that one allows us setting attributes on built-in types
PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc);
@ -2910,14 +2903,19 @@ PythonModule::make_classes (const char *mod_name)
const gsi::ClassBase *PythonModule::cls_for_type (PyTypeObject *type)
{
while (type) {
// all pya class use our own metatype
if (type->ob_type == &PYA_MetaType) {
return ((PYATypeObject *)type)->cls;
// GSI classes store their class index inside the __gsi_id__ attribute
if (PyObject_HasAttrString ((PyObject *) type, "__gsi_id__")) {
PyObject *cls_id = PyObject_GetAttrString ((PyObject *) type, "__gsi_id__");
if (cls_id != NULL && pya::test_type<size_t> (cls_id)) {
size_t i = pya::python2c<size_t> (cls_id);
if (i < m_classes.size ()) {
return m_classes [i];
}
}
// not found - try base class
type = type->tp_base;
}
return 0;
}

View File

@ -112,6 +112,7 @@ private:
char *mp_mod_def;
static std::map<const gsi::MethodBase *, std::string> m_python_doc;
static std::vector<const gsi::ClassBase *> m_classes;
};
}

View File

@ -32,6 +32,7 @@
#include "tlObject.h"
#include "pyaRefs.h"
#include "pyaCommon.h"
namespace pya
{
@ -176,7 +177,7 @@ private:
* Note: the PYAObjectBase must be directly derived from PyObject so that
* a PyObject pointer can be cast to a PYAObjectBase pointer.
*/
class PYAObjectBase
class PYA_PUBLIC PYAObjectBase
: public PyObject
{
public:

View File

@ -114,13 +114,13 @@ PyObject *PythonRef::release ()
PythonPtr::PythonPtr ()
: mp_obj (NULL)
{
{
// .. nothing yet ..
}
PythonPtr::PythonPtr (PyObject *obj)
: mp_obj (obj)
{
{
Py_XINCREF (obj);
}

View File

@ -24,6 +24,8 @@
#ifndef _HDR_pyaRefs
#define _HDR_pyaRefs
#include "pyaCommon.h"
struct _object;
typedef _object PyObject;
@ -41,7 +43,7 @@ class PythonPtr;
* borrowed references too.
* PythonRef will basically become the owner of the referred object.
*/
class PythonRef
class PYA_PUBLIC PythonRef
{
public:
/**
@ -140,7 +142,7 @@ private:
* This reference represents borrowed references. Upon construction and destruction, this
* object will automatically increment and decrement the reference count.
*/
class PythonPtr
class PYA_PUBLIC PythonPtr
{
public:
/**

View File

@ -8,4 +8,4 @@ SOURCES = \
HEADERS += \
LIBS += -lklayout_QtMultimedia
LIBS += -lklayout_QtMultimedia -lklayout_QtNetwork

View File

@ -26,4 +26,9 @@
#include "../../gsiqt/qtbasic/gsiQtMultimediaExternals.h"
FORCE_LINK_GSI_QTMULTIMEDIA
// This is required because QAction and QWidget are used are arguments in QtGui, but are
// defined in QtWidgets
#include "../../gsiqt/qtbasic/gsiQtNetworkExternals.h"
FORCE_LINK_GSI_QTNETWORK
DEFINE_PYMOD(QtMultimedia, "QtMultimedia", "KLayout/Qt module 'QtMultimedia'")

View File

@ -3,7 +3,9 @@ DESTDIR = $$OUT_PWD/..
LIBDIR = $$OUT_PWD/../..
DESTDIR_PYMOD = $$LIBDIR/pykl
include($$PWD/../lib.pri)
TEMPLATE = lib
include($$PWD/../klayout.pri)
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC

View File

@ -35,5 +35,8 @@ equals(HAVE_QTBINDINGS, "1") {
}
ALL_DIRS = $$SUBDIRS
SUBDIRS += unit_tests
unit_tests.depends += $$ALL_DIRS
SUBDIRS += bridge_sample
unit_tests.depends += $$ALL_DIRS bridge_sample