WIP: some refactoring of pya to reduce dependency on GSI for bridge applications.

This commit is contained in:
Matthias Koefferlein 2018-06-09 10:32:26 +02:00
parent d938bb999b
commit 7f2e740dd4
16 changed files with 605 additions and 425 deletions

View File

@ -20,16 +20,18 @@
*/
/**
* @brief This header provides the definitions for embedding support
*/
#ifndef _HDR_pya
#define _HDR_pya
#include "pyaRefs.h"
#include "pyaCommon.h"
#include "gsi.h"
#include "gsiInterpreter.h"
#include "tlScriptError.h"
#include "pyaCommon.h"
#include <list>
#include <string>
@ -68,42 +70,6 @@ namespace pya
throw; \
}
/**
* Two helper macros that translate C++ exceptions into Python errors
*/
#define PYA_TRY \
{ \
try {
#define PYA_CATCH(where) \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
std::string msg = std::string(ex.what ()) + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (tl::Exception &ex) { \
std::string msg; \
msg = ex.msg () + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (...) { \
std::string msg = tl::to_string (QObject::tr ("Unspecific exception in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} \
}
#define PYA_CATCH_ANYWHERE \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.what ()); \
} catch (tl::Exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.msg ().c_str ()); \
} catch (...) { \
PyErr_SetString (PyExc_RuntimeError, tl::to_string (QObject::tr ("Unspecific exception in ")).c_str ()); \
} \
}
/**
* @brief A class encapsulating a python exception
*/

View File

@ -15,7 +15,9 @@ SOURCES = \
pyaObject.cc \
pyaRefs.cc \
pyaUtils.cc \
pyaModule.cc
pyaModule.cc \
pyaSignalHandler.cc \
pyaStatusChangedListener.cc
HEADERS += \
pya.h \
@ -27,7 +29,9 @@ HEADERS += \
pyaObject.h \
pyaRefs.h \
pyaUtils.h \
pyaModule.h
pyaModule.h \
pyaSignalHandler.h \
pyaStatusChangedListener.h
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC

View File

@ -24,13 +24,21 @@
#include "pyaConvert.h"
#include "pyaObject.h"
#include "pyaModule.h"
#include "pyaStatusChangedListener.h"
#include "pyaUtils.h"
#include "gsiClassBase.h"
#include <string>
namespace pya
{
bool is_derived_from (const gsi::ClassBase *cls, const std::type_info &ti)
{
return cls->is_derived_from (gsi::class_by_typeinfo_no_assert (ti));
}
template <>
long python2c_func<long>::operator() (PyObject *rval)
{

View File

@ -29,7 +29,6 @@
#include "pyaCommon.h"
#include "pyaModule.h"
#include "pyaObject.h"
#include "gsiClassBase.h"
#include "tlVariant.h"
#include "tlException.h"
@ -39,10 +38,14 @@
#include <QString>
#include <QByteArray>
#include <typeinfo>
namespace gsi
{
class ClassBase;
class ArgType;
const ClassBase *class_by_typeinfo_no_assert (const std::type_info &ti);
}
namespace pya
@ -50,6 +53,9 @@ namespace pya
class PYAObjectBase;
// Forward declarations to reduce the dependency on gsi
PYA_PUBLIC bool is_derived_from (const gsi::ClassBase *cls, const std::type_info &ti);
// -------------------------------------------------------------------
// Conversion of a generic object to a Python object
@ -196,7 +202,7 @@ struct test_type_func<T &>
{
// TODO: we currently don't check for non-constness
const gsi::ClassBase *cls_decl = pya::PythonModule::cls_for_type (Py_TYPE (rval));
return cls_decl && cls_decl->is_derived_from (gsi::class_by_typeinfo_no_assert (typeid (T)));
return cls_decl && is_derived_from (cls_decl, typeid (T));
}
};
@ -338,7 +344,7 @@ template <class T> struct python2c_func<T &>
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (rval));
tl_assert (cls_decl != 0);
tl_assert (cls_decl->is_derived_from (gsi::class_by_typeinfo_no_assert (typeid (T))));
tl_assert (is_derived_from (cls_decl, typeid (T)));
PYAObjectBase *p = (PYAObjectBase *) (rval);
return *((T *)p->obj ());

View File

@ -26,6 +26,7 @@
#include "pyaMarshal.h"
#include "pyaObject.h"
#include "pyaConvert.h"
#include "pyaSignalHandler.h"
#include "pya.h"
namespace pya

View File

@ -24,10 +24,12 @@
#include <Python.h>
#include "pyaModule.h"
#include "pya.h"
#include "pyaObject.h"
#include "pyaConvert.h"
#include "pyaHelpers.h"
#include "pyaMarshal.h"
#include "pyaSignalHandler.h"
#include "pyaUtils.h"
#include <map>

View File

@ -24,7 +24,21 @@
#ifndef _HDR_pyaModule
#define _HDR_pyaModule
#include "pya.h"
#include <Python.h>
#include "pyaCommon.h"
#include "pyaRefs.h"
#include <map>
#include <list>
#include <vector>
#include <string>
namespace gsi
{
class ClassBase;
class MethodBase;
}
namespace pya
{

View File

@ -25,91 +25,61 @@
#include "pyaMarshal.h"
#include "pyaUtils.h"
#include "pyaConvert.h"
#include "pyaSignalHandler.h"
#include "pyaStatusChangedListener.h"
#include "pya.h"
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiSignals.h"
#include "tlObject.h"
#include "tlLog.h"
namespace pya
{
// --------------------------------------------------------------------------
// Implementation of CallbackFunction
// Private classes
CallbackFunction::CallbackFunction (PythonRef pym, const gsi::MethodBase *m)
: mp_method (m)
/**
* @brief An adaptor class for the callback mechanism
*/
class Callee
: public gsi::Callee
{
// We have a problem here with cyclic references. Bound instances methods can
// create reference cycles if their target objects somehow points back to us
// (or worse, to some parent of us, i.e. inside a QWidget hierarchy).
// A solution is to take a bound instance method apart and store a weak
// reference to self plus a real reference to the function.
public:
/**
* @brief Constructor for a Callee object pointing the to given Python object
*/
Callee (PYAObjectBase *obj);
if (pym && PyMethod_Check (pym.get ()) && PyMethod_Self (pym.get ()) != NULL) {
/**
* @brief Destructor
*/
~Callee ();
m_weak_self = PythonRef (PyWeakref_NewRef (PyMethod_Self (pym.get ()), NULL));
m_callable = PythonRef (PyMethod_Function (pym.get ()), false /* borrowed ref */);
#if PY_MAJOR_VERSION < 3
m_class = PythonRef (PyMethod_Class (pym.get ()), false /* borrowed ref */);
#endif
/**
* @brief Adds a callback (given by the CallbackFunction)
* This method returns a callback ID which can be used to register the callback
* at an GSI object.
*/
int add_callback (const CallbackFunction &vf);
} else {
m_callable = pym;
}
}
/**
* @brief Clears all callbacks registered
*/
void clear_callbacks ();
const gsi::MethodBase *CallbackFunction::method () const
{
return mp_method;
}
/**
* @brief Implementation of the Callee interface
*/
virtual void call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const;
PythonRef CallbackFunction::callable () const
{
if (m_callable && m_weak_self) {
PyObject *self = PyWeakref_GetObject (m_weak_self.get ());
if (self == Py_None) {
// object expired - no callback possible
return PythonRef ();
}
#if PY_MAJOR_VERSION < 3
return PythonRef (PyMethod_New (m_callable.get (), self, m_class.get ()));
#else
return PythonRef (PyMethod_New (m_callable.get (), self));
#endif
} else {
return m_callable;
}
}
bool CallbackFunction::is_instance_method () const
{
return m_callable && m_weak_self;
}
PyObject *CallbackFunction::self_ref () const
{
return PyWeakref_GetObject (m_weak_self.get ());
}
PyObject *CallbackFunction::callable_ref () const
{
return m_callable.get ();
}
bool CallbackFunction::operator== (const CallbackFunction &other) const
{
if (is_instance_method () != other.is_instance_method ()) {
return false;
}
if (m_weak_self) {
if (self_ref () != other.self_ref ()) {
return false;
}
}
return callable_ref () == other.callable_ref ();
}
private:
PYAObjectBase *mp_obj;
std::vector<CallbackFunction> m_cbfuncs;
};
// --------------------------------------------------------------------------
// Implementation of Callee
@ -196,135 +166,12 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
}
}
// --------------------------------------------------------------------------
// Implementation of SignalHandler
SignalHandler::SignalHandler ()
{
// .. nothing yet ..
}
SignalHandler::~SignalHandler ()
{
clear ();
}
void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
{
PYTHON_BEGIN_EXEC
tl::Heap heap;
int args_avail = int (std::distance (meth->begin_arguments (), meth->end_arguments ()));
PythonRef argv (PyTuple_New (args_avail));
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ());
}
// NOTE: in case one event handler deletes the object, it's safer to first collect the handlers and
// then call them.
std::vector<PythonRef> callables;
callables.reserve (m_cbfuncs.size ());
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
callables.push_back (c->callable ());
}
PythonRef result;
for (std::vector<PythonRef>::const_iterator c = callables.begin (); c != callables.end (); ++c) {
// determine the number of arguments required
int arg_count = args_avail;
if (args_avail > 0) {
PythonRef fc (PyObject_GetAttrString (c->get (), "__code__"));
if (fc) {
PythonRef ac (PyObject_GetAttrString (fc.get (), "co_argcount"));
if (ac) {
arg_count = python2c<int> (ac.get ());
if (PyObject_HasAttrString (c->get (), "__self__")) {
arg_count -= 1;
}
}
}
}
// use less arguments if applicable
if (arg_count == 0) {
result = PythonRef (PyObject_CallObject (c->get (), NULL));
} else if (arg_count < args_avail) {
PythonRef argv_less (PyTuple_GetSlice (argv.get (), 0, arg_count));
result = PythonRef (PyObject_CallObject (c->get (), argv_less.get ()));
} else {
result = PythonRef (PyObject_CallObject (c->get (), argv.get ()));
}
if (! result) {
check_error ();
}
}
push_arg (meth->ret_type (), ret, result.get (), heap);
// a Python callback must not leave temporary objects
tl_assert (heap.empty ());
PYTHON_END_EXEC
}
void SignalHandler::add (PyObject *callable)
{
remove (callable);
m_cbfuncs.push_back (CallbackFunction (PythonPtr (callable), 0));
}
void SignalHandler::remove (PyObject *callable)
{
// To avoid cyclic references, the CallbackFunction holder is employed. However, the
// "true" callable no longer is the original one. Hence, we need to do a strict compare
// against the effective one.
CallbackFunction cbref (PythonPtr (callable), 0);
for (std::vector<CallbackFunction>::iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
if (*c == cbref) {
m_cbfuncs.erase (c);
break;
}
}
}
void SignalHandler::clear ()
{
m_cbfuncs.clear ();
}
void SignalHandler::assign (const SignalHandler *other)
{
m_cbfuncs = other->m_cbfuncs;
}
// --------------------------------------------------------------------------
// Implementation of StatusChangedListener
StatusChangedListener::StatusChangedListener (PYAObjectBase *pya_object)
: mp_pya_object (pya_object)
{
// .. nothing yet ..
}
void
StatusChangedListener::object_status_changed (gsi::ObjectBase::StatusEventType type)
{
mp_pya_object->object_status_changed (type);
}
// --------------------------------------------------------------------------
// Implementation of PYAObjectBase
PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl)
: m_listener (this),
m_callee (this),
: m_listener (new pya::StatusChangedListener (this)),
m_callee (new pya::Callee (this)),
m_cls_decl (_cls_decl),
m_obj (0),
m_owned (false),
@ -361,32 +208,24 @@ PYAObjectBase::~PYAObjectBase ()
}
void
PYAObjectBase::object_status_changed (gsi::ObjectBase::StatusEventType type)
PYAObjectBase::object_destroyed ()
{
if (type == gsi::ObjectBase::ObjectDestroyed) {
// This may happen outside the Python interpreter, so we safeguard ourselves against this.
// In this case, we may encounter a memory leak, but there is little we can do
// against this and it will happen in the application teardown anyway.
if (PythonInterpreter::instance ()) {
// This may happen outside the Python interpreter, so we safeguard ourselves against this.
// In this case, we may encounter a memory leak, but there is little we can do
// against this and it will happen in the application teardown anyway.
if (PythonInterpreter::instance ()) {
bool prev_owner = m_owned;
bool prev_owner = m_owned;
m_destroyed = true; // NOTE: must be set before detach!
m_destroyed = true; // NOTE: must be set before detach!
detach ();
// NOTE: this may delete "this"!
if (!prev_owner) {
Py_DECREF (this);
}
detach ();
// NOTE: this may delete "this"!
if (!prev_owner) {
Py_DECREF (this);
}
} else if (type == gsi::ObjectBase::ObjectKeep) {
keep_internal ();
} else if (type == gsi::ObjectBase::ObjectRelease) {
release ();
}
}
@ -446,7 +285,7 @@ PYAObjectBase::detach ()
if (! m_destroyed && cls && cls->is_managed ()) {
gsi::ObjectBase *gsi_object = cls->gsi_object (m_obj, false);
if (gsi_object) {
gsi_object->status_changed_event ().remove (&m_listener, &StatusChangedListener::object_status_changed);
gsi_object->status_changed_event ().remove (m_listener.get (), &StatusChangedListener::object_status_changed);
}
}
@ -485,7 +324,7 @@ PYAObjectBase::set (void *obj, bool owned, bool const_ref, bool can_destroy)
if (gsi_object->already_kept ()) {
keep_internal ();
}
gsi_object->status_changed_event ().add (&m_listener, &StatusChangedListener::object_status_changed);
gsi_object->status_changed_event ().add (m_listener.get (), &StatusChangedListener::object_status_changed);
}
if (!m_owned) {
@ -586,8 +425,8 @@ PYAObjectBase::initialize_callbacks ()
const char *nstr = (*m)->primary_name ().c_str ();
py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr);
int id = m_callee.add_callback (CallbackFunction (py_attr, *m));
(*m)->set_callback (m_obj, gsi::Callback (id, &m_callee, (*m)->argsize (), (*m)->retsize ()));
int id = m_callee->add_callback (CallbackFunction (py_attr, *m));
(*m)->set_callback (m_obj, gsi::Callback (id, m_callee.get (), (*m)->argsize (), (*m)->retsize ()));
}
@ -671,7 +510,7 @@ PYAObjectBase::detach_callbacks ()
}
}
m_callee.clear_callbacks ();
m_callee->clear_callbacks ();
}
void

View File

@ -24,152 +24,28 @@
#ifndef _HDR_pyaObject
#define _HDR_pyaObject
#include "Python.h"
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiSignals.h"
#include "tlObject.h"
#include <Python.h>
#include "pyaRefs.h"
#include "pyaCommon.h"
#include <vector>
#include <map>
#include <memory>
namespace gsi
{
class ClassBase;
class MethodBase;
}
namespace pya
{
class PYAObjectBase;
/**
* @brief A storage object for a function to callback
*/
struct CallbackFunction
{
CallbackFunction (PythonRef pym, const gsi::MethodBase *m);
PythonRef callable () const;
const gsi::MethodBase *method () const;
bool operator== (const CallbackFunction &other) const;
private:
PythonRef m_callable;
PythonRef m_weak_self;
PythonRef m_class;
const gsi::MethodBase *mp_method;
PyObject *self_ref () const;
PyObject *callable_ref () const;
bool is_instance_method () const;
};
/**
* @brief An adaptor class for the callback mechanism
*/
class Callee
: public gsi::Callee
{
public:
/**
* @brief Constructor for a Callee object pointing the to given Python object
*/
Callee (PYAObjectBase *obj);
/**
* @brief Destructor
*/
~Callee ();
/**
* @brief Adds a callback (given by the CallbackFunction)
* This method returns a callback ID which can be used to register the callback
* at an GSI object.
*/
int add_callback (const CallbackFunction &vf);
/**
* @brief Clears all callbacks registered
*/
void clear_callbacks ();
/**
* @brief Implementation of the Callee interface
*/
virtual void call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const;
private:
PYAObjectBase *mp_obj;
std::vector<CallbackFunction> m_cbfuncs;
};
/**
* @brief The signal handler abstraction
*
* This class implements the signal handler that interfaces to GSI's signal system
*/
class SignalHandler
: public gsi::SignalHandler
{
public:
/**
* @brief Constructor
*/
SignalHandler ();
/**
* @brief Destructor
*/
~SignalHandler ();
/**
* @brief Implementation of the callback interface
*/
virtual void call (const gsi::MethodBase *method, gsi::SerialArgs &args, gsi::SerialArgs &ret) const;
/**
* @brief Adds a callable to the list of targets
*/
void add (PyObject *callable);
/**
* @brief Removes a callable from the list of targets
*/
void remove (PyObject *callable);
/**
* @brief Clears the list of callables
*/
void clear ();
/**
* @brief Assign another handler to this
*/
void assign (const SignalHandler *other);
private:
std::vector<CallbackFunction> m_cbfuncs;
};
/**
* @brief A helper object to forward status changed events to a Python object
* This object is used to connect the events to the Python object. Unfortunately,
* PYAObjectBase cannot be derived from tl::Object directly since in that case,
* tl::Object will be placed before PyObject in the memory layout.
*/
class StatusChangedListener
: public tl::Object
{
public:
StatusChangedListener (PYAObjectBase *pya_object);
void object_status_changed (gsi::ObjectBase::StatusEventType type);
PYAObjectBase *pya_object () const
{
return mp_pya_object;
}
private:
PYAObjectBase *mp_pya_object;
};
class SignalHandler;
class Callee;
class StatusChangedListener;
/**
* @brief The Python object representing a GSI object
@ -290,22 +166,6 @@ public:
return m_owned;
}
/**
* @brief The callee interface
*/
Callee &callee ()
{
return m_callee;
}
/**
* @brief The callee interface (const pointer)
*/
const Callee &callee () const
{
return m_callee;
}
/**
* @brief Returns the signal handler for the signal given by "meth"
* If a signal handler was already present, the existing object is returned.
@ -326,9 +186,11 @@ private:
void detach_callbacks ();
void initialize_callbacks ();
void object_destroyed ();
void keep_internal ();
StatusChangedListener m_listener;
Callee m_callee;
std::auto_ptr<StatusChangedListener> m_listener;
std::auto_ptr<Callee> m_callee;
const gsi::ClassBase *m_cls_decl;
void *m_obj;
bool m_owned : 1;
@ -336,9 +198,6 @@ private:
bool m_destroyed : 1;
bool m_can_destroy : 1;
std::map <const gsi::MethodBase *, pya::SignalHandler> m_signal_table;
void object_status_changed (gsi::ObjectBase::StatusEventType type);
void keep_internal ();
};
}

View File

@ -0,0 +1,219 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pyaSignalHandler.h"
#include "pya.h"
#include "pyaMarshal.h"
#include "pyaConvert.h"
#include "pyaUtils.h"
namespace pya
{
// --------------------------------------------------------------------------
// Implementation of CallbackFunction
CallbackFunction::CallbackFunction (PythonRef pym, const gsi::MethodBase *m)
: mp_method (m)
{
// We have a problem here with cyclic references. Bound instances methods can
// create reference cycles if their target objects somehow points back to us
// (or worse, to some parent of us, i.e. inside a QWidget hierarchy).
// A solution is to take a bound instance method apart and store a weak
// reference to self plus a real reference to the function.
if (pym && PyMethod_Check (pym.get ()) && PyMethod_Self (pym.get ()) != NULL) {
m_weak_self = PythonRef (PyWeakref_NewRef (PyMethod_Self (pym.get ()), NULL));
m_callable = PythonRef (PyMethod_Function (pym.get ()), false /* borrowed ref */);
#if PY_MAJOR_VERSION < 3
m_class = PythonRef (PyMethod_Class (pym.get ()), false /* borrowed ref */);
#endif
} else {
m_callable = pym;
}
}
const gsi::MethodBase *CallbackFunction::method () const
{
return mp_method;
}
PythonRef CallbackFunction::callable () const
{
if (m_callable && m_weak_self) {
PyObject *self = PyWeakref_GetObject (m_weak_self.get ());
if (self == Py_None) {
// object expired - no callback possible
return PythonRef ();
}
#if PY_MAJOR_VERSION < 3
return PythonRef (PyMethod_New (m_callable.get (), self, m_class.get ()));
#else
return PythonRef (PyMethod_New (m_callable.get (), self));
#endif
} else {
return m_callable;
}
}
bool CallbackFunction::is_instance_method () const
{
return m_callable && m_weak_self;
}
PyObject *CallbackFunction::self_ref () const
{
return PyWeakref_GetObject (m_weak_self.get ());
}
PyObject *CallbackFunction::callable_ref () const
{
return m_callable.get ();
}
bool CallbackFunction::operator== (const CallbackFunction &other) const
{
if (is_instance_method () != other.is_instance_method ()) {
return false;
}
if (m_weak_self) {
if (self_ref () != other.self_ref ()) {
return false;
}
}
return callable_ref () == other.callable_ref ();
}
// --------------------------------------------------------------------------
// Implementation of SignalHandler
SignalHandler::SignalHandler ()
{
// .. nothing yet ..
}
SignalHandler::~SignalHandler ()
{
clear ();
}
void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
{
PYTHON_BEGIN_EXEC
tl::Heap heap;
int args_avail = int (std::distance (meth->begin_arguments (), meth->end_arguments ()));
PythonRef argv (PyTuple_New (args_avail));
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ());
}
// NOTE: in case one event handler deletes the object, it's safer to first collect the handlers and
// then call them.
std::vector<PythonRef> callables;
callables.reserve (m_cbfuncs.size ());
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
callables.push_back (c->callable ());
}
PythonRef result;
for (std::vector<PythonRef>::const_iterator c = callables.begin (); c != callables.end (); ++c) {
// determine the number of arguments required
int arg_count = args_avail;
if (args_avail > 0) {
PythonRef fc (PyObject_GetAttrString (c->get (), "__code__"));
if (fc) {
PythonRef ac (PyObject_GetAttrString (fc.get (), "co_argcount"));
if (ac) {
arg_count = python2c<int> (ac.get ());
if (PyObject_HasAttrString (c->get (), "__self__")) {
arg_count -= 1;
}
}
}
}
// use less arguments if applicable
if (arg_count == 0) {
result = PythonRef (PyObject_CallObject (c->get (), NULL));
} else if (arg_count < args_avail) {
PythonRef argv_less (PyTuple_GetSlice (argv.get (), 0, arg_count));
result = PythonRef (PyObject_CallObject (c->get (), argv_less.get ()));
} else {
result = PythonRef (PyObject_CallObject (c->get (), argv.get ()));
}
if (! result) {
check_error ();
}
}
push_arg (meth->ret_type (), ret, result.get (), heap);
// a Python callback must not leave temporary objects
tl_assert (heap.empty ());
PYTHON_END_EXEC
}
void SignalHandler::add (PyObject *callable)
{
remove (callable);
m_cbfuncs.push_back (CallbackFunction (PythonPtr (callable), 0));
}
void SignalHandler::remove (PyObject *callable)
{
// To avoid cyclic references, the CallbackFunction holder is employed. However, the
// "true" callable no longer is the original one. Hence, we need to do a strict compare
// against the effective one.
CallbackFunction cbref (PythonPtr (callable), 0);
for (std::vector<CallbackFunction>::iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
if (*c == cbref) {
m_cbfuncs.erase (c);
break;
}
}
}
void SignalHandler::clear ()
{
m_cbfuncs.clear ();
}
void SignalHandler::assign (const SignalHandler *other)
{
m_cbfuncs = other->m_cbfuncs;
}
}

View File

@ -0,0 +1,108 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HDR_pyaSignalHandler
#define _HDR_pyaSignalHandler
#include <Python.h>
#include "pyaRefs.h"
#include "gsiSignals.h"
namespace pya
{
/**
* @brief A storage object for a function to callback
*/
struct CallbackFunction
{
CallbackFunction (PythonRef pym, const gsi::MethodBase *m);
PythonRef callable () const;
const gsi::MethodBase *method () const;
bool operator== (const CallbackFunction &other) const;
private:
PythonRef m_callable;
PythonRef m_weak_self;
PythonRef m_class;
const gsi::MethodBase *mp_method;
PyObject *self_ref () const;
PyObject *callable_ref () const;
bool is_instance_method () const;
};
/**
* @brief The signal handler abstraction
*
* This class implements the signal handler that interfaces to GSI's signal system
*/
class SignalHandler
: public gsi::SignalHandler
{
public:
/**
* @brief Constructor
*/
SignalHandler ();
/**
* @brief Destructor
*/
~SignalHandler ();
/**
* @brief Implementation of the callback interface
*/
virtual void call (const gsi::MethodBase *method, gsi::SerialArgs &args, gsi::SerialArgs &ret) const;
/**
* @brief Adds a callable to the list of targets
*/
void add (PyObject *callable);
/**
* @brief Removes a callable from the list of targets
*/
void remove (PyObject *callable);
/**
* @brief Clears the list of callables
*/
void clear ();
/**
* @brief Assign another handler to this
*/
void assign (const SignalHandler *other);
private:
std::vector<CallbackFunction> m_cbfuncs;
};
}
#endif

View File

@ -0,0 +1,49 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pyaStatusChangedListener.h"
#include "pyaObject.h"
namespace pya
{
// --------------------------------------------------------------------------
// Implementation of StatusChangedListener
StatusChangedListener::StatusChangedListener (PYAObjectBase *pya_object)
: mp_pya_object (pya_object)
{
// .. nothing yet ..
}
void
StatusChangedListener::object_status_changed (gsi::ObjectBase::StatusEventType type)
{
if (type == gsi::ObjectBase::ObjectDestroyed) {
mp_pya_object->object_destroyed ();
} else if (type == gsi::ObjectBase::ObjectKeep) {
mp_pya_object->keep_internal ();
} else if (type == gsi::ObjectBase::ObjectRelease) {
mp_pya_object->release ();
}
}
}

View File

@ -0,0 +1,64 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HDR_pyaStatusChangedListener
#define _HDR_pyaStatusChangedListener
#include <Python.h>
#include "pyaRefs.h"
#include "gsiSignals.h"
#include "gsiObject.h"
namespace pya
{
class PYAObjectBase;
/**
* @brief A helper object to forward status changed events to a Python object
* This object is used to connect the events to the Python object. Unfortunately,
* PYAObjectBase cannot be derived from tl::Object directly since in that case,
* tl::Object will be placed before PyObject in the memory layout.
*/
class StatusChangedListener
: public tl::Object
{
public:
StatusChangedListener (PYAObjectBase *pya_object);
void object_status_changed (gsi::ObjectBase::StatusEventType type);
PYAObjectBase *pya_object () const
{
return mp_pya_object;
}
private:
PYAObjectBase *mp_pya_object;
};
}
#endif

View File

@ -24,9 +24,47 @@
#ifndef _HDR_pyaUtils
#define _HDR_pyaUtils
#include "tlScriptError.h"
namespace pya
{
/**
* Some helper macros that translate C++ exceptions into Python errors
*/
#define PYA_TRY \
{ \
try {
#define PYA_CATCH(where) \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
std::string msg = std::string(ex.what ()) + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (tl::Exception &ex) { \
std::string msg; \
msg = ex.msg () + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (...) { \
std::string msg = tl::to_string (QObject::tr ("Unspecific exception in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} \
}
#define PYA_CATCH_ANYWHERE \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.what ()); \
} catch (tl::Exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.msg ().c_str ()); \
} catch (...) { \
PyErr_SetString (PyExc_RuntimeError, tl::to_string (QObject::tr ("Unspecific exception in ")).c_str ()); \
} \
}
/**
* @brief Turn Python errors into C++ exceptions
*/

View File

@ -49,9 +49,9 @@ QT = core
# - GSI (generic scripting interface)
# - TL (basic toolkit)
# - PYA (Python binding for GSI)
INCLUDEPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/gsi/gsi $$INC/pya/pya
DEPENDPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/gsi/gsi $$INC/pya/pya
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_gsi -lklayout_pya
INCLUDEPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/pya/pya
DEPENDPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/pya/pya
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_pya
# Also include DB as this is our sample
INCLUDEPATH += $$INC/db/db

View File

@ -31,7 +31,10 @@
*/
#include <Python.h>
#include "pyaModule.h"
#include "pyaUtils.h"
#include "gsi.h"
#include "gsiExpression.h"