Class properties

This commit is contained in:
Matthias Koefferlein 2022-10-23 23:47:26 +02:00
parent eb31f67aeb
commit 9011af54ac
3 changed files with 106 additions and 123 deletions

View File

@ -310,9 +310,9 @@ def get_py_methods(
ret_type = translate_ret_type(getter.ret_type()) ret_type = translate_ret_type(getter.ret_type())
doc = getter.doc() doc = getter.doc()
properties.append( properties.append(
MethodStub( PropertyStub(
decorator="@property", decorator="",
signature=f"def {name}(self) -> ClassVar[{ret_type}]", signature=f"{name}: ClassVar[{ret_type}]",
name=name, name=name,
docstring=doc, docstring=doc,
) )

View File

@ -28,6 +28,8 @@
#include "tlLog.h" #include "tlLog.h"
#include <cctype>
namespace pya namespace pya
{ {
@ -155,10 +157,14 @@ MethodTable::MethodTable (const gsi::ClassBase *cls_decl, PythonModule *module)
if (! (*m)->is_callback () && ! (*m)->is_signal ()) { if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
bool st = (*m)->is_static (); bool st = (*m)->is_static ();
bool no_args = ((*m)->end_arguments () == (*m)->begin_arguments ());
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
if (! syn->is_getter && ! syn->is_setter) { if (! syn->is_getter && ! syn->is_setter) {
if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && is_property_setter (st, syn->name) && ! is_property_getter (st, syn->name)) { if (no_args && is_property_setter (st, syn->name) && ! is_property_getter (st, syn->name)) {
add_getter (syn->name, *m);
} else if (st && no_args && (isupper (syn->name [0]) || (*m)->is_const ())) {
// static methods without arguments which start with a capital letter are treated as constants
add_getter (syn->name, *m); add_getter (syn->name, *m);
} else { } else {
if (syn->is_predicate && std::string (syn->name, 0, 3) == "is_") { if (syn->is_predicate && std::string (syn->name, 0, 3) == "is_") {

View File

@ -386,23 +386,23 @@ public:
// produce the properties // produce the properties
if (! as_static) { for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) {
for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) { 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;
MethodTableEntry::method_iterator begin_setters = mt->begin_setters (mid); bool is_static = false;
MethodTableEntry::method_iterator end_setters = mt->end_setters (mid); if (begin_setters != end_setters) {
MethodTableEntry::method_iterator begin_getters = mt->begin_getters (mid); is_static = (*begin_setters)->is_static ();
MethodTableEntry::method_iterator end_getters = mt->end_getters (mid); } else if (begin_getters != end_getters) {
int setter_mid = begin_setters != end_setters ? int (mid) : -1; is_static = (*begin_getters)->is_static ();
int getter_mid = begin_getters != end_getters ? int (mid) : -1; }
bool is_static = false; if (! as_static || is_static) {
if (begin_setters != end_setters) {
is_static = (*begin_setters)->is_static ();
} else if (begin_getters != end_getters) {
is_static = (*begin_getters)->is_static ();
}
const std::string &name = mt->property_name (mid); const std::string &name = mt->property_name (mid);
@ -479,65 +479,63 @@ public:
} }
// collect the names which have been disambiguated static/non-static wise if (! as_static) {
std::vector<std::string> disambiguated_names;
// produce the methods now // collect the names which have been disambiguated static/non-static wise
for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { std::vector<std::string> disambiguated_names;
if (! mt->is_enabled (mid)) { // produce the methods now
continue; for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) {
}
std::string name = mt->name (mid); if (! mt->is_enabled (mid)) {
continue;
// does this method hide a property? -> append "_" in that case
std::pair<bool, size_t> t = mt->find_property (mt->is_static (mid), name);
if (t.first) {
name += "_";
}
// needs static/non-static disambiguation?
t = mt->find_method (! mt->is_static (mid), name);
if (t.first) {
disambiguated_names.push_back (name);
if (mt->is_static (mid)) {
name = "_class_" + name;
} else {
name = "_inst_" + name;
} }
mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name)); std::string name = mt->name (mid);
} else if (is_reserved_word (name)) { // does this method hide a property? -> append "_" in that case
std::pair<bool, size_t> t = mt->find_property (mt->is_static (mid), name);
// drop non-standard names if (t.first) {
if (tl::verbosity () >= 20) { name += "_";
tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name;
} }
name += "_"; // needs static/non-static disambiguation?
t = mt->find_method (! mt->is_static (mid), name);
if (t.first) {
mt->rename (mid, name); disambiguated_names.push_back (name);
mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name)); if (mt->is_static (mid)) {
name = "_class_" + name;
} else {
name = "_inst_" + name;
}
} mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name));
} else if (is_reserved_word (name)) {
// drop non-standard names
if (tl::verbosity () >= 20) {
tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name;
}
name += "_";
mt->rename (mid, name);
mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name));
// create documentation
std::string doc;
for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
if (! doc.empty ()) {
doc = "\n\n";
} }
doc += (*m)->doc ();
}
const gsi::MethodBase *m_first = *mt->begin (mid); // create documentation
std::string doc;
for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
if (! doc.empty ()) {
doc = "\n\n";
}
doc += (*m)->doc ();
}
if (! mt->is_static (mid)) { // Bound methods if (! mt->is_static (mid)) { // Bound methods
if (! as_static) {
PyMethodDef *method = mp_module->make_method_def (); PyMethodDef *method = mp_module->make_method_def ();
method->ml_name = mp_module->make_string (name); method->ml_name = mp_module->make_string (name);
@ -552,42 +550,21 @@ public:
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
set_type_attr (type, name, attr); set_type_attr (type, name, attr);
} } else { // Class methods
} else if (isupper (name [0]) || m_first->is_const ()) { PyMethodDef *method = mp_module->make_method_def ();
method->ml_name = mp_module->make_string (name);
method->ml_meth = (PyCFunction) get_method_adaptor (mid);
method->ml_doc = mp_module->make_string (doc);
method->ml_flags = METH_VARARGS | METH_CLASS;
if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) { PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method));
// static methods without arguments which start with a capital letter are treated as constants
PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name));
desc->type = type;
desc->getter = get_method_adaptor (mid);
PythonRef attr (desc);
set_type_attr (type, name, attr); set_type_attr (type, name, attr);
} else if (tl::verbosity () >= 20) {
tl::warn << "Upper case method name encountered which cannot be used as a Python constant (more than one overload or at least one argument): " << cls->name () << "." << name;
mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python")));
} }
} else if (! as_static) { // Class methods
PyMethodDef *method = mp_module->make_method_def ();
method->ml_name = mp_module->make_string (name);
method->ml_meth = (PyCFunction) get_method_adaptor (mid);
method->ml_doc = mp_module->make_string (doc);
method->ml_flags = METH_VARARGS | METH_CLASS;
PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method));
set_type_attr (type, name, attr);
} }
}
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
@ -663,44 +640,44 @@ public:
} }
} // install the static/non-static dispatcher descriptor
// install the static/non-static dispatcher descriptor std::sort (disambiguated_names.begin (), disambiguated_names.end ());
disambiguated_names.erase (std::unique (disambiguated_names.begin (), disambiguated_names.end ()), disambiguated_names.end ());
std::sort (disambiguated_names.begin (), disambiguated_names.end ()); for (std::vector<std::string>::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) {
disambiguated_names.erase (std::unique (disambiguated_names.begin (), disambiguated_names.end ()), disambiguated_names.end ());
for (std::vector<std::string>::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) { std::pair<bool, size_t> pa;
pa = mt->find_method (true, *a);
std::pair<bool, size_t> pa; if (pa.first) {
pa = mt->find_method (true, *a); mt->alias (pa.second, "_class_" + *a);
if (pa.first) { }
mt->alias (pa.second, "_class_" + *a); pa = mt->find_method (false, *a);
} if (pa.first) {
pa = mt->find_method (false, *a); mt->alias (pa.second, "_inst_" + *a);
if (pa.first) {
mt->alias (pa.second, "_inst_" + *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 ();
if (tl::verbosity () >= 20) {
tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << cls->name ();
} }
} else { 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) {
PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class); // some error -> don't install the disambiguator
PythonRef name (c2python (*a)); Py_XDECREF (attr_inst);
// Note: we use GenericSetAttr since that one allows us setting attributes on built-in types Py_XDECREF (attr_class);
PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc); PyErr_Clear ();
if (tl::verbosity () >= 20) {
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);
}
} }