diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 3c09c805c..6aed6692c 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -116,6 +116,44 @@ public: MethodTable (const gsi::ClassBase *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 ()) { const MethodTable *base_mt = method_table_by_class (cls_decl->base ()); tl_assert (base_mt); @@ -369,24 +407,34 @@ private: struct PythonClassClientData : public gsi::PerClassClientSpecificData { - PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type) - : py_type_object (_py_type), method_table (_cls) + PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static) + : py_type_object (_py_type), py_type_object_static (_py_type_static), method_table (_cls) { // .. nothing yet .. } PyTypeObject *py_type_object; + PyTypeObject *py_type_object_static; 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(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(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 tl_assert (mod_name != 0); tl_assert (mp_module.get () == 0); - check (mod_name); m_mod_name = pymod_name + "." + mod_name; m_mod_description = description; @@ -2269,7 +2316,6 @@ PythonModule::init (const char *mod_name, PyObject *module) { // do some checks before we create the module tl_assert (mp_module.get () == 0); - check (mod_name); m_mod_name = mod_name; 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::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 { @@ -2380,7 +2391,6 @@ public: // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - m_extensions.insert (cls->declaration ()); } 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) { return pt; } @@ -2403,28 +2419,26 @@ public: int n_bases = (cls->base () != 0 ? 1 : 0); auto exts = m_extensions_for.find (cls); if (exts != m_extensions_for.end ()) { -// @@@ n_bases += int (exts->second.size ()); + n_bases += int (exts->second.size ()); } bases = PythonRef (PyTuple_New (n_bases)); int ibase = 0; if (cls->base () != 0) { - PyTypeObject *pt = make_class (cls->base ()); + PyTypeObject *pt = make_class (cls->base (), as_static); PyObject *base = (PyObject *) pt; Py_INCREF (base); PyTuple_SetItem (bases.get (), ibase++, base); } -/*@@@ if (exts != m_extensions_for.end ()) { 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); PyTuple_SetItem (bases.get (), ibase++, base); } } -@@@*/ // creates the type object @@ -2434,7 +2448,11 @@ public: PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (mp_module->next_class_id ())).get ()); 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 (), 2, dict.release ()); @@ -2445,37 +2463,42 @@ public: } // 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; + if (! as_static) { + 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 (*cls, type); + PythonClassClientData::initialize (*cls, type, as_static); mp_module->register_class (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 ()) { - 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 { + if (! cls->parent ()) { PyList_Append (m_all_list, PythonRef (c2python (cls->name ())).get ()); 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 auto links = m_links_for.find (cls); if (links != m_links_for.end ()) { 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*/); set_type_attr (type, il->first.c_str (), attr); } @@ -2485,133 +2508,99 @@ public: 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 - 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); - 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; + for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++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 (); - } + MethodTableEntry::method_iterator begin_setters = mt->begin_setters (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; - 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 gsi::ClassBase *icls = cls; - while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) { + const std::string &name = mt->property_name (mid); - const MethodTable *mt_base = MethodTable::method_table_by_class (icls); - tl_assert (mt_base); - std::pair t = mt_base->find_property (is_static, name); - 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); + // look for the real getter and setter, also look in the base classes + 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); + tl_assert (mt_base); + std::pair t = mt_base->find_property (is_static, name); + 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)); } - } - - 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"; + for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++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 writable attribute '%s'. This is the setter.\n\n")), name)); } - 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 ()) { - doc += "\n\n"; + PythonRef 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); + } - 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; - - 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); } - set_type_attr (type, name, attr); - } // 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])); if (! mt->is_static (mid)) { - std::vector alt_names; + if (! as_static) { - if (name == "to_s" && m_first->compatible_with_num_args (0)) { + std::vector alt_names; - // The str method is also routed via the tp_str implementation - alt_names.push_back ("__str__"); + if (name == "to_s" && m_first->compatible_with_num_args (0)) { + + // The str method is also routed via the tp_str implementation + alt_names.push_back ("__str__"); #if GSI_ALIAS_INSPECT - bool alias_inspect = ! has_inspect; + bool alias_inspect = ! has_inspect; #else - bool alias_inspect = false; + bool alias_inspect = false; #endif - 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)'"))); + 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)'"))); + 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__"); - } 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 ::const_iterator an = alt_names.begin (); an != alt_names.end (); ++an) { - // 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)'"))); + // needs registration under an alternative name to enable special protocols - } 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 - 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__"); + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, *an, attr); - } 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 ::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 (); - 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_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; 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 ()) { 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"))); } - } else { + } else if (! as_static) { 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. - // Unlike Ruby, Python does not automatically implement != from == for example. - // We assume that "==" and "<" are the minimum requirements for full comparison - // and "==" is the minimum requirement for equality. Hence: - // * If "==" is given, but no "!=", synthesize - // "a != b" by "!a == b" - // * If "==" and "<" are given, synthesize if required - // "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" + // Complete the comparison operators if necessary. + // Unlike Ruby, Python does not automatically implement != from == for example. + // We assume that "==" and "<" are the minimum requirements for full comparison + // and "==" is the minimum requirement for equality. Hence: + // * If "==" is given, but no "!=", synthesize + // "a != b" by "!a == b" + // * If "==" and "<" are given, synthesize if required + // "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" - bool has_eq = mt->find_method (false, "==").first; - bool has_ne = mt->find_method (false, "!=").first; - bool has_lt = mt->find_method (false, "<").first; - bool has_le = mt->find_method (false, "<=").first; - bool has_gt = mt->find_method (false, ">").first; - bool has_ge = mt->find_method (false, ">=").first; - bool has_cmp = mt->find_method (false, "<=>").first; + bool has_eq = mt->find_method (false, "==").first; + bool has_ne = mt->find_method (false, "!=").first; + bool has_lt = mt->find_method (false, "<").first; + bool has_le = mt->find_method (false, "<=").first; + bool has_gt = mt->find_method (false, ">").first; + bool has_ge = 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__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__ne__"; - method->ml_meth = &object_default_ne_impl; - method->ml_flags = METH_VARARGS; + // Add a definition for "__ne__" + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__ne__"; + method->ml_meth = &object_default_ne_impl; + method->ml_flags = METH_VARARGS; - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + 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__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__le__"; - method->ml_meth = &object_default_le_impl; - method->ml_flags = METH_VARARGS; + for (std::vector::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + 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 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__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__gt__"; - method->ml_meth = &object_default_gt_impl; - method->ml_flags = METH_VARARGS; + 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); - 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::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; PyObject *m_all_list; std::map > m_extensions_for; - std::set m_extensions; std::map > > m_links_for; }; @@ -2968,16 +2962,16 @@ PythonModule::make_classes (const char *mod_name) for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if ((*c)->module () != mod_name) { // 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)); } } } } - // first pass: register the extensions - for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { - if ((*c)->declaration () != *c && (! mod_name || (*c)->module () == mod_name)) { + // first pass: register the extensions using all available classes + for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () != *c) { gen.register_extension (*c); } } @@ -2985,7 +2979,7 @@ PythonModule::make_classes (const char *mod_name) // second pass: make the classes for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { 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) { - return PythonClassClientData::py_type (*cls); + return PythonClassClientData::py_type (*cls, false); } } diff --git a/src/pya/pya/pyaModule.h b/src/pya/pya/pyaModule.h index 1656a11e4..af21dbe48 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -158,8 +158,6 @@ public: } private: - static void check (const char *mod_name); - std::list m_string_heap; std::vector m_methods_heap; std::vector m_getseters_heap; diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 551e61c57..74618fef1 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -586,5 +586,17 @@ PYAObjectBase::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; +} + } diff --git a/src/pya/pya/pyaObject.h b/src/pya/pya/pyaObject.h index 36e1ab97d..fbf183848 100644 --- a/src/pya/pya/pyaObject.h +++ b/src/pya/pya/pyaObject.h @@ -70,25 +70,21 @@ public: */ ~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) { + // the objects must not be a pure static extension 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 */ diff --git a/testdata/python/basic.py b/testdata/python/basic.py index 5bd49cb16..ea1e8c6c9 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -3045,8 +3045,23 @@ class BasicTest(unittest.TestCase): 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 -# + def test_80(self): gc = pya.GObject.g_inst_count()