Fixed a segfault on help(pya.Box)

Needed to refactor the class hierarchy of the Python classes.
Basically the module specific base class was removed as it does
not provide any benefit. The object layout of the PyObject
specialization was modified such that the payload is attached
to the end. This is compatible with the hidden extensions
which Python adds to normal objects.
This commit is contained in:
Matthias Koefferlein 2018-07-18 23:48:40 +02:00
parent aca209c095
commit 5fe0aca9c7
8 changed files with 114 additions and 99 deletions

View File

@ -291,7 +291,7 @@ tl::Variant python2c_func<tl::Variant>::operator() (PyObject *rval)
const gsi::ClassBase *cls = PythonModule::cls_for_type (Py_TYPE (rval));
if (cls) {
PYAObjectBase *p = (PYAObjectBase *) rval;
PYAObjectBase *p = PYAObjectBase::from_pyobject (rval);
// employ the tl::Variant binding capabilities of the Expression binding to derive the
// variant value.
@ -359,9 +359,8 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype)
* object turns into a non-const one. This may be confusing but provides a certain level
* of "constness", at least until there is another non-const reference for that object.
*/
void correct_constness (PyObject *obj, bool const_required)
void correct_constness (PYAObjectBase *p, bool const_required)
{
PYAObjectBase *p = (PYAObjectBase *) obj;
if (p->const_ref () && ! const_required) {
// promote to non-const object
p->set_const_ref (false);
@ -421,20 +420,21 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
tl_assert (type != NULL);
// create a instance and copy the value
PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0);
new (new_object) PYAObjectBase (clsact);
PyObject *new_pyobject = type->tp_alloc (type, 0);
PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject);
new (new_object) PYAObjectBase (clsact, new_pyobject);
clsact->assign (new_object->obj (), obj);
return new_object;
return new_pyobject;
} else if (pya_object) {
// we have a that is located in C++ space but is supposed to get attached
// a Python object. If it already has, we simply return a reference to this
Py_INCREF (pya_object);
Py_INCREF (pya_object->py_object ());
correct_constness (pya_object, is_const);
return pya_object;
return pya_object->py_object ();
} else {
@ -442,10 +442,11 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
tl_assert (type != NULL);
// create a instance and copy the value
PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0);
new (new_object) PYAObjectBase (clsact);
PyObject *new_pyobject = type->tp_alloc (type, 0);
PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject);
new (new_object) PYAObjectBase (clsact, new_pyobject);
new_object->set (obj, pass_obj, is_const, can_destroy);
return new_object;
return new_pyobject;
}
}

View File

@ -352,7 +352,7 @@ template <class T> struct python2c_func<T &>
tl_assert (cls_decl != 0);
tl_assert (is_derived_from (cls_decl, typeid (T)));
PYAObjectBase *p = (PYAObjectBase *) (rval);
PYAObjectBase *p = PYAObjectBase::from_pyobject (rval);
return *((T *)p->obj ());
}
};

View File

@ -187,7 +187,7 @@ struct get_boxed_value_func
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Passing an object to pointer or reference requires a boxed type (pya.%s)")), bt->name ()));
}
PYAObjectBase *p = (PYAObjectBase *) arg;
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
gsi::Value *bo = reinterpret_cast<gsi::Value *> (p->obj ());
if (bo) {
*ret = bo->value ().template morph<R> ().native_ptr ();
@ -389,7 +389,7 @@ struct writer<gsi::ObjectType>
if (cls_decl->is_derived_from (atype.cls ())) {
PYAObjectBase *p = (PYAObjectBase *) (arg);
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
if (cls_decl->adapted_type_info ()) {
// resolved adapted type
@ -400,7 +400,7 @@ struct writer<gsi::ObjectType>
} else if (cls_decl->can_convert_to (atype.cls ())) {
PYAObjectBase *p = (PYAObjectBase *) (arg);
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
// We can convert objects for cref and cptr, but ownership over these objects is not transferred.
// Hence we have to keep them on the heap.
@ -421,7 +421,7 @@ struct writer<gsi::ObjectType>
if (cls_decl->is_derived_from (atype.cls ())) {
PYAObjectBase *p = (PYAObjectBase *) (arg);
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
if (cls_decl->adapted_type_info ()) {
// resolved adapted type
@ -432,7 +432,7 @@ struct writer<gsi::ObjectType>
} else if (cls_decl->can_convert_to (atype.cls ())) {
PYAObjectBase *p = (PYAObjectBase *) (arg);
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
aa->write<void *> (atype.cls ()->create_obj_from (cls_decl, p->obj ()));
} else {
@ -471,7 +471,7 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl
template <class R>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
{
if (arg.is_ref ()) {
*ret = c2python (rr->template read<R &> (*heap));
@ -506,7 +506,7 @@ struct reader
template <>
struct reader<void *>
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
{
tl_assert (! arg.is_cref ());
tl_assert (! arg.is_ref ());

View File

@ -575,9 +575,9 @@ static std::string extract_python_name (const std::string &name)
static void
pya_object_deallocate (PyObject *self)
{
PYAObjectBase *p = (PYAObjectBase *) self;
PYAObjectBase *p = PYAObjectBase::from_pyobject (self);
p->~PYAObjectBase ();
Py_TYPE (self)->tp_free ((PyObject*)self);
Py_TYPE (self)->tp_free (self);
}
/**
@ -602,9 +602,10 @@ static PyObject *
pya_object_new (PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
{
// create the object
PYAObjectBase *self = (PYAObjectBase *) type->tp_alloc (type, 0);
new (self) PYAObjectBase (PythonModule::cls_for_type (type));
return (PyObject *) self;
PyObject *self_pyobject = type->tp_alloc (type, 0);
PYAObjectBase *self = PYAObjectBase::from_pyobject_unsafe (self_pyobject);
new (self) PYAObjectBase (PythonModule::cls_for_type (type), self_pyobject);
return self_pyobject;
}
// --------------------------------------------------------------------------
@ -619,7 +620,7 @@ method_name_from_id (int mid, PyObject *self)
const gsi::ClassBase *cls_decl = 0;
if (! PyType_Check (self)) {
PYAObjectBase *p = (PYAObjectBase *) self;
PYAObjectBase *p = PYAObjectBase::from_pyobject (self);
cls_decl = p->cls_decl ();
} else {
cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
@ -652,7 +653,7 @@ property_name_from_id (int mid, PyObject *self)
const gsi::ClassBase *cls_decl = 0;
if (! PyType_Check (self)) {
PYAObjectBase *p = (PYAObjectBase *) self;
PYAObjectBase *p = PYAObjectBase::from_pyobject (self);
cls_decl = p->cls_decl ();
} else {
cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
@ -684,7 +685,7 @@ get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::Meth
if (meth->ret_type ().is_iter ()) {
gsi::IterAdaptorAbstractBase *iter = (gsi::IterAdaptorAbstractBase *) retlist.read<gsi::IterAdaptorAbstractBase *> (heap);
ret = (PyObject *) PYAIteratorObject::create (self, iter, &meth->ret_type ());
ret = (PyObject *) PYAIteratorObject::create (self ? self->py_object () : 0, iter, &meth->ret_type ());
} else {
@ -702,7 +703,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
PYAObjectBase *p = 0;
if (! PyType_Check (self)) {
p = (PYAObjectBase *) self;
p = PYAObjectBase::from_pyobject (self);
cls_decl = p->cls_decl ();
} else {
cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
@ -869,10 +870,11 @@ object_dup (PyObject *self, PyObject *args)
throw tl::Exception (tl::to_string (tr ("No copy constructor provided for class '%s'")), cls_decl_self->name ());
}
PYAObjectBase *new_object = (PYAObjectBase *) Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0);
PyObject *new_object = Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0);
PythonRef obj (new_object);
new (new_object) PYAObjectBase (cls_decl_self);
new_object->set (cls_decl_self->clone (((PYAObjectBase *) self)->obj ()), true, false, false);
PYAObjectBase *new_pya_base = PYAObjectBase::from_pyobject_unsafe (new_object);
new (new_pya_base) PYAObjectBase (cls_decl_self, new_object);
new_pya_base->set (cls_decl_self->clone (PYAObjectBase::from_pyobject (self)->obj ()), true, false, false);
return obj.release ();
}
@ -901,7 +903,7 @@ object_assign (PyObject *self, PyObject *args)
throw tl::Exception (tl::to_string (tr ("No assignment provided for class '%s'")), cls_decl_self->name ());
}
cls_decl_self->assign (((PYAObjectBase *) self)->obj (), ((PYAObjectBase *) src)->obj ());
cls_decl_self->assign ((PYAObjectBase::from_pyobject (self))->obj (), (PYAObjectBase::from_pyobject (src))->obj ());
Py_INCREF (self);
return self;
@ -997,7 +999,7 @@ object_create (PyObject *self, PyObject *args)
return NULL;
}
((PYAObjectBase *) self)->obj ();
(PYAObjectBase::from_pyobject (self))->obj ();
Py_RETURN_NONE;
}
@ -1011,7 +1013,7 @@ object_release (PyObject *self, PyObject *args)
return NULL;
}
((PYAObjectBase *) self)->release ();
(PYAObjectBase::from_pyobject (self))->release ();
Py_RETURN_NONE;
}
@ -1025,7 +1027,7 @@ object_keep (PyObject *self, PyObject *args)
return NULL;
}
((PYAObjectBase *) self)->keep ();
(PYAObjectBase::from_pyobject (self))->keep ();
Py_RETURN_NONE;
}
@ -1039,7 +1041,7 @@ object_destroy (PyObject *self, PyObject *args)
return NULL;
}
((PYAObjectBase *) self)->destroy ();
(PYAObjectBase::from_pyobject (self))->destroy ();
Py_RETURN_NONE;
}
@ -1053,7 +1055,7 @@ object_destroyed (PyObject *self, PyObject *args)
return NULL;
}
return c2python (((PYAObjectBase *) self)->destroyed ());
return c2python (PYAObjectBase::from_pyobject (self)->destroyed ());
}
/**
@ -1066,7 +1068,7 @@ object_is_const (PyObject *self, PyObject *args)
return NULL;
}
return c2python (((PYAObjectBase *) self)->const_ref ());
return c2python (PYAObjectBase::from_pyobject (self)->const_ref ());
}
static PyObject *
@ -1112,7 +1114,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args)
PYAObjectBase *p = 0;
if (! PyType_Check (self)) {
// non-static method
p = (PYAObjectBase *) self;
p = PYAObjectBase::from_pyobject (self);
}
tl::Heap heap;
@ -1666,7 +1668,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
{
PYA_TRY
PYAObjectBase *p = (PYAObjectBase *) self;
PYAObjectBase *p = PYAObjectBase::from_pyobject (self);
// delete any object which we may have already
if (p->is_attached ()) {
@ -1888,7 +1890,7 @@ property_getter_impl (int mid, PyObject *self)
PYAObjectBase *p = 0;
if (! PyType_Check (self)) {
p = (PYAObjectBase *) self;
p = PYAObjectBase::from_pyobject (self);
cls_decl = p->cls_decl ();
} else {
cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
@ -1969,7 +1971,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
PYAObjectBase *p = 0;
if (! PyType_Check (self)) {
p = (PYAObjectBase *) self;
p = PYAObjectBase::from_pyobject (self);
cls_decl = p->cls_decl ();
} else {
cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
@ -2156,7 +2158,6 @@ PythonModule::~PythonModule ()
// the Python objects were probably deleted by Python itself as it exited -
// don't try to delete them again.
mp_module.release ();
mp_base_class.release ();
while (!m_methods_heap.empty ()) {
delete m_methods_heap.back ();
@ -2339,36 +2340,6 @@ PythonModule::make_classes (const char *mod_name)
PyObject_SetAttrString (module, "__doc__", PythonRef (c2python (m_mod_description)).get ());
// Create a (built-in) base class for all objects exposed by this module
m_base_class_name = m_mod_name + ".__Base";
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->tp_base = &PyBaseObject_Type;
base_class->tp_name = m_base_class_name.c_str ();
base_class->tp_basicsize = sizeof (PYAObjectBase);
base_class->tp_init = &pya_object_init;
base_class->tp_new = &pya_object_new;
base_class->tp_dealloc = (destructor) &pya_object_deallocate;
base_class->tp_setattro = PyObject_GenericSetAttr;
base_class->tp_getattro = PyObject_GenericGetAttr;
#if PY_MAJOR_VERSION < 3
base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES;
#else
base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
#endif
if (PyType_Ready (base_class) < 0) {
check_error ();
return;
}
PyModule_AddObject (module, "__Base", (PyObject *) base_class);
// Build a class for descriptors for static attributes
PYAStaticAttributeDescriptorObject::make_class (module);
@ -2434,15 +2405,17 @@ PythonModule::make_classes (const char *mod_name)
m_classes.push_back (c.operator-> ());
PythonRef bases (PyTuple_New (1));
PyObject *base = mp_base_class.get ();
PythonRef bases;
if (c->base () != 0) {
bases = PythonRef (PyTuple_New (1));
PyTypeObject *pt = PythonClassClientData::py_type (*c->base ());
tl_assert (pt != 0);
base = (PyObject *) pt;
PyObject *base = (PyObject *) pt;
Py_INCREF (base);
PyTuple_SetItem (bases.get (), 0, base);
} else {
bases = PythonRef (PyTuple_New (0));
}
Py_INCREF (base);
PyTuple_SetItem (bases.get (), 0, base);
PythonRef dict (PyDict_New ());
PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (m_mod_name)).get ());
@ -2459,9 +2432,18 @@ PythonModule::make_classes (const char *mod_name)
check_error ();
tl_assert (false);
}
// Customize
type->tp_basicsize += sizeof (PYAObjectBase);
type->tp_init = &pya_object_init;
type->tp_new = &pya_object_new;
type->tp_dealloc = (destructor) &pya_object_deallocate;
type->tp_setattro = PyObject_GenericSetAttr;
type->tp_getattro = PyObject_GenericGetAttr;
PythonClassClientData::initialize (*c, type);
tl_assert (cls_for_type (type) == c.operator-> ()); // @@@
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);

View File

@ -120,9 +120,7 @@ private:
std::vector<PyGetSetDef *> m_getseters_heap;
std::string m_mod_name, m_mod_description;
std::string m_base_class_name;
PythonRef mp_module;
PythonRef mp_base_class;
char *mp_mod_def;
static std::map<const gsi::MethodBase *, std::string> m_python_doc;

View File

@ -129,8 +129,8 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
PythonRef argv (PyTuple_New (arg4self + std::distance (meth->begin_arguments (), meth->end_arguments ())));
// Put self into first argument
PyTuple_SetItem (argv.get (), 0, mp_obj);
Py_INCREF (mp_obj);
PyTuple_SetItem (argv.get (), 0, mp_obj->py_object ());
Py_INCREF (mp_obj->py_object ());
// TODO: callbacks with default arguments?
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
@ -169,8 +169,9 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
// --------------------------------------------------------------------------
// Implementation of PYAObjectBase
PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl)
: m_listener (new pya::StatusChangedListener (this)),
PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl, PyObject *py_object)
: mp_py_object (py_object),
m_listener (new pya::StatusChangedListener (this)),
m_callee (new pya::Callee (this)),
m_cls_decl (_cls_decl),
m_obj (0),
@ -179,6 +180,7 @@ PYAObjectBase::PYAObjectBase(const gsi::ClassBase *_cls_decl)
m_destroyed (false),
m_can_destroy (false)
{
// .. nothing yet ..
}
PYAObjectBase::~PYAObjectBase ()
@ -223,7 +225,7 @@ PYAObjectBase::object_destroyed ()
// NOTE: this may delete "this"!
if (!prev_owner) {
Py_DECREF (this);
Py_DECREF (py_object ());
}
}
@ -246,7 +248,7 @@ PYAObjectBase::release ()
if (!m_owned) {
m_owned = true;
// NOTE: this may delete "this"! TODO: this should not happen. Can we assert that somehow?
Py_DECREF (this);
Py_DECREF (py_object ());
}
}
@ -254,7 +256,7 @@ void
PYAObjectBase::keep_internal ()
{
if (m_owned) {
Py_INCREF (this);
Py_INCREF (py_object ());
m_owned = false;
}
}
@ -328,7 +330,7 @@ PYAObjectBase::set (void *obj, bool owned, bool const_ref, bool can_destroy)
}
if (!m_owned) {
Py_INCREF (this);
Py_INCREF (py_object ());
}
}
@ -353,7 +355,7 @@ PYAObjectBase::initialize_callbacks ()
// TODO: caching appears to create some leaks ...
#if 1
PythonRef type_ref ((PyObject *) Py_TYPE (this), false /*borrowed*/);
PythonRef type_ref ((PyObject *) Py_TYPE (py_object ()), false /*borrowed*/);
// Locate the callback-enabled methods set by Python tpye object (pointer)
// NOTE: I'm not quite sure whether the type object pointer is a good key
@ -391,7 +393,7 @@ PYAObjectBase::initialize_callbacks ()
// TOOD: That may happen too often, i.e. if the Python class does not reimplement the virtual
// method, but the C++ class defines a method hook that the reimplementation can call.
// We don't want to produce a lot of overhead for the Qt classes here.
PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr);
PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr);
if (! py_attr) {
// because PyObject_GetAttrString left an error
@ -423,7 +425,7 @@ PYAObjectBase::initialize_callbacks ()
PythonRef py_attr;
const char *nstr = (*m)->primary_name ().c_str ();
py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr);
py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr);
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 ()));
@ -458,7 +460,7 @@ PYAObjectBase::initialize_callbacks ()
// TOOD: That may happen too often, i.e. if the Python class does not reimplement the virtual
// method, but the C++ class defines a method hook that the reimplementation can call.
// We don't want to produce a lot of overhead for the Qt classes here.
PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr);
PythonRef py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr);
if (! py_attr) {
// because PyObject_GetAttrString left an error
@ -471,7 +473,7 @@ PYAObjectBase::initialize_callbacks ()
// may create issues with callbacks during destruction (i.e. QWidget-destroyed signal)
if (! PyCFunction_Check (py_attr.get ())) {
PyObject *py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (this), nstr);
PyObject *py_attr = PyObject_GetAttrString ((PyObject *) Py_TYPE (py_object ()), nstr);
tl_assert (py_attr != NULL);
int id = m_callee.add_callback (CallbackFunction (py_attr, *m));
(*m)->set_callback (m_obj, gsi::Callback (id, &m_callee, (*m)->argsize (), (*m)->retsize ()));
@ -501,7 +503,7 @@ PYAObjectBase::clear_callbacks_cache ()
void
PYAObjectBase::detach_callbacks ()
{
PythonRef type_ref ((PyObject *) Py_TYPE (this), false /*borrowed*/);
PythonRef type_ref ((PyObject *) Py_TYPE (py_object ()), false /*borrowed*/);
callbacks_cache::iterator cb = s_callbacks_cache.find (type_ref);
if (cb != s_callbacks_cache.end ()) {

View File

@ -29,6 +29,8 @@
#include "pyaRefs.h"
#include "pyaCommon.h"
#include "tlAssert.h"
#include <vector>
#include <map>
#include <memory>
@ -50,23 +52,43 @@ class StatusChangedListener;
/**
* @brief The Python object representing a GSI object
*
* Note: the PYAObjectBase must be directly derived from PyObject so that
* a PyObject pointer can be cast to a PYAObjectBase pointer.
* NOTE: this memory block is attached to the actual structure
* and obtained by taking the last sizeof(PYAObjectBase) bytes.
* It's basically a connector between GSI objects and the Python
* objects.
*/
class PYA_PUBLIC PYAObjectBase
: public PyObject
{
public:
/**
* @brief Constructor - creates a new object for the given GSI class
*/
PYAObjectBase (const gsi::ClassBase *_cls_decl);
PYAObjectBase (const gsi::ClassBase *_cls_decl, PyObject *py_object);
/**
* @brief Destructor
*/
~PYAObjectBase ();
/**
* @brief Gets the PYAObjectBase pointer from a PyObject pointer
*/
static PYAObjectBase *from_pyobject (PyObject *py_object)
{
PYAObjectBase *pya_object = (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase));
tl_assert (pya_object->py_object () == py_object);
return pya_object;
}
/**
* @brief Gets the PYAObjectBase pointer from a PyObject pointer
* This version doesn't check anything.
*/
static PYAObjectBase *from_pyobject_unsafe (PyObject *py_object)
{
return (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase));
}
/**
* @brief Indicates that a C++ object is present
*/
@ -142,6 +164,14 @@ public:
m_const_ref = c;
}
/**
* @brief Gets the Python object for this bridge object
*/
PyObject *py_object () const
{
return mp_py_object;
}
/**
* @brief Returns the C++ object reference
*/
@ -189,6 +219,7 @@ private:
void object_destroyed ();
void keep_internal ();
PyObject *mp_py_object;
std::auto_ptr<StatusChangedListener> m_listener;
std::auto_ptr<Callee> m_callee;
const gsi::ClassBase *m_cls_decl;

View File

@ -2718,6 +2718,7 @@ class BasicTest(unittest.TestCase):
go = None
self.assertEqual(pya.GObject.g_inst_count(), gc)
# run unit tests
if __name__ == '__main__':
suite = unittest.TestSuite()