From daad80d5d5efde1a4bdf863c1cd35a137b714ae9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Oct 2022 22:08:06 +0200 Subject: [PATCH] Synthesize getters from is_... predicates, added getter for RecursiveShapeIterator#shape_flags --- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 7 + src/pya/pya/gsiDeclPya.cc | 23 ++ src/pya/pya/pya.pro | 7 +- src/pya/pya/pyaInternal.cc | 241 ++++++------------- src/pya/pya/pyaInternal.h | 40 ++- src/pya/pya/pyaModule.cc | 22 +- 6 files changed, 153 insertions(+), 187 deletions(-) diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index c70423544..791e112c2 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -523,6 +523,13 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "The flags must be specified before the shapes are being retrieved.\n" "Settings the shapes flags will reset the iterator.\n" ) + + gsi::method ("shape_flags", (unsigned int (db::RecursiveShapeIterator::*)() const) &db::RecursiveShapeIterator::shape_flags, + "@brief Gets the shape selection flags\n" + "\n" + "See \\shape_flags= for a description of that property.\n" + "\n" + "This getter has been introduced in version 0.28.\n" + ) + gsi::method ("trans|#itrans", &db::RecursiveShapeIterator::trans, "@brief Gets the current transformation by which the shapes must be transformed into the initial cell\n" "\n" diff --git a/src/pya/pya/gsiDeclPya.cc b/src/pya/pya/gsiDeclPya.cc index 6a82b36a2..798ee8269 100644 --- a/src/pya/pya/gsiDeclPya.cc +++ b/src/pya/pya/gsiDeclPya.cc @@ -24,10 +24,27 @@ #include "gsiDecl.h" #include "pyaInternal.h" +#include "pya.h" namespace gsi { +static const pya::MethodTableEntry *getter (std::pair *p) +{ + return p->second; +} + +static const pya::MethodTableEntry *setter (std::pair *p) +{ + return p->first; +} + +gsi::Class > decl_PythonGetterSetterPair ("tl", "PythonGetterSetterPair", + gsi::method_ext ("getter", &getter, "@brief Gets the getter function") + + gsi::method_ext ("setter", &setter, "@brief Gets the setter function"), + "@hide" +); + gsi::Class decl_PythonFunction ("tl", "PythonFunction", gsi::method ("methods", &pya::MethodTableEntry::methods, "@brief Gets the list of methods bound to this Python function") + gsi::method ("name", &pya::MethodTableEntry::name, "@brief Gets the name of this Python function") + @@ -77,4 +94,10 @@ gsi::ClassExt class_base_ext ( "@hide" ); +static +gsi::ClassExt method_base_ext ( + gsi::method_ext ("python_methods", &pya::PythonInterpreter::python_doc, "@brief Gets the Python specific documentation"), + "@hide" +); + } diff --git a/src/pya/pya/pya.pro b/src/pya/pya/pya.pro index 9e514c318..84176f306 100644 --- a/src/pya/pya/pya.pro +++ b/src/pya/pya/pya.pro @@ -11,13 +11,16 @@ SOURCES = \ pyaConvert.cc \ pyaHelpers.cc \ pyaInspector.cc \ + pyaInternal.cc \ + pyaCallables.cc \ pyaMarshal.cc \ pyaObject.cc \ pyaRefs.cc \ pyaUtils.cc \ pyaModule.cc \ pyaSignalHandler.cc \ - pyaStatusChangedListener.cc + pyaStatusChangedListener.cc \ + gsiDeclPya.cc HEADERS += \ pya.h \ @@ -25,6 +28,8 @@ HEADERS += \ pyaConvert.h \ pyaHelpers.h \ pyaInspector.h \ + pyaInternal.h \ + pyaCallables.h \ pyaMarshal.h \ pyaObject.h \ pyaRefs.h \ diff --git a/src/pya/pya/pyaInternal.cc b/src/pya/pya/pyaInternal.cc index c1b263e5b..eb7c1d61a 100644 --- a/src/pya/pya/pyaInternal.cc +++ b/src/pya/pya/pyaInternal.cc @@ -35,7 +35,7 @@ namespace pya // MethodTableEntry implementation MethodTableEntry::MethodTableEntry (const std::string &name, bool st, bool prot) - : m_name (name), m_is_static (st), m_is_protected (prot), m_is_enabled (true) + : m_name (name), m_is_static (st), m_is_protected (prot), m_is_enabled (true), m_is_init (false) { } const std::string & @@ -62,6 +62,18 @@ MethodTableEntry::is_enabled () const return m_is_enabled; } +void +MethodTableEntry::set_init (bool f) +{ + m_is_init = f; +} + +bool +MethodTableEntry::is_init () const +{ + return m_is_init; +} + bool MethodTableEntry::is_static () const { @@ -139,16 +151,28 @@ MethodTable::MethodTable (const gsi::ClassBase *cls_decl, PythonModule *module) // 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 ()) { + + bool st = (*m)->is_static (); + 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) { + if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && is_property_setter (st, syn->name) && ! is_property_getter (st, syn->name)) { add_getter (syn->name, *m); } else { + if (syn->is_predicate && std::string (syn->name, 0, 3) == "is_") { + std::string n = std::string (syn->name, 3, std::string::npos); + if (is_property_setter (st, n) && ! is_property_getter (st, n)) { + // synthesize a getter from is_...? predicates (e.g. is_empty? -> empty getter) + add_getter (n, *m); + } + } add_method (syn->name, *m); } } } + } } } @@ -199,6 +223,26 @@ MethodTable::find_property (bool st, const std::string &name) const } } +bool +MethodTable::is_property_setter (bool st, const std::string &name) +{ + std::pair p = find_property (st, name); + if (! p.first) { + return false; + } + return (begin_setters (p.second) != end_setters (p.second)); +} + +bool +MethodTable::is_property_getter (bool st, const std::string &name) +{ + std::pair p = find_property (st, name); + if (! p.first) { + return false; + } + return (begin_getters (p.second) != end_getters (p.second)); +} + /** * @brief Extracts the Python name from a generic name * @@ -324,7 +368,14 @@ static std::string extract_python_name (const std::string &name) void MethodTable::add_method (const std::string &name, const gsi::MethodBase *mb) { - if (name == "to_s" && mb->compatible_with_num_args (0)) { + if (name == "new" && mb->ret_type ().type () == gsi::T_object && mb->ret_type ().pass_obj ()) { + + add_method_basic (name, mb); + + add_method_basic ("__init__", mb, true /*enabled*/, true /*constructor*/); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is the default initializer of the object"))); + + } else if (name == "to_s" && mb->compatible_with_num_args (0)) { add_method_basic (name, mb); @@ -473,6 +524,18 @@ MethodTable::set_enabled (size_t mid, bool en) m_table [mid - m_method_offset].set_enabled (en); } +bool +MethodTable::is_init(size_t mid) const +{ + return m_table [mid - m_method_offset].is_init (); +} + +void +MethodTable::set_init (size_t mid, bool f) +{ + m_table [mid - m_method_offset].set_init (f); +} + bool MethodTable::is_static (size_t mid) const { @@ -561,9 +624,9 @@ MethodTable::finish () } void -MethodTable::add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled) +MethodTable::add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled, bool init) { - bool st = mb->is_static (); + bool st = mb->is_static () && ! init; std::map, size_t>::iterator n = m_name_map.find (std::make_pair (st, name)); if (n == m_name_map.end ()) { @@ -573,6 +636,9 @@ MethodTable::add_method_basic (const std::string &name, const gsi::MethodBase *m if (! enabled) { m_table.back ().set_enabled (false); } + if (init) { + m_table.back ().set_init (true); + } m_table.back ().add (mb); } else { @@ -585,6 +651,9 @@ MethodTable::add_method_basic (const std::string &name, const gsi::MethodBase *m if (! enabled) { m_table [n->second].set_enabled (false); } + if (init) { + tl_assert (m_table [n->second].is_init ()); + } } } @@ -627,167 +696,5 @@ PythonClassClientData::initialize (const gsi::ClassBase &cls_decl, PyTypeObject } } -// -------------------------------------------------------------------------- -// The PythonModule implementation - -std::map PythonModule::m_python_doc; -std::vector PythonModule::m_classes; - -const std::string pymod_name ("klayout"); - -PythonModule::PythonModule () - : mp_mod_def (0) -{ - // .. nothing yet .. -} - -PythonModule::~PythonModule () -{ - PYAObjectBase::clear_callbacks_cache (); - - // the Python objects were probably deleted by Python itself as it exited - - // don't try to delete them again. - mp_module.release (); - - while (!m_methods_heap.empty ()) { - delete m_methods_heap.back (); - m_methods_heap.pop_back (); - } - - while (!m_getseters_heap.empty ()) { - delete m_getseters_heap.back (); - m_getseters_heap.pop_back (); - } - - if (mp_mod_def) { - delete[] mp_mod_def; - mp_mod_def = 0; - } -} - -PyObject * -PythonModule::module () -{ - return mp_module.get (); -} - -PyObject * -PythonModule::take_module () -{ - return mp_module.release (); -} - -void -PythonModule::init (const char *mod_name, const char *description) -{ - // create a (standalone) Python interpreter if we don't have one yet - // NOTE: Python itself will take care to remove this instance in this case. - if (! pya::PythonInterpreter::instance ()) { - new pya::PythonInterpreter (false); - } - - // do some checks before we create the module - tl_assert (mod_name != 0); - tl_assert (mp_module.get () == 0); - - m_mod_name = pymod_name + "." + mod_name; - m_mod_description = description; - - PyObject *module = 0; - -#if PY_MAJOR_VERSION < 3 - - static PyMethodDef module_methods[] = { - {NULL} // Sentinel - }; - - module = Py_InitModule3 (m_mod_name.c_str (), module_methods, m_mod_description.c_str ()); - -#else - - struct PyModuleDef mod_def = { - PyModuleDef_HEAD_INIT, - m_mod_name.c_str (), - NULL, // module documentation - -1, // size of per-interpreter state of the module, - // if the module keeps state in global variables. - NULL - }; - - tl_assert (! mp_mod_def); - - // prepare a persistent structure with the module definition - // and pass this one to PyModule_Create - mp_mod_def = new char[sizeof (PyModuleDef)]; - memcpy ((void *) mp_mod_def, (const void *) &mod_def, sizeof (PyModuleDef)); - - module = PyModule_Create ((PyModuleDef *) mp_mod_def); - -#endif - - mp_module = PythonRef (module); -} - -void -PythonModule::init (const char *mod_name, PyObject *module) -{ - // do some checks before we create the module - tl_assert (mp_module.get () == 0); - - m_mod_name = mod_name; - mp_module = PythonRef (module); -} - -PyMethodDef * -PythonModule::make_method_def () -{ - static PyMethodDef md = { }; - m_methods_heap.push_back (new PyMethodDef (md)); - return m_methods_heap.back (); -} - -PyGetSetDef * -PythonModule::make_getset_def () -{ - static PyGetSetDef gsd = { }; - m_getseters_heap.push_back (new PyGetSetDef (gsd)); - return m_getseters_heap.back (); -} - -char * -PythonModule::make_string (const std::string &s) -{ - m_string_heap.push_back (s); - return const_cast (m_string_heap.back ().c_str ()); -} - -void -PythonModule::add_python_doc (const gsi::ClassBase & /*cls*/, const MethodTable *mt, int mid, const std::string &doc) -{ - for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) { - std::string &doc_string = m_python_doc [*m]; - doc_string += doc; - doc_string += "\n\n"; - } -} - -void -PythonModule::add_python_doc (const gsi::MethodBase *m, const std::string &doc) -{ - m_python_doc [m] += doc; -} - -std::string -PythonModule::python_doc (const gsi::MethodBase *method) -{ - std::map::const_iterator d = m_python_doc.find (method); - if (d != m_python_doc.end ()) { - return d->second; - } else { - return std::string (); - } -} - - } diff --git a/src/pya/pya/pyaInternal.h b/src/pya/pya/pyaInternal.h index f013b62da..2b8d0d128 100644 --- a/src/pya/pya/pyaInternal.h +++ b/src/pya/pya/pyaInternal.h @@ -67,8 +67,10 @@ public: void set_enabled (bool en); bool is_enabled () const; - bool is_static () const; + void set_init(bool f); + bool is_init () const; + bool is_static () const; bool is_protected () const; void add (const gsi::MethodBase *m); @@ -78,11 +80,17 @@ public: method_iterator begin () const; method_iterator end () const; + const std::vector &methods () const + { + return m_methods; + } + private: std::string m_name; bool m_is_static : 1; bool m_is_protected : 1; bool m_is_enabled : 1; + bool m_is_init : 1; std::vector m_methods; }; @@ -161,6 +169,16 @@ public: */ void set_enabled (size_t mid, bool en); + /** + * @brief Returns true if the method is an initializer + */ + bool is_init (size_t mid) const; + + /** + * @brief Sets initializer + */ + void set_init (size_t mid, bool f); + /** * @brief Returns true if the method with the given ID is static */ @@ -228,6 +246,22 @@ public: */ static MethodTable *method_table_by_class (const gsi::ClassBase *cls_decl); + /** + * @brief Gets the method table + */ + const std::vector &method_table () const + { + return m_table; + } + + /** + * @brief Gets the property table + */ + const std::vector > &property_table () const + { + return m_property_table; + } + private: size_t m_method_offset; size_t m_property_offset; @@ -238,7 +272,9 @@ private: std::vector > m_property_table; PythonModule *mp_module; - void add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled = true); + void add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled = true, bool init = false); + bool is_property_setter (bool st, const std::string &name); + bool is_property_getter (bool st, const std::string &name); }; struct PythonClassClientData diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 4900c6ac2..f98f31359 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -541,7 +541,11 @@ public: PyMethodDef *method = mp_module->make_method_def (); method->ml_name = mp_module->make_string (name); - method->ml_meth = (PyCFunction) get_method_adaptor (mid); + if (mt->is_init (mid)) { + method->ml_meth = (PyCFunction) get_method_init_adaptor (mid); + } else { + method->ml_meth = (PyCFunction) get_method_adaptor (mid); + } method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; @@ -569,22 +573,6 @@ public: } else if (! as_static) { // Class methods - if (m_first->ret_type ().type () == gsi::T_object && m_first->ret_type ().pass_obj () && name == "new") { - - // The constructor is also routed via the pya_object_init implementation - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is the default initializer of the object"))); - - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__init__"; - method->ml_meth = (PyCFunction) get_method_init_adaptor (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, method->ml_name, attr); - - } - PyMethodDef *method = mp_module->make_method_def (); method->ml_name = mp_module->make_string (name); method->ml_meth = (PyCFunction) get_method_adaptor (mid);