aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2020-10-07 19:25:47 +0200
committerChristian Tismer <tismer@stackless.com>2020-10-09 22:51:46 +0200
commit8847a47aad95d7f85d5e184071bf95c44826c4c7 (patch)
treefa68d8a0f6d481b73d305cbb63ca70d289c4be14
parent9d8a918d35c8de3400a5e0471e37981a2514c96f (diff)
feature_select: finish properties and postpone static properties
Static properties are not easy to support. They need an extra property subtype and support in the meta-class. A problem is that the implementation needs to go deeply into the innards of things and need to use `_PyType_Lookup`. That is hard to circumvent when the limited API is used. Therefore, the current implementation will be it for 5.15 . At most we might add a few missing properties through XML. Task-number: PYSIDE-1019 Change-Id: I56a9eabe5f774c1ff04c149227e06318c8bf4f29 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
-rw-r--r--sources/pyside2/libpyside/feature_select.cpp79
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.cpp3
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.h2
3 files changed, 80 insertions, 4 deletions
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp
index b26810add..6a21d168d 100644
--- a/sources/pyside2/libpyside/feature_select.cpp
+++ b/sources/pyside2/libpyside/feature_select.cpp
@@ -445,6 +445,8 @@ void finalize()
Py_DECREF(fast_id_array[idx]);
}
+static bool patch_property_impl();
+
void init()
{
// This function can be called multiple times.
@@ -457,6 +459,7 @@ void init()
featurePointer = featureProcArray;
initSelectableFeature(SelectFeatureSet);
registerCleanupFunction(finalize);
+ patch_property_impl();
is_initialized = true;
}
// Reset the cache. This is called at any "from __feature__ import".
@@ -551,9 +554,8 @@ static PyObject *createProperty(PyObject *getter, PyObject *setter)
assert(getter != nullptr);
if (setter == nullptr)
setter = Py_None;
- PyObject *prop = PyObject_CallObject(reinterpret_cast<PyObject *>(&PyProperty_Type), nullptr);
- AutoDecRef args(Py_BuildValue("OO", getter, setter));
- PyProperty_Type.tp_init(prop, args, nullptr);
+ auto obtype = reinterpret_cast<PyObject *>(&PyProperty_Type);
+ PyObject *prop = PyObject_CallFunctionObjArgs(obtype, getter, setter, nullptr);
return prop;
}
@@ -635,6 +637,77 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in
//////////////////////////////////////////////////////////////////////////////
//
+// These are a number of patches to make Python's property object better
+// suitable for us.
+// We turn `__doc__` into a lazy attribute saving signature initialization.
+//
+// Currently, there is no static extension planned, because _PyType_Lookup
+// and Limited_API are hard to use at the same time.
+//
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *prop_get;
+ PyObject *prop_set;
+ PyObject *prop_del;
+ PyObject *prop_doc;
+ int getter_doc;
+} propertyobject;
+
+static PyObject *property_doc_get(PyObject *self, void *)
+{
+ auto po = reinterpret_cast<propertyobject *>(self);
+
+ if (po->prop_doc != nullptr && po->prop_doc != Py_None) {
+ Py_INCREF(po->prop_doc);
+ return po->prop_doc;
+ }
+ if (po->prop_get) {
+ // PYSIDE-1019: Fetch the default `__doc__` from fget. We do it late.
+ auto txt = PyObject_GetAttr(po->prop_get, PyMagicName::doc());
+ if (txt != nullptr) {
+ Py_INCREF(txt);
+ po->prop_doc = txt;
+ Py_INCREF(txt);
+ return txt;
+ }
+ PyErr_Clear();
+ }
+ Py_RETURN_NONE;
+}
+
+static int property_doc_set(PyObject *self, PyObject *value, void *)
+{
+ auto po = reinterpret_cast<propertyobject *>(self);
+
+ Py_INCREF(value);
+ po->prop_doc = value;
+ return 0;
+}
+
+static PyGetSetDef property_getset[] = {
+ // This gets added to the existing getsets
+ {const_cast<char *>("__doc__"), property_doc_get, property_doc_set, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+static bool patch_property_impl()
+{
+ // Turn `__doc__` into a computed attribute without changing writability.
+ auto gsp = property_getset;
+ auto type = &PyProperty_Type;
+ auto dict = type->tp_dict;
+ AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
+ if (descr.isNull())
+ return false;
+ if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
+ return false;
+ // Replace property_descr_get/set by slightly changed versions
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
// PYSIDE-1019: Support switchable extensions
//
// Feature 0x04..0x40: A fake switchable option for testing
diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp
index 6e4926370..cb8042561 100644
--- a/sources/shiboken2/libshiboken/pep384impl.cpp
+++ b/sources/shiboken2/libshiboken/pep384impl.cpp
@@ -95,6 +95,7 @@ static PyMemberDef probe_members[] = {
#define probe_tp_members probe_members
#define probe_tp_getset probe_getseters
#define probe_tp_descr_get make_dummy(10)
+#define probe_tp_descr_set make_dummy(18)
#define probe_tp_init make_dummy(11)
#define probe_tp_alloc make_dummy(12)
#define probe_tp_new make_dummy(13)
@@ -118,6 +119,7 @@ static PyType_Slot typeprobe_slots[] = {
{Py_tp_members, probe_tp_members},
{Py_tp_getset, probe_tp_getset},
{Py_tp_descr_get, probe_tp_descr_get},
+ {Py_tp_descr_set, probe_tp_descr_set},
{Py_tp_init, probe_tp_init},
{Py_tp_alloc, probe_tp_alloc},
{Py_tp_new, probe_tp_new},
@@ -167,6 +169,7 @@ check_PyTypeObject_valid()
|| !PyDict_Check(check->tp_dict)
|| !PyDict_GetItemString(check->tp_dict, "dummy")
|| probe_tp_descr_get != check->tp_descr_get
+ || probe_tp_descr_set != check->tp_descr_set
|| probe_tp_dictoffset != typetype->tp_dictoffset
|| probe_tp_init != check->tp_init
|| probe_tp_alloc != check->tp_alloc
diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h
index 4b3e32ea2..7a6f57fcd 100644
--- a/sources/shiboken2/libshiboken/pep384impl.h
+++ b/sources/shiboken2/libshiboken/pep384impl.h
@@ -115,7 +115,7 @@ typedef struct _typeobject {
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
- void *X33; // descrsetfunc tp_descr_set;
+ descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;