diff options
author | Christian Tismer <tismer@stackless.com> | 2020-09-27 14:39:25 +0200 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2020-09-29 09:27:53 +0200 |
commit | 11179f639531c1c880498196382678b31974247d (patch) | |
tree | d1695067feb3fea621a1ee79d615f1263a2edb7f | |
parent | 50247e7d4a67e892a85dff7472f889bac97c91c7 (diff) |
Feature-select: Implement signature-awareness of snake_case
After implementing selectable features, support from the signature
module was quite much missing. It was not clear for some time what
to do the best.
It turned out to have the smallest impact and runtime penalty
to use the Python parser output dictionaries and create copies
with snake case naming. That has almost no overhead.
Also, it was necessary to augment the internal map_dict with
snake_case versions. It may be possible to simplify that map_dict
further in another check-in.
Remaining is the problem of static properties. This will be tried
using the PySide Property objects which can be improved.
Change-Id: Ied83ccb197a3c15932c4202b5f1ade772416e17b
Task-number: PYSIDE-1019
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
7 files changed, 85 insertions, 33 deletions
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp index a1ba76251..b26810add 100644 --- a/sources/pyside2/libpyside/feature_select.cpp +++ b/sources/pyside2/libpyside/feature_select.cpp @@ -546,40 +546,17 @@ static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, in // Feature 0x02: Use true properties instead of getters and setters // -static PyObject *createProperty(PyObject *getter, PyObject *setter, PyObject *doc) +static PyObject *createProperty(PyObject *getter, PyObject *setter) { assert(getter != nullptr); if (setter == nullptr) setter = Py_None; - PyObject *deleter = Py_None; PyObject *prop = PyObject_CallObject(reinterpret_cast<PyObject *>(&PyProperty_Type), nullptr); - AutoDecRef args(Py_BuildValue("OOOO", getter, setter, deleter, doc)); + AutoDecRef args(Py_BuildValue("OO", getter, setter)); PyProperty_Type.tp_init(prop, args, nullptr); return prop; } -static PyObject *calcPropDocString(PyTypeObject *type, PyObject *getterName, PyObject *setterName) -{ - // To calculate the docstring, we need the __doc__ attribute of the original - // getter and setter. We temporatily switch back to no features. This - // might change when we have full signature support for features. - auto hold = type->tp_dict; - moveToFeatureSet(type, fast_id_array[0]); - auto dict = type->tp_dict; - auto getter = PyDict_GetItem(dict, getterName); - auto setter = setterName ? PyDict_GetItem(dict, setterName) : nullptr; - PyObject *buf = PyObject_GetAttr(getter, PyMagicName::doc()); - type->tp_dict = hold; - - if (setter == nullptr) - return buf; - AutoDecRef nl(Py_BuildValue("s", "\n")); - AutoDecRef wdoc(PyObject_GetAttr(setter, PyMagicName::doc())); - String::concat(&buf, nl); - String::concat(&buf, wdoc); - return buf; -} - static QStringList parseFields(const char *propstr) { /* @@ -639,10 +616,7 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in if (setter != nullptr && Py_TYPE(setter) != PepMethodDescr_TypePtr) continue; - PyObject *doc_read = make_snake_case(fields[1], false); - PyObject *doc_write(haveWrite ? make_snake_case(fields[2], false) : nullptr); - AutoDecRef doc(calcPropDocString(type, doc_read, doc_write)); - AutoDecRef PyProperty(createProperty(getter, setter, doc)); + AutoDecRef PyProperty(createProperty(getter, setter)); if (PyProperty.isNull()) return false; if (PyDict_SetItem(prop_dict, name, PyProperty) < 0) diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp index 672be4009..5559d58d6 100644 --- a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp @@ -56,6 +56,8 @@ STATIC_STRING_IMPL(dumps, "dumps") STATIC_STRING_IMPL(fget, "fget") STATIC_STRING_IMPL(fset, "fset") STATIC_STRING_IMPL(loads, "loads") +STATIC_STRING_IMPL(multi, "multi") +STATIC_STRING_IMPL(name, "name") STATIC_STRING_IMPL(result, "result") STATIC_STRING_IMPL(value, "value") STATIC_STRING_IMPL(values, "values") diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h index 09e22b395..b72fa989b 100644 --- a/sources/shiboken2/libshiboken/sbkstaticstrings.h +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h @@ -55,6 +55,8 @@ LIBSHIBOKEN_API PyObject *fset(); LIBSHIBOKEN_API PyObject *f_code(); LIBSHIBOKEN_API PyObject *f_lineno(); LIBSHIBOKEN_API PyObject *loads(); +LIBSHIBOKEN_API PyObject *multi(); +LIBSHIBOKEN_API PyObject *name(); LIBSHIBOKEN_API PyObject *result(); LIBSHIBOKEN_API PyObject *value(); LIBSHIBOKEN_API PyObject *values(); diff --git a/sources/shiboken2/libshiboken/signature/signature.cpp b/sources/shiboken2/libshiboken/signature/signature.cpp index 76a71b00b..085d751aa 100644 --- a/sources/shiboken2/libshiboken/signature/signature.cpp +++ b/sources/shiboken2/libshiboken/signature/signature.cpp @@ -343,6 +343,9 @@ PyObject *PySide_BuildSignatureProps(PyObject *type_key) empty_dict = PyDict_New(); return empty_dict; } + // PYSIDE-1019: Build snake case versions of the functions. + if (insert_snake_case_variants(dict) < 0) + return nullptr; // We replace the arguments by the result dict. if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0) return nullptr; diff --git a/sources/shiboken2/libshiboken/signature/signature_helper.cpp b/sources/shiboken2/libshiboken/signature/signature_helper.cpp index 7e92a9861..2b360c786 100644 --- a/sources/shiboken2/libshiboken/signature/signature_helper.cpp +++ b/sources/shiboken2/libshiboken/signature/signature_helper.cpp @@ -150,6 +150,28 @@ static PyObject *compute_name_key(PyObject *ob) return Py_BuildValue("(OO)", type_key.object(), func_name.object()); } +static PyObject *_func_with_new_name(PyTypeObject *type, + PyMethodDef *meth, + const char *new_name) +{ + /* + * Create a function with a lower case name. + * Note: This is similar to feature_select's methodWithNewName, + * but does not create a descriptor. + * XXX Maybe we can get rid of this, completely? + */ + auto obtype = reinterpret_cast<PyObject *>(type); + int len = strlen(new_name); + auto name = new char[len + 1]; + strcpy(name, new_name); + auto new_meth = new PyMethodDef; + new_meth->ml_name = name; + new_meth->ml_meth = meth->ml_meth; + new_meth->ml_flags = meth->ml_flags; + new_meth->ml_doc = meth->ml_doc; + return PyCFunction_NewEx(new_meth, obtype, nullptr); +} + static int build_name_key_to_func(PyObject *obtype) { auto *type = reinterpret_cast<PyTypeObject *>(obtype); @@ -167,6 +189,17 @@ static int build_name_key_to_func(PyObject *obtype) || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0) return -1; } + // PYSIDE-1019: Now we repeat the same for snake case names. + meth = type->tp_methods; + for (; meth->ml_name != nullptr; meth++) { + const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true)); + AutoDecRef func(_func_with_new_name(type, meth, name)); + AutoDecRef func_name(get_funcname(func)); + AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object())); + if (func.isNull() || name_key.isNull() + || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0) + return -1; + } return 0; } @@ -198,6 +231,46 @@ PyObject *name_key_to_func(PyObject *ob) return ret; } +static PyObject *_build_new_entry(PyObject *new_name, PyObject *value) +{ + PyObject *new_value = PyDict_Copy(value); + PyObject *multi = PyDict_GetItem(value, PyName::multi()); + if (multi != nullptr && Py_TYPE(multi) == &PyList_Type) { + ssize_t len = PyList_Size(multi); + AutoDecRef list(PyList_New(len)); + if (list.isNull()) + return nullptr; + for (int idx = 0; idx < len; ++idx) { + auto multi_entry = PyList_GetItem(multi, idx); + auto dup = PyDict_Copy(multi_entry); + if (PyDict_SetItem(dup, PyName::name(), new_name) < 0) + return nullptr; + if (PyList_SetItem(list, idx, dup) < 0) + return nullptr; + } + if (PyDict_SetItem(new_value, PyName::multi(), list) < 0) + return nullptr; + } else { + if (PyDict_SetItem(new_value, PyName::name(), new_name) < 0) + return nullptr; + } + return new_value; +} + +int insert_snake_case_variants(PyObject *dict) +{ + AutoDecRef snake_dict(PyDict_New()); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + AutoDecRef name(String::getSnakeCaseName(key, true)); + AutoDecRef new_value(_build_new_entry(name, value)); + if (PyDict_SetItem(snake_dict, name, new_value) < 0) + return -1; + } + return PyDict_Merge(dict, snake_dict, 0); +} + PyObject *_get_class_of_cf(PyObject *ob_cf) { PyObject *selftype = PyCFunction_GET_SELF(ob_cf); diff --git a/sources/shiboken2/libshiboken/signature/signature_p.h b/sources/shiboken2/libshiboken/signature/signature_p.h index 9444f3e9b..ef7846472 100644 --- a/sources/shiboken2/libshiboken/signature/signature_p.h +++ b/sources/shiboken2/libshiboken/signature/signature_p.h @@ -95,6 +95,7 @@ PyObject *pyside_tp_get___doc__(PyObject *tp); PyObject *_get_qualname(PyObject *ob); int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr); PyObject *name_key_to_func(PyObject *ob); +int insert_snake_case_variants(PyObject *dict); PyObject *_get_class_of_cf(PyObject *ob_cf); PyObject *_get_class_of_sm(PyObject *ob_sm); PyObject *_get_class_of_descr(PyObject *ob); diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 8a814114a..20c791cc1 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -349,7 +349,6 @@ def calculate_props(line): props.annotations = annotations props.varnames = varnames = tuple(tup[0] for tup in arglist) funcname = parsed.funcname - props.fullname = funcname shortname = funcname[funcname.rindex(".")+1:] props.name = shortname props.multi = parsed.multi @@ -366,7 +365,6 @@ def fix_variables(props, line): if retvar and isinstance(retvar, (ResultVariable, ArrayLikeVariable)): # Special case: a ResultVariable which is the result will always be an array! annos["return"] = retvar = typing.List[retvar.type] - fullname = props.fullname varnames = list(props.varnames) defaults = list(props.defaults) diff = len(varnames) - len(defaults) @@ -456,8 +454,7 @@ def pyside_type_init(type_key, sig_strings): multi_props.append(props) if multi > 0: continue - fullname = props.pop("fullname") - multi_props = {"multi": multi_props, "fullname": fullname} + multi_props = {"multi": multi_props} ret[shortname] = multi_props dprint(multi_props) multi_props = [] |