mirror of https://github.com/KLayout/klayout.git
Providing a different implementation of the multi-base mixin concept for Python to support multiple bases in Qt binding (e.g. QIODeviceBase)
This commit is contained in:
parent
202ac55200
commit
f15db66fc4
|
|
@ -116,6 +116,44 @@ public:
|
||||||
MethodTable (const gsi::ClassBase *cls_decl)
|
MethodTable (const gsi::ClassBase *cls_decl)
|
||||||
: m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl)
|
: m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl)
|
||||||
{
|
{
|
||||||
|
// signals are translated into the setters and getters
|
||||||
|
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||||
|
if ((*m)->is_signal ()) {
|
||||||
|
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||||
|
add_getter (syn->name, *m);
|
||||||
|
add_setter (syn->name, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first add getters and setters
|
||||||
|
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||||
|
if (! (*m)->is_callback ()) {
|
||||||
|
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||||
|
if (syn->is_getter) {
|
||||||
|
add_getter (syn->name, *m);
|
||||||
|
} else if (syn->is_setter) {
|
||||||
|
add_setter (syn->name, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then add normal methods - on name clash with properties make them a getter
|
||||||
|
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||||
|
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
||||||
|
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||||
|
if (! syn->is_getter && ! syn->is_setter) {
|
||||||
|
if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && find_property ((*m)->is_static (), syn->name).first) {
|
||||||
|
add_getter (syn->name, *m);
|
||||||
|
} else {
|
||||||
|
add_method (syn->name, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cls_decl->base ()) {
|
if (cls_decl->base ()) {
|
||||||
const MethodTable *base_mt = method_table_by_class (cls_decl->base ());
|
const MethodTable *base_mt = method_table_by_class (cls_decl->base ());
|
||||||
tl_assert (base_mt);
|
tl_assert (base_mt);
|
||||||
|
|
@ -369,24 +407,34 @@ private:
|
||||||
struct PythonClassClientData
|
struct PythonClassClientData
|
||||||
: public gsi::PerClassClientSpecificData
|
: public gsi::PerClassClientSpecificData
|
||||||
{
|
{
|
||||||
PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type)
|
PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static)
|
||||||
: py_type_object (_py_type), method_table (_cls)
|
: py_type_object (_py_type), py_type_object_static (_py_type_static), method_table (_cls)
|
||||||
{
|
{
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject *py_type_object;
|
PyTypeObject *py_type_object;
|
||||||
|
PyTypeObject *py_type_object_static;
|
||||||
MethodTable method_table;
|
MethodTable method_table;
|
||||||
|
|
||||||
static PyTypeObject *py_type (const gsi::ClassBase &cls_decl)
|
static PyTypeObject *py_type (const gsi::ClassBase &cls_decl, bool as_static)
|
||||||
{
|
{
|
||||||
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl.data (gsi::ClientIndex::Python));
|
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl.data (gsi::ClientIndex::Python));
|
||||||
return cd ? cd->py_type_object : 0;
|
return cd ? (as_static ? cd->py_type_object_static : cd->py_type_object) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type)
|
static void initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type, bool as_static)
|
||||||
{
|
{
|
||||||
cls_decl.set_data (gsi::ClientIndex::Python, new PythonClassClientData (&cls_decl, py_type));
|
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl.data (gsi::ClientIndex::Python));
|
||||||
|
if (cd) {
|
||||||
|
if (as_static) {
|
||||||
|
cd->py_type_object_static = py_type;
|
||||||
|
} else {
|
||||||
|
cd->py_type_object = py_type;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cls_decl.set_data (gsi::ClientIndex::Python, new PythonClassClientData (&cls_decl, as_static ? NULL : py_type, as_static ? py_type : NULL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2224,7 +2272,6 @@ PythonModule::init (const char *mod_name, const char *description)
|
||||||
// do some checks before we create the module
|
// do some checks before we create the module
|
||||||
tl_assert (mod_name != 0);
|
tl_assert (mod_name != 0);
|
||||||
tl_assert (mp_module.get () == 0);
|
tl_assert (mp_module.get () == 0);
|
||||||
check (mod_name);
|
|
||||||
|
|
||||||
m_mod_name = pymod_name + "." + mod_name;
|
m_mod_name = pymod_name + "." + mod_name;
|
||||||
m_mod_description = description;
|
m_mod_description = description;
|
||||||
|
|
@ -2269,7 +2316,6 @@ PythonModule::init (const char *mod_name, PyObject *module)
|
||||||
{
|
{
|
||||||
// do some checks before we create the module
|
// do some checks before we create the module
|
||||||
tl_assert (mp_module.get () == 0);
|
tl_assert (mp_module.get () == 0);
|
||||||
check (mod_name);
|
|
||||||
|
|
||||||
m_mod_name = mod_name;
|
m_mod_name = mod_name;
|
||||||
mp_module = PythonRef (module);
|
mp_module = PythonRef (module);
|
||||||
|
|
@ -2325,41 +2371,6 @@ PythonModule::python_doc (const gsi::MethodBase *method)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PythonModule::check (const char *mod_name)
|
|
||||||
{
|
|
||||||
if (! mod_name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the new classes are self-contained within this module
|
|
||||||
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
|
|
||||||
|
|
||||||
if (c->module () != mod_name) {
|
|
||||||
// don't handle classes outside this module
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PythonClassClientData::py_type (*c)) {
|
|
||||||
// don't handle classes twice
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All child classes must originate from this module or be known already
|
|
||||||
for (tl::weak_collection<gsi::ClassBase>::const_iterator cc = c->begin_child_classes (); cc != c->end_child_classes (); ++cc) {
|
|
||||||
if (! PythonClassClientData::py_type (*cc->declaration ()) && cc->module () != mod_name) {
|
|
||||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Class %s from module %s depends on %s.%s (try 'import %s' before 'import %s')")), c->name (), mod_name, cc->module (), cc->name (), pymod_name + "." + cc->module (), pymod_name + "." + mod_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same for base class
|
|
||||||
if (c->base () && ! PythonClassClientData::py_type (*c->base ()) && c->base ()->module () != mod_name) {
|
|
||||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Class %s from module %s depends on %s.%s (try 'import %s' before 'import %s')")), c->name (), mod_name, c->base ()->module (), c->base ()->name (), pymod_name + "." + c->base ()->module (), pymod_name + "." + mod_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -2380,7 +2391,6 @@ public:
|
||||||
// got an extension
|
// got an extension
|
||||||
tl_assert (cls->parent ());
|
tl_assert (cls->parent ());
|
||||||
m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ());
|
m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ());
|
||||||
m_extensions.insert (cls->declaration ());
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
@ -2389,9 +2399,15 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject *make_class (const gsi::ClassBase *cls)
|
PyTypeObject *make_class (const gsi::ClassBase *cls, bool as_static)
|
||||||
{
|
{
|
||||||
PyTypeObject *pt = PythonClassClientData::py_type (*cls);
|
// NOTE: with as_static = true, this method produces a mixin. This is a class entirely consisting
|
||||||
|
// of static constants and child classes only. It can be mixed into an existing class for emulation
|
||||||
|
// additional base classes.
|
||||||
|
// Everything else, like properties and methods will not work as the method enumeration scheme is
|
||||||
|
// not capable of handling more than a single base class.
|
||||||
|
|
||||||
|
PyTypeObject *pt = PythonClassClientData::py_type (*cls, as_static);
|
||||||
if (pt != 0) {
|
if (pt != 0) {
|
||||||
return pt;
|
return pt;
|
||||||
}
|
}
|
||||||
|
|
@ -2403,28 +2419,26 @@ public:
|
||||||
int n_bases = (cls->base () != 0 ? 1 : 0);
|
int n_bases = (cls->base () != 0 ? 1 : 0);
|
||||||
auto exts = m_extensions_for.find (cls);
|
auto exts = m_extensions_for.find (cls);
|
||||||
if (exts != m_extensions_for.end ()) {
|
if (exts != m_extensions_for.end ()) {
|
||||||
// @@@ n_bases += int (exts->second.size ());
|
n_bases += int (exts->second.size ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bases = PythonRef (PyTuple_New (n_bases));
|
bases = PythonRef (PyTuple_New (n_bases));
|
||||||
|
|
||||||
int ibase = 0;
|
int ibase = 0;
|
||||||
if (cls->base () != 0) {
|
if (cls->base () != 0) {
|
||||||
PyTypeObject *pt = make_class (cls->base ());
|
PyTypeObject *pt = make_class (cls->base (), as_static);
|
||||||
PyObject *base = (PyObject *) pt;
|
PyObject *base = (PyObject *) pt;
|
||||||
Py_INCREF (base);
|
Py_INCREF (base);
|
||||||
PyTuple_SetItem (bases.get (), ibase++, base);
|
PyTuple_SetItem (bases.get (), ibase++, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@@@
|
|
||||||
if (exts != m_extensions_for.end ()) {
|
if (exts != m_extensions_for.end ()) {
|
||||||
for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) {
|
for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) {
|
||||||
PyObject *base = (PyObject *) make_class (*ie);
|
PyObject *base = (PyObject *) make_class (*ie, true);
|
||||||
Py_INCREF (base);
|
Py_INCREF (base);
|
||||||
PyTuple_SetItem (bases.get (), ibase++, base);
|
PyTuple_SetItem (bases.get (), ibase++, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@@*/
|
|
||||||
|
|
||||||
// creates the type object
|
// creates the type object
|
||||||
|
|
||||||
|
|
@ -2434,7 +2448,11 @@ public:
|
||||||
PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (mp_module->next_class_id ())).get ());
|
PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (mp_module->next_class_id ())).get ());
|
||||||
|
|
||||||
PythonRef args (PyTuple_New (3));
|
PythonRef args (PyTuple_New (3));
|
||||||
PyTuple_SetItem (args.get (), 0, c2python (cls->name ()));
|
if (! as_static) {
|
||||||
|
PyTuple_SetItem (args.get (), 0, c2python (cls->name ()));
|
||||||
|
} else {
|
||||||
|
PyTuple_SetItem (args.get (), 0, c2python (cls->name () + "_Mixin"));
|
||||||
|
}
|
||||||
PyTuple_SetItem (args.get (), 1, bases.release ());
|
PyTuple_SetItem (args.get (), 1, bases.release ());
|
||||||
PyTuple_SetItem (args.get (), 2, dict.release ());
|
PyTuple_SetItem (args.get (), 2, dict.release ());
|
||||||
|
|
||||||
|
|
@ -2445,37 +2463,42 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Customize
|
// Customize
|
||||||
type->tp_basicsize += sizeof (PYAObjectBase);
|
if (! as_static) {
|
||||||
type->tp_init = &pya_object_init;
|
type->tp_basicsize += sizeof (PYAObjectBase);
|
||||||
type->tp_new = &pya_object_new;
|
type->tp_init = &pya_object_init;
|
||||||
type->tp_dealloc = (destructor) &pya_object_deallocate;
|
type->tp_new = &pya_object_new;
|
||||||
type->tp_setattro = PyObject_GenericSetAttr;
|
type->tp_dealloc = (destructor) &pya_object_deallocate;
|
||||||
type->tp_getattro = PyObject_GenericGetAttr;
|
type->tp_setattro = PyObject_GenericSetAttr;
|
||||||
|
type->tp_getattro = PyObject_GenericGetAttr;
|
||||||
|
}
|
||||||
|
|
||||||
PythonClassClientData::initialize (*cls, type);
|
PythonClassClientData::initialize (*cls, type, as_static);
|
||||||
|
|
||||||
mp_module->register_class (cls);
|
mp_module->register_class (cls);
|
||||||
|
|
||||||
tl_assert (mp_module->cls_for_type (type) == cls);
|
tl_assert (mp_module->cls_for_type (type) == cls);
|
||||||
|
|
||||||
// Add to the parent class as child class or add to module
|
// add to the parent class as child class or add to module
|
||||||
|
|
||||||
if (cls->parent ()) {
|
if (! cls->parent ()) {
|
||||||
tl_assert (cls->parent ()->declaration () != 0);
|
|
||||||
PyTypeObject *parent_type = make_class (cls->parent ()->declaration ());
|
|
||||||
PythonRef attr ((PyObject *) type);
|
|
||||||
set_type_attr (parent_type, cls->name ().c_str (), attr);
|
|
||||||
} else {
|
|
||||||
PyList_Append (m_all_list, PythonRef (c2python (cls->name ())).get ());
|
PyList_Append (m_all_list, PythonRef (c2python (cls->name ())).get ());
|
||||||
PyModule_AddObject (mp_module->module (), cls->name ().c_str (), (PyObject *) type);
|
PyModule_AddObject (mp_module->module (), cls->name ().c_str (), (PyObject *) type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// produce the child classes
|
||||||
|
|
||||||
|
for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
|
||||||
|
PyTypeObject *child_class = make_class (cc.operator-> (), as_static);
|
||||||
|
PythonRef attr ((PyObject *) child_class, false /*borrowed*/);
|
||||||
|
set_type_attr (type, cc->name ().c_str (), attr);
|
||||||
|
}
|
||||||
|
|
||||||
// add named extensions
|
// add named extensions
|
||||||
|
|
||||||
auto links = m_links_for.find (cls);
|
auto links = m_links_for.find (cls);
|
||||||
if (links != m_links_for.end ()) {
|
if (links != m_links_for.end ()) {
|
||||||
for (auto il = links->second.begin (); il != links->second.end (); ++il) {
|
for (auto il = links->second.begin (); il != links->second.end (); ++il) {
|
||||||
PyTypeObject *linked_type = make_class (il->second);
|
PyTypeObject *linked_type = make_class (il->second, false);
|
||||||
PythonRef attr ((PyObject *) linked_type, false /*borrowed*/);
|
PythonRef attr ((PyObject *) linked_type, false /*borrowed*/);
|
||||||
set_type_attr (type, il->first.c_str (), attr);
|
set_type_attr (type, il->first.c_str (), attr);
|
||||||
}
|
}
|
||||||
|
|
@ -2485,133 +2508,99 @@ public:
|
||||||
|
|
||||||
MethodTable *mt = MethodTable::method_table_by_class (cls);
|
MethodTable *mt = MethodTable::method_table_by_class (cls);
|
||||||
|
|
||||||
// signals are translated into the setters and getters
|
|
||||||
for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) {
|
|
||||||
if ((*m)->is_signal ()) {
|
|
||||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
|
||||||
mt->add_getter (syn->name, *m);
|
|
||||||
mt->add_setter (syn->name, *m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// first add getters and setters
|
|
||||||
for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) {
|
|
||||||
if (! (*m)->is_callback ()) {
|
|
||||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
|
||||||
if (syn->is_getter) {
|
|
||||||
mt->add_getter (syn->name, *m);
|
|
||||||
} else if (syn->is_setter) {
|
|
||||||
mt->add_setter (syn->name, *m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// then add normal methods - on name clash with properties make them a getter
|
|
||||||
for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) {
|
|
||||||
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
|
||||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
|
||||||
if (! syn->is_getter && ! syn->is_setter) {
|
|
||||||
if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && mt->find_property ((*m)->is_static (), syn->name).first) {
|
|
||||||
mt->add_getter (syn->name, *m);
|
|
||||||
} else {
|
|
||||||
mt->add_method (syn->name, *m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// produce the properties
|
// produce the properties
|
||||||
|
|
||||||
for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) {
|
if (! as_static) {
|
||||||
|
|
||||||
MethodTableEntry::method_iterator begin_setters = mt->begin_setters (mid);
|
for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) {
|
||||||
MethodTableEntry::method_iterator end_setters = mt->end_setters (mid);
|
|
||||||
MethodTableEntry::method_iterator begin_getters = mt->begin_getters (mid);
|
|
||||||
MethodTableEntry::method_iterator end_getters = mt->end_getters (mid);
|
|
||||||
int setter_mid = begin_setters != end_setters ? int (mid) : -1;
|
|
||||||
int getter_mid = begin_getters != end_getters ? int (mid) : -1;
|
|
||||||
|
|
||||||
bool is_static = false;
|
MethodTableEntry::method_iterator begin_setters = mt->begin_setters (mid);
|
||||||
if (begin_setters != end_setters) {
|
MethodTableEntry::method_iterator end_setters = mt->end_setters (mid);
|
||||||
is_static = (*begin_setters)->is_static ();
|
MethodTableEntry::method_iterator begin_getters = mt->begin_getters (mid);
|
||||||
} else if (begin_getters != end_getters) {
|
MethodTableEntry::method_iterator end_getters = mt->end_getters (mid);
|
||||||
is_static = (*begin_getters)->is_static ();
|
int setter_mid = begin_setters != end_setters ? int (mid) : -1;
|
||||||
}
|
int getter_mid = begin_getters != end_getters ? int (mid) : -1;
|
||||||
|
|
||||||
const std::string &name = mt->property_name (mid);
|
bool is_static = false;
|
||||||
|
if (begin_setters != end_setters) {
|
||||||
|
is_static = (*begin_setters)->is_static ();
|
||||||
|
} else if (begin_getters != end_getters) {
|
||||||
|
is_static = (*begin_getters)->is_static ();
|
||||||
|
}
|
||||||
|
|
||||||
// look for the real getter and setter, also look in the base classes
|
const std::string &name = mt->property_name (mid);
|
||||||
const gsi::ClassBase *icls = cls;
|
|
||||||
while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) {
|
|
||||||
|
|
||||||
const MethodTable *mt_base = MethodTable::method_table_by_class (icls);
|
// look for the real getter and setter, also look in the base classes
|
||||||
tl_assert (mt_base);
|
const gsi::ClassBase *icls = cls;
|
||||||
std::pair<bool, size_t> t = mt_base->find_property (is_static, name);
|
while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) {
|
||||||
if (t.first) {
|
|
||||||
if (begin_setters == end_setters && mt_base->begin_setters (t.second) != mt_base->end_setters (t.second)) {
|
const MethodTable *mt_base = MethodTable::method_table_by_class (icls);
|
||||||
setter_mid = int (t.second);
|
tl_assert (mt_base);
|
||||||
begin_setters = mt_base->begin_setters (t.second);
|
std::pair<bool, size_t> t = mt_base->find_property (is_static, name);
|
||||||
end_setters = mt_base->end_setters (t.second);
|
if (t.first) {
|
||||||
|
if (begin_setters == end_setters && mt_base->begin_setters (t.second) != mt_base->end_setters (t.second)) {
|
||||||
|
setter_mid = int (t.second);
|
||||||
|
begin_setters = mt_base->begin_setters (t.second);
|
||||||
|
end_setters = mt_base->end_setters (t.second);
|
||||||
|
}
|
||||||
|
if (begin_getters == end_getters && mt_base->begin_getters (t.second) != mt_base->end_getters (t.second)) {
|
||||||
|
getter_mid = int (t.second);
|
||||||
|
begin_getters = mt_base->begin_getters (t.second);
|
||||||
|
end_getters = mt_base->end_getters (t.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (begin_getters == end_getters && mt_base->begin_getters (t.second) != mt_base->end_getters (t.second)) {
|
|
||||||
getter_mid = int (t.second);
|
}
|
||||||
begin_getters = mt_base->begin_getters (t.second);
|
|
||||||
end_getters = mt_base->end_getters (t.second);
|
std::string doc;
|
||||||
|
|
||||||
|
// add getter and setter documentation, create specific Python documentation
|
||||||
|
|
||||||
|
for (MethodTableEntry::method_iterator m = begin_getters; m != end_getters; ++m) {
|
||||||
|
if (! doc.empty ()) {
|
||||||
|
doc += "\n\n";
|
||||||
}
|
}
|
||||||
|
doc += (*m)->doc ();
|
||||||
|
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) {
|
||||||
|
if (! doc.empty ()) {
|
||||||
std::string doc;
|
doc += "\n\n";
|
||||||
|
}
|
||||||
// add getter and setter documentation, create specific Python documentation
|
doc += (*m)->doc ();
|
||||||
|
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name));
|
||||||
for (MethodTableEntry::method_iterator m = begin_getters; m != end_getters; ++m) {
|
|
||||||
if (! doc.empty ()) {
|
|
||||||
doc += "\n\n";
|
|
||||||
}
|
}
|
||||||
doc += (*m)->doc ();
|
|
||||||
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) {
|
PythonRef attr;
|
||||||
if (! doc.empty ()) {
|
|
||||||
doc += "\n\n";
|
if (! is_static) {
|
||||||
|
|
||||||
|
// non-static attribute getters/setters
|
||||||
|
PyGetSetDef *getset = mp_module->make_getset_def ();
|
||||||
|
getset->name = mp_module->make_string (name);
|
||||||
|
getset->get = begin_getters != end_getters ? &property_getter_func : NULL;
|
||||||
|
getset->set = begin_setters != end_setters ? &property_setter_func : NULL;
|
||||||
|
getset->doc = mp_module->make_string (doc);
|
||||||
|
getset->closure = make_closure (getter_mid, setter_mid);
|
||||||
|
|
||||||
|
attr = PythonRef (PyDescr_NewGetSet (type, getset));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name));
|
||||||
|
|
||||||
|
desc->type = type;
|
||||||
|
desc->getter = begin_getters != end_getters ? property_getter_adaptors[getter_mid] : NULL;
|
||||||
|
desc->setter = begin_setters != end_setters ? property_setter_adaptors[setter_mid] : NULL;
|
||||||
|
attr = PythonRef (desc);
|
||||||
|
|
||||||
}
|
}
|
||||||
doc += (*m)->doc ();
|
|
||||||
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name));
|
|
||||||
}
|
|
||||||
|
|
||||||
PythonRef attr;
|
set_type_attr (type, name, attr);
|
||||||
|
|
||||||
if (! is_static) {
|
|
||||||
|
|
||||||
// non-static attribute getters/setters
|
|
||||||
PyGetSetDef *getset = mp_module->make_getset_def ();
|
|
||||||
getset->name = mp_module->make_string (name);
|
|
||||||
getset->get = begin_getters != end_getters ? &property_getter_func : NULL;
|
|
||||||
getset->set = begin_setters != end_setters ? &property_setter_func : NULL;
|
|
||||||
getset->doc = mp_module->make_string (doc);
|
|
||||||
getset->closure = make_closure (getter_mid, setter_mid);
|
|
||||||
|
|
||||||
attr = PythonRef (PyDescr_NewGetSet (type, getset));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name));
|
|
||||||
|
|
||||||
desc->type = type;
|
|
||||||
desc->getter = begin_getters != end_getters ? property_getter_adaptors[getter_mid] : NULL;
|
|
||||||
desc->setter = begin_setters != end_setters ? property_setter_adaptors[setter_mid] : NULL;
|
|
||||||
attr = PythonRef (desc);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_type_attr (type, name, attr);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect the names which have been disambiguated static/non-static wise
|
// collect the names which have been disambiguated static/non-static wise
|
||||||
|
|
@ -2691,80 +2680,84 @@ public:
|
||||||
tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0]));
|
tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0]));
|
||||||
if (! mt->is_static (mid)) {
|
if (! mt->is_static (mid)) {
|
||||||
|
|
||||||
std::vector<std::string> alt_names;
|
if (! as_static) {
|
||||||
|
|
||||||
if (name == "to_s" && m_first->compatible_with_num_args (0)) {
|
std::vector<std::string> alt_names;
|
||||||
|
|
||||||
// The str method is also routed via the tp_str implementation
|
if (name == "to_s" && m_first->compatible_with_num_args (0)) {
|
||||||
alt_names.push_back ("__str__");
|
|
||||||
|
// The str method is also routed via the tp_str implementation
|
||||||
|
alt_names.push_back ("__str__");
|
||||||
#if GSI_ALIAS_INSPECT
|
#if GSI_ALIAS_INSPECT
|
||||||
bool alias_inspect = ! has_inspect;
|
bool alias_inspect = ! has_inspect;
|
||||||
#else
|
#else
|
||||||
bool alias_inspect = false;
|
bool alias_inspect = false;
|
||||||
#endif
|
#endif
|
||||||
if (alias_inspect) {
|
if (alias_inspect) {
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'")));
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'")));
|
||||||
|
alt_names.push_back ("__repr__");
|
||||||
|
} else {
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'")));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (name == "hash" && m_first->compatible_with_num_args (0)) {
|
||||||
|
|
||||||
|
// The hash method is also routed via the tp_hash implementation
|
||||||
|
alt_names.push_back ("__hash__");
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'")));
|
||||||
|
|
||||||
|
} else if (name == "inspect" && m_first->compatible_with_num_args (0)) {
|
||||||
|
|
||||||
|
// The str method is also routed via the tp_str implementation
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'")));
|
||||||
alt_names.push_back ("__repr__");
|
alt_names.push_back ("__repr__");
|
||||||
} else {
|
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'")));
|
} else if (name == "size" && m_first->compatible_with_num_args (0)) {
|
||||||
|
|
||||||
|
// The size method is also routed via the sequence methods protocol if there
|
||||||
|
// is a [] function
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'")));
|
||||||
|
alt_names.push_back ("__len__");
|
||||||
|
|
||||||
|
} else if (name == "each" && m_first->compatible_with_num_args (0) && m_first->ret_type ().is_iter ()) {
|
||||||
|
|
||||||
|
// each makes the object iterable
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object")));
|
||||||
|
alt_names.push_back ("__iter__");
|
||||||
|
|
||||||
|
} else if (name == "__mul__") {
|
||||||
|
// Adding right multiplication
|
||||||
|
// Rationale: if pyaObj * x works, so should x * pyaObj
|
||||||
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'")));
|
||||||
|
alt_names.push_back ("__rmul__");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (name == "hash" && m_first->compatible_with_num_args (0)) {
|
for (std::vector <std::string>::const_iterator an = alt_names.begin (); an != alt_names.end (); ++an) {
|
||||||
|
|
||||||
// The hash method is also routed via the tp_hash implementation
|
// needs registration under an alternative name to enable special protocols
|
||||||
alt_names.push_back ("__hash__");
|
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'")));
|
|
||||||
|
|
||||||
} else if (name == "inspect" && m_first->compatible_with_num_args (0)) {
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
|
method->ml_name = mp_module->make_string (*an);
|
||||||
|
method->ml_meth = (PyCFunction) method_adaptors[mid];
|
||||||
|
method->ml_doc = mp_module->make_string (doc);
|
||||||
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
// The str method is also routed via the tp_str implementation
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'")));
|
set_type_attr (type, *an, attr);
|
||||||
alt_names.push_back ("__repr__");
|
|
||||||
|
|
||||||
} else if (name == "size" && m_first->compatible_with_num_args (0)) {
|
}
|
||||||
|
|
||||||
// The size method is also routed via the sequence methods protocol if there
|
|
||||||
// is a [] function
|
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'")));
|
|
||||||
alt_names.push_back ("__len__");
|
|
||||||
|
|
||||||
} else if (name == "each" && m_first->compatible_with_num_args (0) && m_first->ret_type ().is_iter ()) {
|
|
||||||
|
|
||||||
// each makes the object iterable
|
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object")));
|
|
||||||
alt_names.push_back ("__iter__");
|
|
||||||
|
|
||||||
} else if (name == "__mul__") {
|
|
||||||
// Adding right multiplication
|
|
||||||
// Rationale: if pyaObj * x works, so should x * pyaObj
|
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'")));
|
|
||||||
alt_names.push_back ("__rmul__");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector <std::string>::const_iterator an = alt_names.begin (); an != alt_names.end (); ++an) {
|
|
||||||
|
|
||||||
// needs registration under an alternative name to enable special protocols
|
|
||||||
|
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
method->ml_name = mp_module->make_string (*an);
|
method->ml_name = mp_module->make_string (name);
|
||||||
method->ml_meth = (PyCFunction) method_adaptors[mid];
|
method->ml_meth = (PyCFunction) method_adaptors[mid];
|
||||||
method->ml_doc = mp_module->make_string (doc);
|
method->ml_doc = mp_module->make_string (doc);
|
||||||
method->ml_flags = METH_VARARGS;
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
set_type_attr (type, *an, attr);
|
set_type_attr (type, name, attr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
|
||||||
method->ml_name = mp_module->make_string (name);
|
|
||||||
method->ml_meth = (PyCFunction) method_adaptors[mid];
|
|
||||||
method->ml_doc = mp_module->make_string (doc);
|
|
||||||
method->ml_flags = METH_VARARGS;
|
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
|
||||||
set_type_attr (type, name, attr);
|
|
||||||
|
|
||||||
} else if (isupper (name [0]) || m_first->is_const ()) {
|
} else if (isupper (name [0]) || m_first->is_const ()) {
|
||||||
|
|
||||||
if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) {
|
if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) {
|
||||||
|
|
@ -2782,7 +2775,7 @@ public:
|
||||||
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python")));
|
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python")));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else if (! as_static) {
|
||||||
|
|
||||||
if (m_first->ret_type ().type () == gsi::T_object && m_first->ret_type ().pass_obj () && name == "new") {
|
if (m_first->ret_type ().type () == gsi::T_object && m_first->ret_type ().pass_obj () && name == "new") {
|
||||||
|
|
||||||
|
|
@ -2813,104 +2806,106 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
if (! as_static) {
|
||||||
|
|
||||||
// Complete the comparison operators if necessary.
|
// Complete the comparison operators if necessary.
|
||||||
// Unlike Ruby, Python does not automatically implement != from == for example.
|
// Unlike Ruby, Python does not automatically implement != from == for example.
|
||||||
// We assume that "==" and "<" are the minimum requirements for full comparison
|
// We assume that "==" and "<" are the minimum requirements for full comparison
|
||||||
// and "==" is the minimum requirement for equality. Hence:
|
// and "==" is the minimum requirement for equality. Hence:
|
||||||
// * If "==" is given, but no "!=", synthesize
|
// * If "==" is given, but no "!=", synthesize
|
||||||
// "a != b" by "!a == b"
|
// "a != b" by "!a == b"
|
||||||
// * If "==" and "<" are given, synthesize if required
|
// * If "==" and "<" are given, synthesize if required
|
||||||
// "a <= b" by "a < b || a == b"
|
// "a <= b" by "a < b || a == b"
|
||||||
// "a > b" by "!(a < b || a == b)" (could be b < a, but this avoids having to switch arguments)
|
// "a > b" by "!(a < b || a == b)" (could be b < a, but this avoids having to switch arguments)
|
||||||
// "a >= b" by "!a < b"
|
// "a >= b" by "!a < b"
|
||||||
|
|
||||||
bool has_eq = mt->find_method (false, "==").first;
|
bool has_eq = mt->find_method (false, "==").first;
|
||||||
bool has_ne = mt->find_method (false, "!=").first;
|
bool has_ne = mt->find_method (false, "!=").first;
|
||||||
bool has_lt = mt->find_method (false, "<").first;
|
bool has_lt = mt->find_method (false, "<").first;
|
||||||
bool has_le = mt->find_method (false, "<=").first;
|
bool has_le = mt->find_method (false, "<=").first;
|
||||||
bool has_gt = mt->find_method (false, ">").first;
|
bool has_gt = mt->find_method (false, ">").first;
|
||||||
bool has_ge = mt->find_method (false, ">=").first;
|
bool has_ge = mt->find_method (false, ">=").first;
|
||||||
bool has_cmp = mt->find_method (false, "<=>").first;
|
bool has_cmp = mt->find_method (false, "<=>").first;
|
||||||
|
|
||||||
if (! has_cmp && has_eq) {
|
if (! has_cmp && has_eq) {
|
||||||
|
|
||||||
if (! has_ne) {
|
if (! has_ne) {
|
||||||
|
|
||||||
// Add a definition for "__ne__"
|
// Add a definition for "__ne__"
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
method->ml_name = "__ne__";
|
method->ml_name = "__ne__";
|
||||||
method->ml_meth = &object_default_ne_impl;
|
method->ml_meth = &object_default_ne_impl;
|
||||||
method->ml_flags = METH_VARARGS;
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
set_type_attr (type, method->ml_name, attr);
|
set_type_attr (type, method->ml_name, attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_lt && ! has_le) {
|
||||||
|
|
||||||
|
// Add a definition for "__le__"
|
||||||
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
|
method->ml_name = "__le__";
|
||||||
|
method->ml_meth = &object_default_le_impl;
|
||||||
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
|
set_type_attr (type, method->ml_name, attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_lt && ! has_gt) {
|
||||||
|
|
||||||
|
// Add a definition for "__gt__"
|
||||||
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
|
method->ml_name = "__gt__";
|
||||||
|
method->ml_meth = &object_default_gt_impl;
|
||||||
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
|
set_type_attr (type, method->ml_name, attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_lt && ! has_ge) {
|
||||||
|
|
||||||
|
// Add a definition for "__ge__"
|
||||||
|
PyMethodDef *method = mp_module->make_method_def ();
|
||||||
|
method->ml_name = "__ge__";
|
||||||
|
method->ml_meth = &object_default_ge_impl;
|
||||||
|
method->ml_flags = METH_VARARGS;
|
||||||
|
|
||||||
|
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
||||||
|
set_type_attr (type, method->ml_name, attr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_lt && ! has_le) {
|
// install the static/non-static dispatcher descriptor
|
||||||
|
|
||||||
// Add a definition for "__le__"
|
for (std::vector<std::string>::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) {
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
|
||||||
method->ml_name = "__le__";
|
|
||||||
method->ml_meth = &object_default_le_impl;
|
|
||||||
method->ml_flags = METH_VARARGS;
|
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
PyObject *attr_inst = PyObject_GetAttrString ((PyObject *) type, ("_inst_" + *a).c_str ());
|
||||||
set_type_attr (type, method->ml_name, attr);
|
PyObject *attr_class = PyObject_GetAttrString ((PyObject *) type, ("_class_" + *a).c_str ());
|
||||||
|
if (attr_inst == NULL || attr_class == NULL) {
|
||||||
|
|
||||||
}
|
// some error or disambiguator is not required -> don't install it
|
||||||
|
Py_XDECREF (attr_inst);
|
||||||
|
Py_XDECREF (attr_class);
|
||||||
|
PyErr_Clear ();
|
||||||
|
|
||||||
if (has_lt && ! has_gt) {
|
} else {
|
||||||
|
|
||||||
// Add a definition for "__gt__"
|
PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class);
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
PythonRef name (c2python (*a));
|
||||||
method->ml_name = "__gt__";
|
// Note: we use GenericSetAttr since that one allows us setting attributes on built-in types
|
||||||
method->ml_meth = &object_default_gt_impl;
|
PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc);
|
||||||
method->ml_flags = METH_VARARGS;
|
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
}
|
||||||
set_type_attr (type, method->ml_name, attr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_lt && ! has_ge) {
|
|
||||||
|
|
||||||
// Add a definition for "__ge__"
|
|
||||||
PyMethodDef *method = mp_module->make_method_def ();
|
|
||||||
method->ml_name = "__ge__";
|
|
||||||
method->ml_meth = &object_default_ge_impl;
|
|
||||||
method->ml_flags = METH_VARARGS;
|
|
||||||
|
|
||||||
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
|
|
||||||
set_type_attr (type, method->ml_name, attr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// install the static/non-static dispatcher descriptor
|
|
||||||
|
|
||||||
for (std::vector<std::string>::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) {
|
|
||||||
|
|
||||||
PyObject *attr_inst = PyObject_GetAttrString ((PyObject *) type, ("_inst_" + *a).c_str ());
|
|
||||||
PyObject *attr_class = PyObject_GetAttrString ((PyObject *) type, ("_class_" + *a).c_str ());
|
|
||||||
if (attr_inst == NULL || attr_class == NULL) {
|
|
||||||
|
|
||||||
// some error -> don't install the disambiguator
|
|
||||||
Py_XDECREF (attr_inst);
|
|
||||||
Py_XDECREF (attr_class);
|
|
||||||
PyErr_Clear ();
|
|
||||||
|
|
||||||
tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << cls->name ();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class);
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2925,7 +2920,6 @@ private:
|
||||||
PythonModule *mp_module;
|
PythonModule *mp_module;
|
||||||
PyObject *m_all_list;
|
PyObject *m_all_list;
|
||||||
std::map<const gsi::ClassBase *, std::vector<const gsi::ClassBase *> > m_extensions_for;
|
std::map<const gsi::ClassBase *, std::vector<const gsi::ClassBase *> > m_extensions_for;
|
||||||
std::set<const gsi::ClassBase *> m_extensions;
|
|
||||||
std::map<const gsi::ClassBase *, std::vector<std::pair<std::string, const gsi::ClassBase *> > > m_links_for;
|
std::map<const gsi::ClassBase *, std::vector<std::pair<std::string, const gsi::ClassBase *> > > m_links_for;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2968,16 +2962,16 @@ PythonModule::make_classes (const char *mod_name)
|
||||||
for (std::list<const gsi::ClassBase *>::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
for (std::list<const gsi::ClassBase *>::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
||||||
if ((*c)->module () != mod_name) {
|
if ((*c)->module () != mod_name) {
|
||||||
// don't handle classes outside this module, but require them to be present
|
// don't handle classes outside this module, but require them to be present
|
||||||
if (! PythonClassClientData::py_type (**c)) {
|
if (! PythonClassClientData::py_type (**c, false)) {
|
||||||
throw tl::Exception (tl::sprintf ("class %s.%s required from outside the module %s, but that module is not loaded", (*c)->module (), (*c)->name (), mod_name));
|
throw tl::Exception (tl::sprintf ("class %s.%s required from outside the module %s, but that module is not loaded", (*c)->module (), (*c)->name (), mod_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// first pass: register the extensions
|
// first pass: register the extensions using all available classes
|
||||||
for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
for (std::list<const gsi::ClassBase *>::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
||||||
if ((*c)->declaration () != *c && (! mod_name || (*c)->module () == mod_name)) {
|
if ((*c)->declaration () != *c) {
|
||||||
gen.register_extension (*c);
|
gen.register_extension (*c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2985,7 +2979,7 @@ PythonModule::make_classes (const char *mod_name)
|
||||||
// second pass: make the classes
|
// second pass: make the classes
|
||||||
for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) {
|
||||||
if ((*c)->declaration () == *c && (! mod_name || (*c)->module () == mod_name)) {
|
if ((*c)->declaration () == *c && (! mod_name || (*c)->module () == mod_name)) {
|
||||||
gen.make_class (*c);
|
gen.make_class (*c, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3010,7 +3004,7 @@ const gsi::ClassBase *PythonModule::cls_for_type (PyTypeObject *type)
|
||||||
|
|
||||||
PyTypeObject *PythonModule::type_for_cls (const gsi::ClassBase *cls)
|
PyTypeObject *PythonModule::type_for_cls (const gsi::ClassBase *cls)
|
||||||
{
|
{
|
||||||
return PythonClassClientData::py_type (*cls);
|
return PythonClassClientData::py_type (*cls, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,8 +158,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void check (const char *mod_name);
|
|
||||||
|
|
||||||
std::list<std::string> m_string_heap;
|
std::list<std::string> m_string_heap;
|
||||||
std::vector<PyMethodDef *> m_methods_heap;
|
std::vector<PyMethodDef *> m_methods_heap;
|
||||||
std::vector<PyGetSetDef *> m_getseters_heap;
|
std::vector<PyGetSetDef *> m_getseters_heap;
|
||||||
|
|
|
||||||
|
|
@ -586,5 +586,17 @@ PYAObjectBase::obj ()
|
||||||
return m_obj;
|
return m_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PYAObjectBase *
|
||||||
|
PYAObjectBase::from_pyobject (PyObject *py_object)
|
||||||
|
{
|
||||||
|
if (Py_TYPE (py_object)->tp_init == NULL) {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("Extension classes do not support instance methods or properties")));
|
||||||
|
}
|
||||||
|
|
||||||
|
PYAObjectBase *pya_object = from_pyobject_unsafe (py_object);
|
||||||
|
tl_assert (pya_object->py_object () == py_object);
|
||||||
|
return pya_object;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,25 +70,21 @@ public:
|
||||||
*/
|
*/
|
||||||
~PYAObjectBase ();
|
~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
|
* @brief Gets the PYAObjectBase pointer from a PyObject pointer
|
||||||
* This version doesn't check anything.
|
* This version doesn't check anything.
|
||||||
*/
|
*/
|
||||||
static PYAObjectBase *from_pyobject_unsafe (PyObject *py_object)
|
static PYAObjectBase *from_pyobject_unsafe (PyObject *py_object)
|
||||||
{
|
{
|
||||||
|
// the objects must not be a pure static extension
|
||||||
return (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase));
|
return (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the PYAObjectBase pointer from a PyObject pointer
|
||||||
|
*/
|
||||||
|
static PYAObjectBase *from_pyobject (PyObject *py_object);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Indicates that a C++ object is present
|
* @brief Indicates that a C++ object is present
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3045,8 +3045,23 @@ class BasicTest(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(pya.A.ba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ])
|
self.assertEqual(pya.A.ba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ])
|
||||||
|
|
||||||
|
# Tests multi-base mixins (only constants and enums available)
|
||||||
|
def test_multiBaseMixins(self):
|
||||||
|
|
||||||
|
bb = pya.BB() # base classes B1,B2,B3
|
||||||
|
bb.set1(17) # B1
|
||||||
|
self.assertEqual(bb.get1(), 17) # B1
|
||||||
|
bb.set1(21) # B1
|
||||||
|
|
||||||
|
self.assertEqual(bb.get1(), 21) # B1
|
||||||
|
self.assertEqual(pya.BB.C2, 17) # B2
|
||||||
|
self.assertEqual(pya.BB.C3, -1) # B3
|
||||||
|
self.assertEqual(pya.BB.E.E3B.to_i(), 101) # B3
|
||||||
|
self.assertEqual(bb.d3(pya.BB.E.E3C, pya.BB.E.E3A), -2) # BB with B3 enums
|
||||||
|
self.assertEqual(bb.d3(pya.BB.E.E3A, pya.BB.E.E3C), 2) # BB with B3 enums
|
||||||
|
|
||||||
# Custom factory implemented in Python
|
# Custom factory implemented in Python
|
||||||
#
|
|
||||||
def test_80(self):
|
def test_80(self):
|
||||||
|
|
||||||
gc = pya.GObject.g_inst_count()
|
gc = pya.GObject.g_inst_count()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue