aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside2/libpyside/feature_select.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside2/libpyside/feature_select.cpp')
-rw-r--r--sources/pyside2/libpyside/feature_select.cpp766
1 files changed, 0 insertions, 766 deletions
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp
deleted file mode 100644
index 660547d99..000000000
--- a/sources/pyside2/libpyside/feature_select.cpp
+++ /dev/null
@@ -1,766 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt for Python.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "feature_select.h"
-#include "pyside.h"
-#include "pysidestaticstrings.h"
-#include "class_property.h"
-
-#include <shiboken.h>
-#include <sbkstaticstrings.h>
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// PYSIDE-1019: Support switchable extensions
-//
-// This functionality is no longer implemented in the signature module, since
-// the PyCFunction getsets do not have to be modified any longer.
-// Instead, we simply exchange the complete class dicts. This is done in the
-// basewrapper.cpp file and in every generated `tp_(get|set)attro`.
-//
-// This is the general framework of the switchable extensions.
-// A maximum of eight features is planned so far. This seems to be enough.
-// More features are possible, but then we must somehow register the
-// extra `select_id`s above 255.
-//
-
-/*****************************************************************************
-
- How Does This Feature Selection Work?
- -------------------------------------
-
-The basic idea is to replace the `tp_dict` of a QObject derived type.
-This way, we can replace the methods of the class in no time.
-
-The crucial point to understand is how the `tp_dict` is actually accessed:
-When you type "QObject.__dict__", the descriptor of `SbkObjectType_Type`
-is called. This descriptor is per default unassigned, so the base class
-PyType_Type provides the tp_getset method `type_dict`:
-
- static PyObject *
- type_dict(PyTypeObject *type, void *context)
- {
- if (type->tp_dict == NULL) {
- Py_RETURN_NONE;
- }
- return PyDictProxy_New(type->tp_dict);
- }
-
-In order to change that, we need to insert our own version into SbkObjectType:
-
- static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context)
- {
- auto dict = type->tp_dict;
- if (dict == NULL)
- Py_RETURN_NONE;
- if (SelectFeatureSet != nullptr)
- dict = SelectFeatureSet(type);
- return PyDictProxy_New(dict);
- }
-
-This way, the Python function `type_ready()` does not fill in the default,
-but uses our modified version. It a similar way, we overwrite type_getattro
-with our own version, again in SbkObjectType, replacing the default of
-PyType_Type.
-
-Now we can exchange the dict with a customized version.
-We have our own derived type `ChameleonDict` with additional attributes.
-These allow us to create a ring of dicts which can be rotated to the actual
-needed dict version:
-
-Every dict has a field `select_id` which is selected by the `from __feature__`
-import. The dicts are cyclic connected by the `dict_ring` field.
-
-When a class dict is required, now always `SelectFeatureSet` is called, which
-looks into the `__name__` attribute of the active module and decides which
-version of `tp_dict` is needed. Then the right dict is searched in the ring
-and created if not already there.
-
-Furthermore, we need to overwrite every `tp_(get|set)attro` with a version
-that switches dicts right before looking up methods.
-The dict changing must walk the whole `tp_mro` in order to change all names.
-
-This is everything that the following code does.
-
-*****************************************************************************/
-
-
-namespace PySide { namespace Feature {
-
-using namespace Shiboken;
-
-typedef bool(*FeatureProc)(PyTypeObject *type, PyObject *prev_dict, int id);
-
-static FeatureProc *featurePointer = nullptr;
-
-static PyObject *cached_globals = nullptr;
-static PyObject *last_select_id = nullptr;
-
-static PyObject *_fast_id_array[1 + 256] = {};
-// this will point to element 1 to allow indexing from -1
-static PyObject **fast_id_array;
-
-static inline PyObject *getFeatureSelectId()
-{
- static PyObject *undef = fast_id_array[-1];
- static PyObject *feature_dict = GetFeatureDict();
- // these things are all borrowed
- PyObject *globals = PyEval_GetGlobals();
- if ( globals == nullptr
- || globals == cached_globals)
- return last_select_id;
-
- PyObject *modname = PyDict_GetItem(globals, PyMagicName::name());
- if (modname == nullptr)
- return last_select_id;
-
- PyObject *select_id = PyDict_GetItem(feature_dict, modname);
- if ( select_id == nullptr
- || !PyInt_Check(select_id) // int/long cheating
- || select_id == undef)
- return last_select_id;
-
- cached_globals = globals;
- last_select_id = select_id;
- assert(PyInt_AsSsize_t(select_id) >= 0);
- return select_id;
-}
-
-// Create a derived dict class
-static PyTypeObject *
-createDerivedDictType()
-{
- // It is not easy to create a compatible dict object with the
- // limited API. Easier is to use Python to create a derived
- // type and to modify that a bit from the C code.
- PyObject *ChameleonDict = PepRun_GetResult(R"CPP(if True:
-
- class ChameleonDict(dict):
- __slots__ = ("dict_ring", "select_id")
-
- result = ChameleonDict
-
- )CPP");
- return reinterpret_cast<PyTypeObject *>(ChameleonDict);
-}
-
-static PyTypeObject *new_dict_type = nullptr;
-
-static void ensureNewDictType()
-{
- if (new_dict_type == nullptr) {
- new_dict_type = createDerivedDictType();
- if (new_dict_type == nullptr)
- Py_FatalError("PySide2: Problem creating ChameleonDict");
- }
-}
-
-static inline PyObject *nextInCircle(PyObject *dict)
-{
- // returns a borrowed ref
- AutoDecRef next_dict(PyObject_GetAttr(dict, PyName::dict_ring()));
- return next_dict;
-}
-
-static inline void setNextDict(PyObject *dict, PyObject *next_dict)
-{
- PyObject_SetAttr(dict, PyName::dict_ring(), next_dict);
-}
-
-static inline void setSelectId(PyObject *dict, PyObject *select_id)
-{
- PyObject_SetAttr(dict, PyName::select_id(), select_id);
-}
-
-static inline PyObject *getSelectId(PyObject *dict)
-{
- auto select_id = PyObject_GetAttr(dict, PyName::select_id());
- return select_id;
-}
-
-static inline void setCurrentSelectId(PyTypeObject *type, PyObject *select_id)
-{
- SbkObjectType_SetReserved(type, PyInt_AsSsize_t(select_id)); // int/long cheating
-}
-
-static inline void setCurrentSelectId(PyTypeObject *type, int id)
-{
- SbkObjectType_SetReserved(type, id);
-}
-
-static inline PyObject *getCurrentSelectId(PyTypeObject *type)
-{
- int id = SbkObjectType_GetReserved(type);
- // This can be too early.
- if (id < 0)
- id = 0;
- return fast_id_array[id];
-}
-
-static bool replaceClassDict(PyTypeObject *type)
-{
- /*
- * Replace the type dict by the derived ChameleonDict.
- * This is mandatory for all type dicts when they are touched.
- */
- ensureNewDictType();
- PyObject *dict = type->tp_dict;
- auto ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
- PyObject *new_dict = PyObject_CallObject(ob_ndt, nullptr);
- if (new_dict == nullptr || PyDict_Update(new_dict, dict) < 0)
- return false;
- // Insert the default id. Cannot fail for small numbers.
- AutoDecRef select_id(PyInt_FromLong(0));
- setSelectId(new_dict, select_id);
- // insert the dict into itself as ring
- setNextDict(new_dict, new_dict);
- // We have now an exact copy of the dict with a new type.
- // Replace `__dict__` which usually has refcount 1 (but see cyclic_test.py)
- Py_DECREF(type->tp_dict);
- type->tp_dict = new_dict;
- return true;
-}
-
-static bool addNewDict(PyTypeObject *type, PyObject *select_id)
-{
- /*
- * Add a new dict to the ring and set it as `type->tp_dict`.
- * A 'false' return is fatal.
- */
- auto dict = type->tp_dict;
- auto ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
- auto new_dict = PyObject_CallObject(ob_ndt, nullptr);
- if (new_dict == nullptr)
- return false;
- setSelectId(new_dict, select_id);
- // insert the dict into the ring
- auto next_dict = nextInCircle(dict);
- setNextDict(dict, new_dict);
- setNextDict(new_dict, next_dict);
- type->tp_dict = new_dict;
- return true;
-}
-
-static bool moveToFeatureSet(PyTypeObject *type, PyObject *select_id)
-{
- /*
- * Rotate the ring to the given `select_id` and return `true`.
- * If not found, stay at the current position and return `false`.
- */
- auto initial_dict = type->tp_dict;
- auto dict = initial_dict;
- do {
- dict = nextInCircle(dict);
- AutoDecRef current_id(getSelectId(dict));
- // This works because small numbers are singleton objects.
- if (current_id == select_id) {
- type->tp_dict = dict;
- setCurrentSelectId(type, select_id);
- return true;
- }
- } while (dict != initial_dict);
- type->tp_dict = initial_dict;
- return false;
-}
-
-static bool createNewFeatureSet(PyTypeObject *type, PyObject *select_id)
-{
- /*
- * Create a new feature set.
- * A `false` return value is a fatal error.
- *
- * A FeatureProc sees an empty `type->tp_dict` and the previous dict
- * content in `prev_dict`. It is responsible of filling `type->tp_dict`
- * with modified content.
- */
- static auto small_1 = PyInt_FromLong(255);
- Q_UNUSED(small_1);
- static auto small_2 = PyInt_FromLong(255);
- Q_UNUSED(small_2);
- // make sure that small integers are cached
- assert(small_1 != nullptr && small_1 == small_2);
-
- static auto zero = fast_id_array[0];
- bool ok = moveToFeatureSet(type, zero);
- Q_UNUSED(ok);
- assert(ok);
-
- AutoDecRef prev_dict(type->tp_dict);
- Py_INCREF(prev_dict); // keep the first ref unchanged
- if (!addNewDict(type, select_id))
- return false;
- auto id = PyInt_AsSsize_t(select_id); // int/long cheating
- if (id == -1)
- return false;
- setCurrentSelectId(type, id);
- FeatureProc *proc = featurePointer;
- for (int idx = id; *proc != nullptr; ++proc, idx >>= 1) {
- if (idx & 1) {
- // clear the tp_dict that will get new content
- PyDict_Clear(type->tp_dict);
- // let the proc re-fill the tp_dict
- if (!(*proc)(type, prev_dict, id))
- return false;
- // if there is still a step, prepare `prev_dict`
- if (idx >> 1) {
- prev_dict.reset(PyDict_Copy(type->tp_dict));
- if (prev_dict.isNull())
- return false;
- }
- }
- }
- return true;
-}
-
-static bool SelectFeatureSetSubtype(PyTypeObject *type, PyObject *select_id)
-{
- /*
- * This is the selector for one sublass. We need to call this for
- * every subclass until no more subclasses or reaching the wanted id.
- */
- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) {
- // On first touch, we initialize the dynamic naming.
- // The dict type will be replaced after the first call.
- if (!replaceClassDict(type)) {
- Py_FatalError("failed to replace class dict!");
- return false;
- }
- }
- if (!moveToFeatureSet(type, select_id)) {
- if (!createNewFeatureSet(type, select_id)) {
- Py_FatalError("failed to create a new feature set!");
- return false;
- }
- }
- return true;
-}
-
-static inline PyObject *SelectFeatureSet(PyTypeObject *type)
-{
- /*
- * This is the main function of the module.
- * The purpose of this function is to switch the dict of a class right
- * before a (get|set)attro call is performed.
- *
- * Generated functions call this directly.
- * Shiboken will assign it via a public hook of `basewrapper.cpp`.
- */
- if (Py_TYPE(type->tp_dict) == Py_TYPE(PyType_Type.tp_dict)) {
- // We initialize the dynamic features by using our own dict type.
- if (!replaceClassDict(type))
- return nullptr;
- }
- PyObject *select_id = getFeatureSelectId(); // borrowed
- PyObject *current_id = getCurrentSelectId(type); // borrowed
- static PyObject *undef = fast_id_array[-1];
-
- // PYSIDE-1019: During import PepType_SOTP is still zero.
- if (current_id == undef)
- current_id = select_id = fast_id_array[0];
-
- if (select_id != current_id) {
- PyObject *mro = type->tp_mro;
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
- // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
- for (idx = 0; idx < n - 2; idx++) {
- auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
- // When any subtype is already resolved (false), we can stop.
- if (!SelectFeatureSetSubtype(sub_type, select_id))
- break;
- }
- }
- return type->tp_dict;
-}
-
-// For cppgenerator:
-void Select(PyObject *obj)
-{
- if (featurePointer == nullptr)
- return;
- auto type = Py_TYPE(obj);
- type->tp_dict = SelectFeatureSet(type);
-}
-
-PyObject *Select(PyTypeObject *type)
-{
- if (featurePointer != nullptr)
- type->tp_dict = SelectFeatureSet(type);
- return type->tp_dict;
-}
-
-static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_04_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_08_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_10_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_20_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_40_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-static bool feature_80_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id);
-
-static FeatureProc featureProcArray[] = {
- feature_01_addLowerNames,
- feature_02_true_property,
- feature_04_addDummyNames,
- feature_08_addDummyNames,
- feature_10_addDummyNames,
- feature_20_addDummyNames,
- feature_40_addDummyNames,
- feature_80_addDummyNames,
- nullptr
-};
-
-void finalize()
-{
- for (int idx = -1; idx < 256; ++idx)
- Py_DECREF(fast_id_array[idx]);
-}
-
-static bool patch_property_impl();
-
-void init()
-{
- // This function can be called multiple times.
- static bool is_initialized = false;
- if (!is_initialized) {
- fast_id_array = &_fast_id_array[1];
- for (int idx = -1; idx < 256; ++idx)
- fast_id_array[idx] = PyInt_FromLong(idx);
- last_select_id = fast_id_array[0];
- featurePointer = featureProcArray;
- initSelectableFeature(SelectFeatureSet);
- registerCleanupFunction(finalize);
- patch_property_impl();
- PySide::ClassProperty::init();
- is_initialized = true;
- }
- // Reset the cache. This is called at any "from __feature__ import".
- cached_globals = nullptr;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// PYSIDE-1019: Support switchable extensions
-//
-// Feature 0x01: Allow snake_case instead of camelCase
-//
-// This functionality is no longer implemented in the signature module, since
-// the PyCFunction getsets do not have to be modified any longer.
-// Instead, we simply exchange the complete class dicts. This is done in the
-// basewrapper.cpp file.
-//
-
-static PyObject *methodWithNewName(PyTypeObject *type,
- PyMethodDef *meth,
- const char *new_name)
-{
- /*
- * Create a method with a lower case name.
- */
- 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;
- PyObject *descr = nullptr;
- if (new_meth->ml_flags & METH_STATIC) {
- AutoDecRef cfunc(PyCFunction_NewEx(new_meth, obtype, nullptr));
- if (cfunc.isNull())
- return nullptr;
- descr = PyStaticMethod_New(cfunc);
- }
- else {
- descr = PyDescr_NewMethod(type, new_meth);
- }
- return descr;
-}
-
-static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int id)
-{
- /*
- * Add objects with lower names to `type->tp_dict` from 'prev_dict`.
- */
- PyObject *lower_dict = type->tp_dict;
- PyObject *key, *value;
- Py_ssize_t pos = 0;
-
- // We first copy the things over which will not be changed:
- while (PyDict_Next(prev_dict, &pos, &key, &value)) {
- if ( Py_TYPE(value) != PepMethodDescr_TypePtr
- && Py_TYPE(value) != PepStaticMethod_TypePtr) {
- if (PyDict_SetItem(lower_dict, key, value))
- return false;
- continue;
- }
- }
- // Then we walk over the tp_methods to get all methods and insert
- // them with changed names.
- PyMethodDef *meth = type->tp_methods;
- if (!meth)
- return true;
-
- for (; meth != nullptr && meth->ml_name != nullptr; ++meth) {
- const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true));
- AutoDecRef new_method(methodWithNewName(type, meth, name));
- if (new_method.isNull())
- return false;
- if (PyDict_SetItemString(lower_dict, name, new_method) < 0)
- return false;
- }
- return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// PYSIDE-1019: Support switchable extensions
-//
-// Feature 0x02: Use true properties instead of getters and setters
-//
-
-// This is the Python 2 version for inspection of m_ml, only.
-// The actual Python 3 version is larget.
-
-typedef struct {
- PyObject_HEAD
- PyMethodDef *m_ml; /* Description of the C function to call */
- PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
- PyObject *m_module; /* The __module__ attribute, can be anything */
-} PyCFunctionObject;
-
-static PyObject *modifyStaticToClassMethod(PyTypeObject *type, PyObject *sm)
-{
- AutoDecRef func_ob(PyObject_GetAttr(sm, PyMagicName::func()));
- if (func_ob.isNull())
- return nullptr;
- auto func = reinterpret_cast<PyCFunctionObject *>(func_ob.object());
- auto new_func = new PyMethodDef;
- new_func->ml_name = func->m_ml->ml_name;
- new_func->ml_meth = func->m_ml->ml_meth;
- new_func->ml_flags = (func->m_ml->ml_flags & ~METH_STATIC) | METH_CLASS;
- new_func->ml_doc = func->m_ml->ml_doc;
- auto cfunc = PyCFunction_NewEx(new_func, nullptr, nullptr);
- cfunc = PyDescr_NewClassMethod(type, new_func);
- return cfunc;
-}
-
-static PyObject *createProperty(PyTypeObject *type, PyObject *getter, PyObject *setter)
-{
- bool chassprop = false;
- assert(getter != nullptr);
- if (setter == nullptr)
- setter = Py_None;
- auto ptype = &PyProperty_Type;
- if (Py_TYPE(getter) == PepStaticMethod_TypePtr) {
- ptype = PyClassPropertyTypeF();
- chassprop = true;
- getter = modifyStaticToClassMethod(type, getter);
- if (setter != Py_None)
- setter = modifyStaticToClassMethod(type, setter);
- }
- auto obtype = reinterpret_cast<PyObject *>(ptype);
- PyObject *prop = PyObject_CallFunctionObjArgs(obtype, getter, setter, nullptr);
- return prop;
-}
-
-static QStringList parseFields(const char *propstr)
-{
- /*
- * Break the string into subfields at ':' and add defaults.
- */
- QString s = QString(QLatin1String(propstr));
- auto list = s.split(QLatin1Char(':'));
- assert(list.size() == 2 || list.size() == 3);
- auto name = list[0];
- auto read = list[1];
- if (read.size() == 0)
- list[1] = name;
- if (list.size() == 2)
- return list;
- auto write = list[2];
- if (write.size() == 0) {
- list[2] = QLatin1String("set") + name;
- list[2][3] = list[2][3].toUpper();
- }
- return list;
-}
-
-static PyObject *make_snake_case(QString s, bool lower)
-{
- if (s.isNull())
- return nullptr;
- return String::getSnakeCaseName(s.toLatin1().data(), lower);
-}
-
-static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id)
-{
- /*
- * Use the property info to create true Python property objects.
- */
-
- // The empty `tp_dict` gets populated by the previous dict.
- PyObject *prop_dict = type->tp_dict;
- if (PyDict_Update(prop_dict, prev_dict) < 0)
- return false;
-
- // We then replace methods by properties.
- bool lower = (id & 0x01) != 0;
- auto props = SbkObjectType_GetPropertyStrings(type);
- if (props == nullptr || *props == nullptr)
- return true;
- for (; *props != nullptr; ++props) {
- auto propstr = *props;
- auto fields = parseFields(propstr);
- bool haveWrite = fields.size() == 3;
- PyObject *name = make_snake_case(fields[0], lower);
- PyObject *read = make_snake_case(fields[1], lower);
- PyObject *write = haveWrite ? make_snake_case(fields[2], lower) : nullptr;
- PyObject *getter = PyDict_GetItem(prev_dict, read);
- if (getter == nullptr || !(Py_TYPE(getter) == PepMethodDescr_TypePtr ||
- Py_TYPE(getter) == PepStaticMethod_TypePtr))
- continue;
- PyObject *setter = haveWrite ? PyDict_GetItem(prev_dict, write) : nullptr;
-
- AutoDecRef PyProperty(createProperty(type, getter, setter));
- if (PyProperty.isNull())
- return false;
- if (PyDict_SetItem(prop_dict, name, PyProperty) < 0)
- return false;
- if (fields[0] != fields[1] && PyDict_GetItem(prop_dict, read))
- if (PyDict_DelItem(prop_dict, read) < 0)
- return false;
- // Theoretically, we need to check for multiple signatures to be exact.
- // But we don't do so intentionally because it would be confusing.
- if (haveWrite && PyDict_GetItem(prop_dict, write))
- if (PyDict_DelItem(prop_dict, write) < 0)
- return false;
- }
- return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// 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.
-//
-// There is now also a class property implementation which inherits
-// from this one.
-//
-
-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;
- return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// PYSIDE-1019: Support switchable extensions
-//
-// Feature 0x04..0x40: A fake switchable option for testing
-//
-
-#define SIMILAR_FEATURE(xx) \
-static bool feature_##xx##_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id) \
-{ \
- PyObject *dict = type->tp_dict; \
- if (PyDict_Update(dict, prev_dict) < 0) \
- return false; \
- if (PyDict_SetItemString(dict, "fake_feature_" #xx, Py_None) < 0) \
- return false; \
- return true; \
-}
-
-SIMILAR_FEATURE(04)
-SIMILAR_FEATURE(08)
-SIMILAR_FEATURE(10)
-SIMILAR_FEATURE(20)
-SIMILAR_FEATURE(40)
-SIMILAR_FEATURE(80)
-
-} // namespace PySide
-} // namespace Feature