aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/doc/developer
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/doc/developer')
-rw-r--r--sources/pyside6/doc/developer/adapt_qt.rst54
-rw-r--r--sources/pyside6/doc/developer/add_module.rst61
-rw-r--r--sources/pyside6/doc/developer/add_port_example.rst67
-rw-r--r--sources/pyside6/doc/developer/add_tool.rst51
-rw-r--r--sources/pyside6/doc/developer/documentation.rst73
-rw-r--r--sources/pyside6/doc/developer/enumfeatures_doc.rst160
-rw-r--r--sources/pyside6/doc/developer/extras.rst55
-rw-r--r--sources/pyside6/doc/developer/feature-motivation.rst303
-rw-r--r--sources/pyside6/doc/developer/index.rst35
-rw-r--r--sources/pyside6/doc/developer/limited_api.rst703
-rw-r--r--sources/pyside6/doc/developer/signature_doc.rst361
11 files changed, 1923 insertions, 0 deletions
diff --git a/sources/pyside6/doc/developer/adapt_qt.rst b/sources/pyside6/doc/developer/adapt_qt.rst
new file mode 100644
index 000000000..aec81fb0f
--- /dev/null
+++ b/sources/pyside6/doc/developer/adapt_qt.rst
@@ -0,0 +1,54 @@
+.. _developer-adapt-qt:
+
+Adapt to new Qt versions
+========================
+
+Adapting to source changes
+--------------------------
+
+The dev branch of PySide is switched to a new Qt minor version
+after its API review is finished and the API is stable.
+
+Until that happens, a patch should be continuously developed
+to adapt to this version.
+
+The `new classes page <https://doc-snapshots.qt.io/qt6-6.7/newclasses67.html>`_
+is a good source of information for new API.
+
+New classes and should be added to the type system file (using
+a ``since`` attribute) and ``CMakeList.txt`` file of the respective module.
+
+Should the class not be available on all platforms, the respective
+``QT_CONFIG`` macro needs to be specified in the type system file and
+feature checks need to be added to ``CMakeList.txt`` (see for example
+``QPermission``).
+
+The process consists of running a build and evaluating the log file.
+The script
+`shiboken2tasks.py <https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/scripts/shiboken2tasks.py>`_
+from the *Qt Creator* repository can be used to convert the shiboken warnings
+into a `task file <https://doc.qt.io/qtcreator/creator-task-lists.html>`_
+for display in the build issues pane of *Qt Creator*.
+
+Warnings about new enumerations will be shown there; they should be added
+to type system file using a ``since`` attribute.
+
+Warnings about not finding a function signature for modification
+also need to be handled; mostly this is a sign of a function parameter
+being changed from ``int`` to ``qsizetype`` or similar.
+
+If the build succeeds, a test run should be done.
+
+The Qt source code should be checked for new overloads
+(indicated by ``QT6_DECL_NEW_OVERLOAD_TAIL`` starting from 6.7).
+The resolution needs to be decided for each individual case,
+mostly by removing old functions and using ``<declare-function>``
+to declare new API.
+
+Bumping the version
+-------------------
+
+To instruct ``COIN`` to use the next version of Qt, adapt the files
+``coin/dependencies.yaml`` and/or ``product_dependencies.yaml`` accordingly.
+Next, the wheel names should be changed by adapting
+``sources/shiboken6/.cmake.conf`` and ``sources/pyside6/.cmake.conf``.
diff --git a/sources/pyside6/doc/developer/add_module.rst b/sources/pyside6/doc/developer/add_module.rst
new file mode 100644
index 000000000..2eb962207
--- /dev/null
+++ b/sources/pyside6/doc/developer/add_module.rst
@@ -0,0 +1,61 @@
+.. _developer-add-module:
+
+Add a new module
+================
+
+New modules can be added for many reasons, the most important
+one is when Qt enables or includes a new one for a new release.
+
+Adding the bindings, and documentation are the essentials
+to include new modules, but adding tests and examples is ideal.
+
+Add bindings
+------------
+
+- Find the correct name (look at the include path of Qt).
+- Add the module to the ``coin/dependencies.yaml`` file.
+- Add it to ``sources/pyside6/cmake/PySideHelpers.cmake``.
+- Add it to ``build_scripts/wheel_files.py`` (plugins, translations).
+- Copy an existing module to ``sources/pyside6/PySide6/<name>``.
+- Adapt the ``typesystem.xml`` and ``CMakeList.txt`` (using for example
+ *Qt Creator*'s case-preserving replace function).
+- Make sure the dependencies are correct.
+- Find the exported public classes, add them to the ``typesystem.xml`` file,
+ checking whether they are ``value-type`` or ``object-type``. Add their enums
+ and flags.
+- Add the wrapper files to ``CMakeList.txt``.
+- Create a test dir under ``sources/pyside6/tests`` with an empty
+ ``CMakeList.txt``.
+- Try to build with the module added to the ``--module-subset`` option of
+ ``setup.py``.
+- Watch out for shiboken warnings in the log.
+- Be aware that ``ninja`` mixes stdout and stderr, so, the first warning is
+ typically hidden behind a progress message.
+- A convenient way of doing this is using
+ ``qt-creator/scripts/shiboken2tasks.py`` from the
+ `*Qt Creator* repository <https://code.qt.io/cgit/qt-creator/qt-creator.git>`_
+ converting them to a ``.tasks`` file which can be loaded into *Qt Creator*'s
+ issue pane.
+- Link errors may manifest when ``generate_pyi`` imports the module trying
+ to create signatures. They indicate a missing source file entry
+ or a bug in the module itself.
+
+.. note:: For the build to succeed, the module must follow the Qt convention
+ of using ``#include <QtModule/header.h>`` since module include paths
+ are not passed in PySide.
+
+Distribution
+------------
+
+- Determine to which wheel the module belongs according to
+ `Qt Modules <https://doc.qt.io/qt-6/qtmodules.html>`_.
+- Add the module to ``build_scripts/wheel_files.py`` for use by
+ ``create_wheels.py``.
+- Add the module to one of the ``README.pyside6_*.md`` files.
+
+Add documentation
+-----------------
+
+- Add entry to ``sources/pyside6/doc/modules.rst``.
+- Add a .qdocconf.in file in ``sources/pyside6/doc/qtmodules``.
+- Add module description ``.rst`` file in ``sources/pyside6/doc/extras``.
diff --git a/sources/pyside6/doc/developer/add_port_example.rst b/sources/pyside6/doc/developer/add_port_example.rst
new file mode 100644
index 000000000..b99641f45
--- /dev/null
+++ b/sources/pyside6/doc/developer/add_port_example.rst
@@ -0,0 +1,67 @@
+.. _developer-add-port-example:
+
+Add a new example or port one
+=============================
+
+Adding examples is a good exercise for people wanting to become familiar with
+the modules and its functionality.
+
+You can either design an example from scratch or inspired in another
+application, or simply you can port an existing Qt example that does not have
+a Python counterpart.
+
+Example code should be free of `flake8 <https://pypi.org/project/flake8/>`_
+warnings; this is enforced by a bot. A configuration file is provided
+at the root of the repository. Offending lines can be excluded by a
+``noqa`` directive if there is a good reason to do so.
+
+Keep in mind we do allow 100 columns for line length.
+
+Additionally, please use `isort <https://pypi.org/project/isort/>`_ to keep the
+imports ordered and consistent with other examples.
+
+For example:
+
+.. code-block:: bash
+
+ $ flake8 --config pyside-setup/.flake8 your_file.py
+ $ isort your_file.py
+
+
+
+Add a new example
+-----------------
+
+- Check if the topic your example covers is not in an existing example already.
+- Create a new directory inside the ``examples/<module>`` you think
+ is more relevant.
+- Inside, place the code of the example, and also a ``.pyproject``
+ file listing the files the example needs.
+- If you want the example to be automatically displayed on the
+ example gallery, include a ``doc`` directory that contains a ``rst``
+ file and a screenshot. Check other examples for formatting questions.
+- When writing the ``rst`` file, you can include code snippets using
+ the ``literalinclude`` directive specifying the relative path
+ as listed in the ``.pyproject`` file. The `example_gallery` tool will
+ expand this (see the `pointconfiguration` example).
+- For the code displayed in the tabs, you can create ``rstinc`` files
+ in the ``doc`` directory containing some description explaining them
+ (see the `samplebinding` example).
+
+Port a Qt example
+-----------------
+
+- Quickly check the C++ example, fix outdated code.
+- Port the sources using ``tools/tools/qtcpp2py.py`` (front-end for
+ ``snippets-translate``).
+- Note that our examples need to have unique names due to the doc build.
+- Verify that all slots are decorated using ``@Slot``.
+- Add a ``.pyproject`` file (verify later on that docs build).
+- Add a ``doc`` directory and descriptive ``.rst`` file,
+ and a screenshot if suitable (use ``optipng`` to reduce file size).
+- Add the ``"""Port of the ... example from Qt 6"""`` doc string.
+- Try to port variable and function names to snake case convention.
+- Remove C++ documentation from ``sources/pyside6/doc/additionaldocs.lst``.
+
+.. note:: Example screenshots in ``.png`` should be optimized by
+ running ``optipng -o 7 -strip all``. Alternatively, the ``.webp`` format can be used.
diff --git a/sources/pyside6/doc/developer/add_tool.rst b/sources/pyside6/doc/developer/add_tool.rst
new file mode 100644
index 000000000..732e6b915
--- /dev/null
+++ b/sources/pyside6/doc/developer/add_tool.rst
@@ -0,0 +1,51 @@
+.. _developer-add-tool:
+
+Add a new tool or a Qt tool wrapper
+===================================
+
+Tooling is essential to |project|, for that reason you can find many ad-hoc
+tools in the repository, which include wrappers of Qt tools or newly developed
+tools to solve issues, or improve some project workflows.
+
+Add a new tool
+--------------
+
+Tools not available to end users
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This depicts the tools that are not shipped with Qt for Python wheels and are used to aid
+Qt for Python development
+
+- Place your tool in the ``tools`` directory.
+- If your project has more than one file, create a directory.
+- Create a ``.pyproject`` file including all the relevant files
+ for your tool.
+
+Tools available to end users
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Place your tool in the ``sources/pyside-tools`` directory.
+- If your project has more than one file, create a directory.
+- Create a ``.pyproject`` file including all the relevant files
+ for your tool.
+- Add the relevant files in ``sources/pyside-tools/CMakeLists.txt``.
+- Add the tool in ``sources/pyside-tools/pyside_tool.py``.
+- Add the tool in ``build_scripts/__init__.py`` to create the setuptools entry points
+ i.e. this enable using the tool from the console as "pyside6-<tool_name>"
+- Add an entry to ``sources/pyside6/doc/gettingstarted/package_details.rst``.
+- Include the necessary Qt binaries explicitly on ``build_scripts/wheel_files.py``
+- Build with ``--standalone``, verify it is working.
+
+
+Add a Qt tool wrapper
+---------------------
+
+- Add the relevant files in ``sources/pyside-tools/CMakeLists.txt``.
+- Add the tool in ``sources/pyside-tools/pyside_tool.py``.
+- Add the tool in ``build_scripts/__init__.py`` to create the setuptools entry points
+ i.e. this enable using the tool from the console as "pyside6-<tool_name>"
+- Add an entry to ``sources/pyside6/doc/tools/index.rst`` and the detailed
+ documentation to ``sources/pyside6/doc/tools/<tool_name>.rst``.
+- Include the necessary Qt binaries explicitly on ``build_scripts/wheel_files.py``
+- Add the necessary files to ``build_scripts/wheel_files.py``.
+- Build with ``--standalone``, verify it is working. Also, check if the wheel bundles the tool.
diff --git a/sources/pyside6/doc/developer/documentation.rst b/sources/pyside6/doc/developer/documentation.rst
new file mode 100644
index 000000000..517bd46f1
--- /dev/null
+++ b/sources/pyside6/doc/developer/documentation.rst
@@ -0,0 +1,73 @@
+.. _developer-documentation:
+
+Fixing Documentation issues
+===========================
+
+Fixing texts
+------------
+
+Shiboken's ``<inject-documentation>`` element can be used to add texts.
+It's ``"replace"`` mode is currently not implemented, though.
+
+Fixing snippets
+---------------
+
+Snippets can replaced by placing a Python equivalent under ``sources/pyside6/doc/snippets``.
+The directory structure matches that of Qt. To replace a snippet with the id `0` in
+``qtbase/examples/foo/snippet.cpp``, place a file ``qtbase/examples/foo/snippet_0.cpp.py``
+under that directory (one snippet per file with the snippet id appended to the base name).
+
+More complicated mappings can be added to ``tools/snippets_translate/override.py``.
+
+# Recreating the module descriptions after a Qt major version change
+
+The source tree contains .rst files containing the module description in
+doc/extras (named for example "QtCore.rst"). They are extracted/adapted from
+the C++ module descriptions. If there is no module description file, shiboken
+will extract the module description from the webxml files generated by qdoc.
+This ends up in the build directory under doc/rst/PySide6/&lt;module&gt;/index.rst.
+It can be used as a starting point for a module description file. C++
+specific information like build instructions should be removed.
+
+The descriptions may link to tutorials which can be added to additionaldocs.lst
+for webxml extraction.
+
+Maintaining additionaldocs.lst
+------------------------------
+
+The file is a list of additional documentation files. These are basically Qt
+tutorials referenced by the documentation. They will receive some Python
+adaption by shiboken/sphinx.
+
+The list can be created by the below script and some hand-editing. It will find
+almost all documents. Quite a number of them might be unreferenced, but there
+is no good way of filtering for this.
+Pages of examples that exist in Python should be removed.
+
+.. code-block:: bash
+
+ for F in *.webxml
+ do
+ echo "$F" | egrep '(-index)|(-module)|(-qmlmodule)\.webxml$' > /dev/null
+ if [ $? -ne 0 ]
+ then
+ if fgrep '<para>' "$F" > /dev/null # Exclude reference only
+ then
+ egrep "(<class )|(<namespace )" $F > /dev/null || echo $F
+ fi
+ fi
+ done
+
+Inheritance graphs
+------------------
+
+``inheritance_diagram.pyproject`` lists the script involved in inheritance
+graph generation, ``inheritance_diagram.py`` being the main one used by sphinx.
+The others have main-test drivers for checking.
+
+There are 2 scripts used for determining the inheritance:
+* ``json_inheritance.py`` (env var ``INHERITANCE_FILE``) reads a
+ inheritance.json file containing the class hierarchy generated by
+ shiboken's doc generator.
+
+* ``import_inheritance.py`` actually tries to import the class (legacy)
diff --git a/sources/pyside6/doc/developer/enumfeatures_doc.rst b/sources/pyside6/doc/developer/enumfeatures_doc.rst
new file mode 100644
index 000000000..91b7b6346
--- /dev/null
+++ b/sources/pyside6/doc/developer/enumfeatures_doc.rst
@@ -0,0 +1,160 @@
+.. _enum-features:
+
+The Set of Enum Features
+========================
+
+The development of the new Python enums took the form of a series of patches.
+While we put a lot of effort into supporting the old Enums (without promoting
+them), it is still possible that someone has a case where they cannot use
+the Python enums as they are now. To avoid people setting the environment
+flag to disable this completely, we implemented a way to select each
+combination of enum functions step by step with a specific set of flags.
+
+
+The Possible Enum Flags
+-----------------------
+
+This is the table of all flags used to control the creation of Python enums.
+
+====================== ===== ======================================================
+Flag Name Value
+====================== ===== ======================================================
+ENOPT_OLD_ENUM 0x00 (False) No longer possible since PySide 6.6
+ENOPT_NEW_ENUM 0x01 (True) The default for PySide 6.4, full implementation
+ENOPT_INHERIT_INT 0x02 Turn all Enum into IntEnum and Flag into IntFlag
+ENOPT_GLOBAL_SHORTCUT 0x04 Re-add shortcuts for global enums
+ENOPT_SCOPED_SHORTCUT 0x08 Re-add shortcuts for scoped enums
+ENOPT_NO_FAKESHORTCUT 0x10 Don't fake rename (forgiveness mode)
+ENOPT_NO_FAKERENAMES 0x20 Don't fake shortcuts (forgiveness mode)
+ENOPT_NO_ZERODEFAULT 0x40 Don't use zero default (forgiveness mode)
+ENOPT_NO_MISSING 0x80 Don't allow missing values in Enum
+====================== ===== ======================================================
+
+Such a set of flags can be defined either by the environment variable
+``PYSIDE6_OPTION_PYTHON_ENUM`` or set by the Python variable
+``sys.pyside6_option_python_enum`` before PySide6 is imported.
+The environment variable also supports arbitrary integer expressions
+by using ``ast.literal_eval``.
+
+
+ENOPT_OLD_ENUM (0x00)
+~~~~~~~~~~~~~~~~~~~~~
+
+This option completely disables the new enum implementation.
+Even though this is a valid option, we want to avoid it if possible.
+The goal is to eventually remove the old implementation. To make this
+possible, we have made the individual features of the enum implementation
+accessible as flags. This way, if users report problems, we may be able
+to provide a temporary solution before extending enum support accordingly.
+
+
+ENOPT_NEW_ENUM (0x01)
+~~~~~~~~~~~~~~~~~~~~~
+
+In a perfect world, no one would choose anything other than this default
+setting. Unfortunately, reality is not always like that. That is why
+there are the following flags.
+
+
+The most likely flags needed
+----------------------------
+
+If there are errors, they are likely to be the following: Either implicit
+assumptions are there that require IntEnum, or global enums are used that
+unfortunately cannot be replaced with tricks.
+
+
+ENOPT_INHERIT_INT (0x02)
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+When this flag is set, all ``enum.Enum/enum.Flag`` classes are converted to
+``enum.IntEnum/enum.IntFlag``. This solves the most likely compatibility
+problem when switching to Python enums. The old Shiboken enums always
+inherit from int, but most Python enums do not.
+
+It was a decision of Python developers not to let enums inherit from int by
+default, since no order should be implied. In most cases, inheritance from
+int can be avoided, either by using the value property or better by
+uplifting: instead of using ``AnEnum.AnInstance.value`` in a function that
+expects an int argument, you can also convert the integer to an enumeration
+instance after the call by ``AnEnum(int_arg)`` and use that in comparisons.
+
+However, there are cases where this is not possible, and explicit support in
+PySide is simply not available. In those cases, you can use this flag as a
+workaround until we have implemented alternatives.
+
+
+ENOPT_GLOBAL_SHORTCUT (0x04)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+At the beginning of the Python enum implementation, we continued to support
+the shortcut behavior of Shiboken enums: the enum constants were mirrored
+into the enclosing scope.
+This was later emulated in the course of forgiveness mode. For enum classes
+in a PySide class this works fine, but for enum classes directly on the module
+level there is no good way to implement forgiveness.
+
+It is unlikely that errors are hidden for global enums, because they should
+already produce an error during import. But for cases without access to
+the source code, you can help yourself with this flag.
+
+A flag value of 0x6 is likely to solve the majority of problems.
+
+
+Flags for completeness
+----------------------
+
+The following flags complement the description of Python Enums.
+They essentially serve the better understanding of the
+implementation and make it fully transparent and customizable.
+
+
+ENOPT_SCOPED_SHORTCUT (0x08)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For completeness, we also supported mirroring scoped enums, although this
+has since been replaced by forgiveness mode. If you want to try this,
+please also use the ENOPT_NO_FAKESHORTCUT flag (0x10), otherwise the
+effect of this flag will remain invisible.
+
+
+ENOPT_NO_FAKERENAMES (0x10)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Forgiveness mode emulates renaming ``Enum.Flag`` classes back to Shiboken
+QFlags structures, which have slightly different names.
+So when such a defunct name is used, the system replaces it internally
+with the new ``enum.Flag`` structure. Unless special boundary problems
+are provoked, this replacement should work.
+
+To see the effect of this renaming, you can turn it off with this flag.
+
+
+ENOPT_NO_ZERODEFAULT (0x40)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As part of the forgiveness mode, Python enums can be created by a
+parameterless call, although Python enums actually force a parameter
+when called.
+
+The effect can be examined if this flag is set to disable it.
+
+
+ENOPT_NO_MISSING (0x80)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few cases where Shiboken enums use missing values. In
+``enum.Flag`` structures, this is allowed anyway because we have set the
+``FlagBoundary.KEEP`` flag (see ``enum.py``).
+
+Normal ``enum.Enum`` structures don't have this provision, but the
+``enum`` module allows to pass a ``_missing_`` function for customization.
+
+Our way of dealing with this situation is to create a new fake
+``enum.Enum`` class with the same name and a nameless instance, and
+pretend with an attribute setting that it has the same type.
+The additional instances created in this way are recorded in a class dict
+``_sbk_missing_`` in order to preserve their identity.
+
+You will see the effect of not defining a ``_missing_`` function if you
+set this flag.
diff --git a/sources/pyside6/doc/developer/extras.rst b/sources/pyside6/doc/developer/extras.rst
new file mode 100644
index 000000000..9788b539d
--- /dev/null
+++ b/sources/pyside6/doc/developer/extras.rst
@@ -0,0 +1,55 @@
+Test a wheel
+============
+
+There is a tool that you can use to test a set of wheels called 'testwheel' but
+it's currently in a different repository (``qt/qtqa``):
+
+- Use ``scripts/packagetesting/testwheel.py`` from the
+ `qtqa repository <https://code.qt.io/cgit/qt/qtqa.git>`_.
+
+To test the wheels:
+
+- Create a virtual environment and activate it.
+- Install the dependencies listed on the ``requirements.txt`` file.
+- Install all the wheels: ``shiboken6``, ``shiboken6-generator``,
+ and ``PySide6-Essentials``.
+- Run the ``testwheel`` tool.
+- Install ``PySide6-Addons`` wheels.
+- Run again the ``testwheel`` tool.
+- In case you have access to commercial wheels, don't forget the
+ ``PySide6-M2M`` as well, and re-run the ``testwheel`` tool.
+
+Build on the command line
+=========================
+
+- Consider using ``build_scripts/qp5_tool.py``.
+
+Build with address sanitizer (Linux)
+====================================
+
+ASAN needs to be told to not exit on memory leaks and its library
+needs to be pre-loaded. Assuming the library is found
+at ``/usr/lib/gcc/x86_64-linux-gnu/11``:
+
+.. code-block:: bash
+
+ export ASAN_OPTIONS=detect_leaks=0
+ export LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/11/libasan.so
+ python setup.py build [...] --sanitize-address
+
+De-Virtualize the Python Files
+==============================
+
+The Python files in the Shiboken module are completely virtual, i.E.
+they are nowhere existent in the file system for security reasons.
+
+For debugging purposes or to change something, it might be desirable
+to move these files into the normal file system, again.
+
+- Setting the environment variable "SBK_EMBED" once to false unpacks these
+ files when PySide6 or shiboken6 are imported. The files are written
+ into "side-packages/shiboken6/files.dir" and are used from then on.
+
+- Setting the variable to true removes "files.dir".
+
+- Without the "SBK_EMBED" variable, the embedding status remains sticky.
diff --git a/sources/pyside6/doc/developer/feature-motivation.rst b/sources/pyside6/doc/developer/feature-motivation.rst
new file mode 100644
index 000000000..1509ea724
--- /dev/null
+++ b/sources/pyside6/doc/developer/feature-motivation.rst
@@ -0,0 +1,303 @@
+.. _feature-why:
+
+Why do we have a __feature__?
+=============================
+
+
+History
+-------
+
+In PySide user story PYSIDE-1019, we tested certain ways to
+make PySide more pythonic. The first idea was to support some
+way to allow for ``snake_case`` function names.
+
+This feature is possible with relatively low compatibility
+problems, because having the same function with different names
+would be not so nice, but a possible low-effort solution.
+
+When going to ``true_property``, things become different. When we
+support properties as first class objects instead of getter
+and setter functions, we get a conflict, because a function
+cannot act as a property (no braces) and be a function at the
+same time.
+
+This consideration led us to the idea:
+Features must be selectable per-module.
+
+
+Why are features selectable per-module?
+---------------------------------------
+
+Suppose you have some pre-existing code. Maybe you use some downloaded
+code or you generated an interface file. When you now decide to
+use a feature, you don't want all this existing stuff to become
+incorrect. By using the statement
+
+.. code-block:: python
+
+ from __feature__ import ...
+
+you declare that this module uses some feature. Other modules will not
+be influenced by this decision and can stay unchanged.
+
+
+Why dunder, and why not __future__?
+-----------------------------------
+
+Especially in Python 2, but in a few cases also in Python 3, there is
+the future statement
+
+.. code-block:: python
+
+ from __future__ import ...
+
+That is a statement that can only appear at the beginning of a module,
+and it switches how the Python parser works.
+
+Our first idea was to mimick this behavior for PySide, although we are
+a bit cheating: The feature statement is not a syntactical construct,
+and we cannot easily forbid that it is in the middle of a module.
+
+We then realized that the intention of Python's ``__future__`` import and
+PySide's ``__feature__`` import are different: While Python implies by
+``__future__`` some improvement, we do not want to associate with
+``__feature__``. We simply think that some users who come from Python may
+like our features, while others are used to the C++ convention and
+consider something that deviates from the Qt documentation as drawback.
+
+The intention to use the ``from __feature__ import ...`` notation was the hope that
+people see the similarity to Python's ``__future__`` statement and put that import
+at the beginning of a module to make it very visible that this module
+has some special global differences.
+
+
+The snake_case feature
+======================
+
+By using the statement
+
+.. code-block:: python
+
+ from __feature__ import snake_case
+
+all methods of all classes used in this module are changing their name.
+
+The algorithm to change names is this:
+
+* if the name has less than 3 chars, or
+* if two upper chars are adjacent, or
+* if the name starts with ``gl`` (which marks OpenGL),
+* the name is returned unchanged. Otherwise
+* a single upper char ``C`` is replaced by ``_c``
+
+
+The true_property feature
+=========================
+
+By using the statement
+
+.. code-block:: python
+
+ from __feature__ import true_property
+
+all methods of all classes used in this module which are declared in the Qt
+documentation as property become real properties in Python.
+
+This feature is incompatible with the past and cannot coexist; it is
+the reason why the feature idea was developed at all.
+
+
+Normal Properties
+-----------------
+
+Normal properties have the same name as before:
+
+.. code-block:: python
+
+ QtWidgets.QLabel().color()
+
+becomes as property
+
+.. code-block:: python
+
+ QtWidgets.QLabel().color
+
+When there is also a setter method,
+
+.. code-block:: python
+
+ QtWidgets.QLabel().setColor(value)
+
+becomes as property
+
+.. code-block:: python
+
+ QtWidgets.QLabel().color = value
+
+Normal properties swallow the getter and setter functions and replace
+them by the property object.
+
+
+Special Properties
+------------------
+
+Special properties are those with non-standard names.
+
+.. code-block:: python
+
+ QtWidgets.QLabel().size()
+
+becomes as property
+
+.. code-block:: python
+
+ QtWidgets.QLabel().size
+
+But here we have no setSize function, but
+
+.. code-block:: python
+
+ QtWidgets.QLabel().resize(value)
+
+which becomes as property
+
+.. code-block:: python
+
+ QtWidgets.QLabel().size = value
+
+In that case, the setter does not become swallowed, because so many
+people are used to the ``resize`` function.
+
+
+Class properties
+----------------
+
+It should be mentioned that we not only support regular properties
+as they are known from Python. There is also the concept of class
+properties which always call their getter and setter:
+
+A regular property like the aforementioned ``QtWidgets.QLabel`` has
+this visibility:
+
+.. code-block:: python
+
+ >>> QtWidgets.QLabel.size
+ <property object at 0x113a23540>
+ >>> QtWidgets.QLabel().size
+ PySide6.QtCore.QSize(640, 480)
+
+A class property instead is also evaluated without requiring an instance:
+
+.. code-block:: python
+
+ >>> QtWidgets.QApplication.windowIcon
+ <PySide6.QtGui.QIcon(null) at 0x113a211c0>
+
+You can only inspect it if you go directly to the right class dict:
+
+.. code-block:: python
+
+ >>> QtGui.QGuiApplication.__dict__["windowIcon"]
+ <PySide6.PyClassProperty object at 0x114fc5270>
+
+
+About Property Completeness
+---------------------------
+
+There are many properties where the Python programmer agrees that these
+functions should be properties, but a few are not properties, like
+
+.. code-block:: python
+
+ >>> QtWidgets.QMainWindow.centralWidget
+ <method 'centralWidget' of 'PySide6.QtWidgets.QMainWindow' objects>
+
+We are currently discussing if we should correct these rare cases, as they
+are probably only omissions. Having to memorize the missing properties
+seems to be quite cumbersome, and instead of looking all properties up in
+the Qt documentation, it would be easier to add all properties that
+should be properties and are obviously missing.
+
+
+Name Clashes and Solution
+-------------------------
+
+There are some rare cases where a property already exists as a function,
+either with multiple signatures or having parameters.
+This is not very nice in C++ as well, but for Python this is forbidden.
+Example:
+
+.. code-block:: python
+
+ >>> from PySide6 import *
+ >>> from PySide6.support.signature import get_signature
+ >>> import pprint
+ >>> pprint.pprint(get_signature(QtCore.QTimer.singleShot))
+ [<Signature (arg__1: int, arg__2: Callable) -> None>,
+ <Signature (msec: int, receiver: PySide6.QtCore.QObject, member: bytes) -> None>,
+ <Signature (msec: int, timerType: PySide6.QtCore.Qt.TimerType,
+ receiver: PySide6.QtCore.QObject, member: bytes) -> None>]
+
+When creating this property, we respect the existing function and use a slightly
+different name for the property by appending an underscore.
+
+.. code-block:: python
+
+ >>> from __feature__ import true_property
+ >>> QtCore.QTimer.singleShot_
+ <property object at 0x118e5f8b0>
+
+We hope that these clashes can be removed in future Qt versions.
+
+
+The __feature__ import
+======================
+
+The implementation of ``from __feature__ import ...`` is built by a slight
+modification of the ``__import__`` builtin. We made that explicit by assigning
+variables in the builtin module. This modification takes place at |project|
+import time:
+
+* The original function in ``__import__`` is kept in ``__orig_import__``.
+* The new function is in ``__feature_import__`` and assigned to ``__import__``.
+
+This function calls the Python function ``PySide6.support.__feature__.feature_import``
+first, and falls back to ``__orig_import__`` if feature import is not applicable.
+
+
+Overriding __import__
+---------------------
+
+This is not recommended. Import modifications should be done using import hooks,
+see the Python documentation on `Import-Hooks`_.
+
+If you would like to modify ``__import__`` anyway without destroying the features,
+please override just the ``__orig_import__`` function.
+
+
+IDEs and Modifying Python stub files
+------------------------------------
+
+|project| comes with pre-generated ``.pyi`` stub files in the same location as
+the binary module. For instance, in the site-packages directory, you can find
+a ``QtCore.pyi`` file next to ``QtCore.abi3.so`` or ``QtCore.pyd`` on Windows.
+
+When using ``__feature__`` often with common IDEs, you may want to provide
+a feature-aware version of ``.pyi`` files to get a correct display. The simplest
+way to change them all in-place is the command:
+
+.. code-block:: bash
+
+ pyside6-genpyi all --feature snake_case true_property
+
+
+Using __feature__ with UIC files
+--------------------------------
+
+Features can be freely used together with generated UIC files. The UIC files
+are _not_ converted, intentionally. Mixing them with feature selections in other
+Python modules should always work, because switching will happen as needed, selected
+by the currently active module. (Please report to us if this fails for an example)
+
+
+.. _`Import-Hooks`: https://docs.python.org/3/reference/import.html#import-hooks
diff --git a/sources/pyside6/doc/developer/index.rst b/sources/pyside6/doc/developer/index.rst
new file mode 100644
index 000000000..92c84259d
--- /dev/null
+++ b/sources/pyside6/doc/developer/index.rst
@@ -0,0 +1,35 @@
+.. _developer-notes:
+
+Developer Notes
+===============
+
+Developing |project| requires people to understand different processes
+and steps that need to be taken into account when dealing with topics
+related to modules, bindings, examples, and more.
+
+Development Topics
+------------------
+
+.. toctree::
+ :maxdepth: 2
+
+ add_module.rst
+ add_port_example.rst
+ add_tool.rst
+ documentation.rst
+ adapt_qt.rst
+ extras.rst
+
+Implementation details
+----------------------
+
+Complementary, here you can find the reasoning and motivation for the
+many features and implementation details that the project has:
+
+.. toctree::
+ :maxdepth: 2
+
+ enumfeatures_doc.rst
+ limited_api.rst
+ signature_doc.rst
+ feature-motivation.rst
diff --git a/sources/pyside6/doc/developer/limited_api.rst b/sources/pyside6/doc/developer/limited_api.rst
new file mode 100644
index 000000000..44d3faad2
--- /dev/null
+++ b/sources/pyside6/doc/developer/limited_api.rst
@@ -0,0 +1,703 @@
+The Transition To The Limited Python API (PEP384)
+=================================================
+
+
+Foreword
+--------
+
+Python supports a limited API that restricts access to certain structures.
+Besides eliminating whole modules and all functions and macros which names
+start with an
+underscore, the most drastic restriction is the removal of normal type object
+declarations.
+
+For details about the eliminated modules and functions, please see the
+`PEP 384`_ page for reference.
+
+
+.. _`PEP 384`: https://www.python.org/dev/peps/pep-0384/
+
+
+
+Changed Modules
+---------------
+
+All changed module's include files are listed with the changed functions here.
+As a general rule, it was tried to keep the changes to a minimum diff.
+Macros which are not available were changed to functions with the same name
+if possible. Completely removed names ``Py{name}`` were re-implemented as ``Pep{name}``.
+
+
+memoryobject.h
+~~~~~~~~~~~~~~
+
+The buffer protocol was completely removed. We redefined all the structures
+and methods, because PySide uses that. This is an exception to the limited API
+that we have to check ourselves. The code is extracted in bufferprocs_py37.h .
+This is related to the following:
+
+
+abstract.h
+~~~~~~~~~~
+
+This belongs to the buffer protocol like memoryobject.h .
+As replacement for ``Py_buffer`` we defined ``Pep_buffer`` and several other
+internal macros.
+
+The version is checked by hand, and the version number must be updated only
+if the implementation does not change. Otherwise, we need to write version
+dependent code paths.
+
+It is questionable if it is worthwhile to continue using the buffer protocol
+or if we should try to get rid of ``Pep_buffer``, completely.
+
+
+pydebug.h
+~~~~~~~~~
+
+We have no direct access to ``Py_VerboseFlag`` because debugging is not
+supported. We redefined it as macro ``Py_VerboseFlag`` which calls ``Pep_VerboseFlag``.
+
+
+unicodeobject.h
+~~~~~~~~~~~~~~~
+
+The macro ``PyUnicode_GET_SIZE`` was removed and replaced by ``PepUnicode_GetLength``
+which evaluates to ``PyUnicode_GetSize`` for Python 2 and ``PyUnicode_GetLength`` for Python 3.
+Since Python 3.3, ``PyUnicode_GetSize`` would have the bad side effect of requiring the GIL!
+
+Function ``_PyUnicode_AsString`` is unavailable and was replaced by a macro
+that calls ``_PepUnicode_AsString``. The implementation was a bit involved,
+and it would be better to change the code and replace this function.
+
+
+bytesobject.h
+~~~~~~~~~~~~~
+
+The macros ``PyBytes_AS_STRING`` and ``PyBytes_GET_SIZE`` were redefined to call
+the according functions.
+
+
+floatobject.h
+~~~~~~~~~~~~~
+
+``PyFloat_AS_DOUBLE`` now calls ``PyFloat_AsDouble``.
+
+
+tupleobject.h
+~~~~~~~~~~~~~
+
+``PyTuple_GET_ITEM``, ``PyTuple_SET_ITEM`` and ``PyTuple_GET_SIZE`` were redefined as
+function calls.
+
+
+listobject.h
+~~~~~~~~~~~~
+
+``PyList_GET_ITEM``, ``PyList_SET_ITEM`` and ``PyList_GET_SIZE`` were redefined as
+function calls.
+
+
+dictobject.h
+~~~~~~~~~~~~
+
+``PyDict_GetItem`` also exists in a ``PyDict_GetItemWithError`` version that does
+not suppress errors. This suppression has the side effect of touching global
+structures. This function exists in Python 2 only since Python 2.7.12 and has
+a different name. We simply implemented the function.
+Needed to avoid the GIL when accessing dictionaries.
+
+
+methodobject.h
+~~~~~~~~~~~~~~
+
+``PyCFunction_GET_FUNCTION``, ``PyCFunction_GET_SELF`` and ``PyCFunction_GET_FLAGS``
+were redefined as function calls.
+
+Direct access to the methoddef structure is not available, and we defined
+``PepCFunction_GET_NAMESTR`` as accessor for name strings.
+
+
+pythonrun.h
+~~~~~~~~~~~
+
+The simple function ``PyRun_String`` is not available. It was re-implemented
+in a simplified version for the signature module.
+
+
+funcobject.h
+~~~~~~~~~~~~
+
+The definitions of funcobject.h are completely missing, although there
+are extra ``#ifdef`` conditional defines inside, too. This suggests that the exclusion
+was unintended.
+
+We therefore redefined ``PyFunctionObject`` as an opaque type.
+
+The missing macro ``PyFunction_Check`` was defined, and the macro
+``PyFunction_GET_CODE`` calls the according function.
+
+There is no equivalent for function name access, therefore we introduced
+``PepFunction_GetName`` either as a function or as a macro.
+
+*TODO: We should fix funcobject.h*
+
+
+classobject.h
+~~~~~~~~~~~~~
+
+Classobject is also completely not imported, instead of defining an opaque type.
+
+We defined the missing functions ``PyMethod_New``, ``PyMethod_Function`` and
+``PyMethod_Self`` and also redefined ``PyMethod_GET_SELF`` and
+``PyMethod_GET_FUNCTION`` as calls to these functions.
+
+*TODO: We should fix classobject.h*
+
+
+code.h
+~~~~~~
+
+The whole code.c code is gone, although it may make sense to
+define some minimum accessibility. This will be clarified on
+`Python-Dev`_. We needed access to code objects and defined the missing
+PepCode_GET_FLAGS and PepCode_GET_ARGCOUNT either as function or macro.
+We further added the missing flags, although few are used:
+
+``CO_OPTIMIZED`` ``CO_NEWLOCALS`` ``CO_VARARGS`` ``CO_VARKEYWORDS`` ``CO_NESTED``
+``CO_GENERATOR``
+
+*TODO: We should maybe fix code.h*
+
+.. _`Python-Dev`: https://mail.python.org/mailman/listinfo/python-dev
+
+datetime.h
+~~~~~~~~~~
+
+The DateTime module is explicitly not included in the limited API.
+We defined all the needed functions but called them via Python instead
+of direct call macros. This has a slight performance impact.
+
+The performance could be easily improved by providing an interface
+that fetches all attributes at once, instead of going through the object
+protocol every time.
+
+The re-defined macros and methods are::
+
+ PyDateTime_GET_YEAR
+ PyDateTime_GET_MONTH
+ PyDateTime_GET_DAY
+ PyDateTime_DATE_GET_HOUR
+ PyDateTime_DATE_GET_MINUTE
+ PyDateTime_DATE_GET_SECOND
+ PyDateTime_DATE_GET_MICROSECOND
+ PyDateTime_DATE_GET_FOLD
+ PyDateTime_TIME_GET_HOUR
+ PyDateTime_TIME_GET_MINUTE
+ PyDateTime_TIME_GET_SECOND
+ PyDateTime_TIME_GET_MICROSECOND
+ PyDateTime_TIME_GET_FOLD
+
+ PyDate_Check
+ PyDateTime_Check
+ PyTime_Check
+
+ PyDate_FromDate
+ PyDateTime_FromDateAndTime
+ PyTime_FromTime
+
+*XXX: We should maybe provide an optimized interface to datetime*
+
+
+object.h
+~~~~~~~~
+
+The file object.h contains the ``PyTypeObject`` structure, which is supposed
+to be completely opaque. All access to types should be done through
+``PyType_GetSlot`` calls. Due to bugs and deficiencies in the limited API
+implementation, it was not possible to do that. Instead, we have defined
+a simplified structure for ``PyTypeObject`` that has only the fields that
+are used in PySide.
+
+We will explain later why and how this was done. Here is the reduced
+structure::
+
+ typedef struct _typeobject {
+ PyVarObject ob_base;
+ const char *tp_name;
+ Py_ssize_t tp_basicsize;
+ void *X03; // Py_ssize_t tp_itemsize;
+ void *X04; // destructor tp_dealloc;
+ void *X05; // printfunc tp_print;
+ void *X06; // getattrfunc tp_getattr;
+ void *X07; // setattrfunc tp_setattr;
+ void *X08; // PyAsyncMethods *tp_as_async;
+ void *X09; // reprfunc tp_repr;
+ void *X10; // PyNumberMethods *tp_as_number;
+ void *X11; // PySequenceMethods *tp_as_sequence;
+ void *X12; // PyMappingMethods *tp_as_mapping;
+ void *X13; // hashfunc tp_hash;
+ ternaryfunc tp_call;
+ reprfunc tp_str;
+ void *X16; // getattrofunc tp_getattro;
+ void *X17; // setattrofunc tp_setattro;
+ void *X18; // PyBufferProcs *tp_as_buffer;
+ void *X19; // unsigned long tp_flags;
+ void *X20; // const char *tp_doc;
+ traverseproc tp_traverse;
+ inquiry tp_clear;
+ void *X23; // richcmpfunc tp_richcompare;
+ Py_ssize_t tp_weaklistoffset;
+ void *X25; // getiterfunc tp_iter;
+ void *X26; // iternextfunc tp_iternext;
+ struct PyMethodDef *tp_methods;
+ void *X28; // struct PyMemberDef *tp_members;
+ void *X29; // struct PyGetSetDef *tp_getset;
+ struct _typeobject *tp_base;
+ PyObject *tp_dict;
+ descrgetfunc tp_descr_get;
+ void *X33; // descrsetfunc tp_descr_set;
+ Py_ssize_t tp_dictoffset;
+ initproc tp_init;
+ allocfunc tp_alloc;
+ newfunc tp_new;
+ freefunc tp_free;
+ inquiry tp_is_gc; /* For PyObject_IS_GC */
+ PyObject *tp_bases;
+ PyObject *tp_mro; /* method resolution order */
+ } PyTypeObject;
+
+Function ``PyIndex_Check`` had to be defined in an unwanted way due to
+a Python issue. See file pep384_issue33738.cpp .
+
+There are extension structures which have been isolated as special macros that
+dynamically compute the right offsets of the extended type structures:
+
+* ``PepType_SOTP`` for ``SbkObjectTypePrivate``
+* ``PepType_SETP`` for ``SbkEnumTypePrivate``
+* ``PepType_PFTP`` for ``PySideQFlagsTypePrivate``
+
+How these extension structures are used can best be seen by searching
+``PepType_{four}`` in the source.
+
+Due to the new heaptype interface, the names of certain types contain
+now the module name in the ``tp_name`` field. To have a compatible way
+to access simple type names as C string, ``PepType_GetNameStr`` has been
+written that skips over dotted name parts.
+
+Finally, the function ``_PyObject_Dump`` was excluded from the limited API.
+This is a useful debugging aid that we always want to have available,
+so it is added back, again. Anyway, we did not reimplement it, and so
+Windows is not supported.
+Therefore, a forgotten debugging call of this functions will break COIN. :-)
+
+
+Using The New Type API
+----------------------
+
+After converting everything but the object.h file, we were a little
+bit shocked: it suddenly was clear that we would have no more
+access to type objects, and even more scary that all types which we
+use have to be heap types, only!
+
+For PySide with its intense use of heap type extensions in various
+flavors, the situation looked quite unsolvable. In the end, it was
+nicely solved, but it took almost 3.5 months to get that right.
+
+Before we see how this is done, we will explain the differences
+between the APIs and their consequences.
+
+
+The Interface
+~~~~~~~~~~~~~
+
+The old type API of Python knows static types and heap types.
+Static types are written down as a declaration of a ``PyTypeObject``
+structure with all its fields filled in. Here is for example
+the definition of the Python type ``object`` (Python 3.6)::
+
+ PyTypeObject PyBaseObject_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "object", /* tp_name */
+ sizeof(PyObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ object_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ object_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)_Py_HashPointer, /* tp_hash */
+ 0, /* tp_call */
+ object_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ PyObject_GenericSetAttr, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ PyDoc_STR("object()\n--\n\nThe most base type"), /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ object_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ object_methods, /* tp_methods */
+ 0, /* tp_members */
+ object_getsets, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ object_init, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ object_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+ };
+
+We can write the same structure in form of a ``PyType_Spec`` structure,
+and there is even an incomplete tool *abitype.py* that does this conversion
+for us. With a few corrections, the result looks like this::
+
+ static PyType_Slot PyBaseObject_Type_slots[] = {
+ {Py_tp_dealloc, (void *)object_dealloc},
+ {Py_tp_repr, (void *)object_repr},
+ {Py_tp_hash, (void *)_Py_HashPointer},
+ {Py_tp_str, (void *)object_str},
+ {Py_tp_getattro, (void *)PyObject_GenericGetAttr},
+ {Py_tp_setattro, (void *)PyObject_GenericSetAttr},
+ {Py_tp_richcompare, (void *)object_richcompare},
+ {Py_tp_methods, (void *)object_methods},
+ {Py_tp_getset, (void *)object_getsets},
+ {Py_tp_init, (void *)object_init},
+ {Py_tp_alloc, (void *)PyType_GenericAlloc},
+ {Py_tp_new, (void *)object_new},
+ {Py_tp_free, (void *)PyObject_Del},
+ {0, 0},
+ };
+ static PyType_Spec PyBaseObject_Type_spec = {
+ "object",
+ sizeof(PyObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ PyBaseObject_Type_slots,
+ };
+
+This new structure is almost compatible with the old one, but there
+are some subtle differences.
+
+* The new types are generated in one step
+
+This seems to be no problem, but it was very much, due to the way the
+types were built in PySide. Types were assembled piece by piece, and
+finally the ``PyType_Ready`` function was called.
+
+With the new API, ``PyType_Ready`` is called already at the end of
+``PyType_FromSpec``, and that meant that the logic of type creation became
+completely turned upside down.
+
+* The new types are always heaptypes
+
+With the new type creation functions, it is no longer possible to
+create "normal" types. Instead, they all have to be allocated on the
+heap and garbage collected. The user should normally not recognize this.
+But type creation is more constrained, and you cannot create a subtype
+if the ``Py_TPFLAGS_BASETYPE`` is not set. This constraint was already
+violated by PySide and needed a quite profound fix.
+
+* The new types always need a module
+
+While this is not a problem per se, the above new type spec will not create
+a usable new type, but complain with::
+
+ DeprecationWarning: builtin type object has no __module__ attribute
+
+But there are more problems:
+
+* The new types have unexpected defaults
+
+When fields are empty, you would usually assume that they stay empty.
+There are just a few corrections that ``PyType_Ready`` will do to a type.
+
+But there is the following clause in ``PyType_FromSpec`` that can give you
+many headaches::
+
+ if (type->tp_dealloc == NULL) {
+ /* It's a heap type, so needs the heap types' dealloc.
+ subtype_dealloc will call the base type's tp_dealloc, if
+ necessary. */
+ type->tp_dealloc = subtype_dealloc;
+ }
+
+In fact, before the move to the new API, the ``PyType_Ready`` function
+filled empty ``tp_dealloc`` fields with ``object_dealloc``. And the code
+that has been written with that in mind now becomes pretty wrong if suddenly
+``subtype_dealloc`` is used.
+
+The way out was to explicitly provide an ``object_dealloc`` function.
+This would then again impose a problem, because ``object_dealloc`` is not
+public. Writing our own version is easy, but it again needs access to
+type objects. But fortunately, we have broken this rule, already...
+
+
+* The new types are only partially allocated
+
+The structures used in ``PyType_FromSpec`` are almost all allocated,
+only the name field is static. This is no problem for types which are
+statically created once. But if you want to parameterize things and
+create multiple types with a single slots and spec definition, the name
+field that is used for tp_name must be allocated dynamically.
+This is misleading, since all the slots already are copies.
+
+* The new types don't support special offsets
+
+The special fields ``tp_weaklistoffset`` and ``tp_dictoffset`` are not supported
+by ``PyType_FromSpec``. Unfortunately the documentation does not tell you
+if you are allowed to set these fields manually after creating the type or not.
+We finally did it and it worked, but we are not sure about correctness.
+
+See basewrapper.cpp function ``SbkObject_TypeF()`` as the only reference to
+these fields in PySide. This single reference is absolutely necessary and
+very important, since all derived types invisibly inherit these two fields.
+
+
+Future Versions Of The Limited API
+----------------------------------
+
+As we have seen, the current version of the limited API does a bit of
+cheating, because it uses parts of the data structure that should be
+an opaque type. At the moment, this works fine because the data is
+still way more compatible as it could be.
+
+But what if this is changed in the future?
+
+We know that the data structures are stable until Python 3.8 comes out.
+Until then, the small bugs and omissions will hopefully all be solved.
+Then it will be possible to replace the current small tricks by calls
+to ``PyType_GetSlot`` in the way things should be.
+
+At the very moment when the current assumptions about the data structure
+are no longer true, we will rewrite the direct attribute access with
+calls to ``PyType_GetSlot``. After that, no more changes will be necessary.
+
+
+Appendix A: The Transition To Simpler Types
+-------------------------------------------
+
+After all code had been converted to the limited API, there was a
+remaining problem with the ``PyHeapTypeObject``.
+
+Why a problem? Well, all the type structures in shiboken use
+special extra fields at the end of the heap type object. This
+currently enforces extra knowledge at compile time about how large the
+heap type object is. In a clean implementation, we would only use
+the ``PyTypeObject`` itself and access the fields *behind* the type
+by a pointer that is computed at runtime.
+
+
+Restricted PyTypeObject
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Before we are going into details, let us motivate the existence of
+the restricted ``PyTypeObject``:
+
+Originally, we wanted to use ``PyTypeObject`` as an opaque type and
+restrict ourselves to only use the access function ``PyType_GetSlot``.
+This function allows access to all fields which are supported by
+the limited API.
+
+But this is a restriction, because we get no access to ``tp_dict``,
+which we need to support the signature extension. But we can work
+around that.
+
+The real restriction is that ``PyType_GetSlot`` only works for heap
+types. This makes the function quite useless, because we have
+no access to ``PyType_Type``, which is the most important type ``type``
+in Python. We need that for instance to compute the size of
+``PyHeapTypeObject`` dynamically.
+
+With much effort, it is possible to clone ``PyType_Type`` as a heap
+type. But due to a bug in the Pep 384 support, we need
+access to the ``nb_index`` field of a normal type. Cloning does not
+help because ``PyNumberMethods`` fields are *not* inherited.
+
+After we realized this dead end, we changed concept and did not
+use ``PyType_GetSlot`` at all (except in function ``copyNumberMethods``),
+but created a restricted ``PyTypeObject`` with only those fields
+defined that are needed in PySide.
+
+Is this breakage of the limited API? I don't think so. A special
+function runs on program startup that checks the correct position
+of the fields of ``PyTypeObject``, although a change in those fields is
+more than unlikely.
+The really crucial thing is to no longer use ``PyHeapTypeObject``
+explicitly because that *does* change its layout over time.
+
+
+Diversification
+~~~~~~~~~~~~~~~
+
+There were multiple ``Sbk{something}`` structures which all used a "d" field
+for their private data. This made it not easy to find the right
+fields when switching between objects and types::
+
+ struct LIBSHIBOKEN_API SbkObject
+ {
+ PyObject_HEAD
+ PyObject *ob_dict;
+ PyObject *weakreflist;
+ SbkObjectPrivate *d;
+ };
+
+ struct LIBSHIBOKEN_API SbkObjectType
+ {
+ PyHeapTypeObject super;
+ SbkObjectTypePrivate *d;
+ };
+
+The first step was to rename the SbkObjectTypePrivate part from "d" to
+"sotp". It was chosen to be short but easy to remember as abbreviation
+of "SbkObjectTypePrivate", leading to::
+
+ struct LIBSHIBOKEN_API SbkObjectType
+ {
+ PyHeapTypeObject super;
+ SbkObjectTypePrivate *sotp;
+ };
+
+After renaming, it was easier to do the following transformations.
+
+
+Abstraction
+~~~~~~~~~~~
+
+After renaming the type extension pointers to ``sotp``, I replaced
+them by function-like macros which did the special access *behind*
+the types, instead of those explicit fields. For instance, the
+expression::
+
+ type->sotp->converter
+
+became::
+
+ PepType_SOTP(type)->converter
+
+The macro expansion can be seen here::
+
+ #define PepHeapType_SIZE \
+ (reinterpret_cast<PyTypeObject *>(&PyType_Type)->tp_basicsize)
+
+ #define _genericTypeExtender(etype) \
+ (reinterpret_cast<char *>(etype) + PepHeapType_SIZE)
+
+ #define PepType_SOTP(etype) \
+ (*reinterpret_cast<SbkObjectTypePrivate **>(_genericTypeExtender(etype)))
+
+This looks complicated, but in the end there is only a single new
+indirection via ``PyType_Type``, which happens at runtime. This is the
+key to fulfil what Pep 384 wants to achieve: *No more version-dependent fields*.
+
+
+Simplification
+~~~~~~~~~~~~~~
+
+After all type extension fields were replaced by macro calls, we
+could remove the following version dependent re-definition of ``PyHeapTypeObject``
+::
+
+ typedef struct _pyheaptypeobject {
+ union {
+ PyTypeObject ht_type;
+ void *opaque[PY_HEAPTYPE_SIZE];
+ };
+ } PyHeapTypeObject;
+
+, and the version dependent structure::
+
+ struct LIBSHIBOKEN_API SbkObjectType
+ {
+ PyHeapTypeObject super;
+ SbkObjectTypePrivate *sotp;
+ };
+
+could be removed. SbkObjectType remains as a (deprecated)
+type alias to PyTypeObject.
+
+
+Appendix B: Verification Of PyTypeObject
+----------------------------------------
+
+We have introduced a limited PyTypeObject in the same place
+as the original PyTypeObject, and now we need to prove that
+we are allowed to do so.
+
+When using the limited API as intended, then types are completely
+opaque, and access is only through ``PyType_FromSpec`` and (from
+version 3.5 upwards) through ``PyType_GetSlot``.
+
+Python then uses all the slot definitions in the type description
+and produces a regular heap type object.
+
+
+Unused Information
+~~~~~~~~~~~~~~~~~~
+
+We know many things about types that are not explicitly said,
+but they are inherently clear:
+
+(a) The basic structure of a type is always the same, regardless
+ if it is a static type or a heap type.
+
+(b) types are evolving very slowly, and a field is never replaced
+ by another field with different semantics.
+
+Inherent rule (a) gives us the following information: If we calculate
+the offsets of the basic fields, then this info is also usable for non-heap
+types.
+
+The validation checks if rule (b) is still valid.
+
+
+How it Works
+~~~~~~~~~~~~
+
+The basic idea of the validation is to produce a new type using
+``PyType_FromSpec`` and to see where in the type structure these fields
+show up. So we build a ``PyType_Slot`` structure with all the fields we
+are using and make sure that these values are all unique in the
+type.
+
+Most fields are not interrogated by ``PyType_FromSpec``, and so we
+simply used some numeric value. Some fields are interpreted, like
+``tp_members``. This field must really be a ``PyMemberDef``. And there are
+``tp_base`` and ``tp_bases`` which have to be type objects and lists
+thereof. It was easiest to not produce these fields from scratch
+but use them from the ``type`` object ``PyType_Type``.
+
+Then one would think to write a function that searches the known
+values in the opaque type structure.
+
+But we can do better and use optimistically the observation (b):
+We simply use the restricted ``PyTypeObject`` structure and assume that
+every field lands exactly where we are awaiting it.
+
+And that is the whole proof: If we find all the disjoint values at
+the places where we expect them, then verification is done.
+
+
+About ``tp_dict``
+~~~~~~~~~~~~~~~~~
+
+One word about the ``tp_dict`` field: This field is a bit special in
+the proof, since it does not appear in the spec and cannot easily
+be checked by ``type.__dict__`` because that creates a *dictproxy*
+object. So how do we prove that is really the right dict?
+
+We have to create that ``PyMethodDef`` structure anyway, and instead of
+leaving it empty, we insert a dummy function. Then we ask the
+``tp_dict`` field if it has the awaited object in it, and that's it!
+
+#EOT
diff --git a/sources/pyside6/doc/developer/signature_doc.rst b/sources/pyside6/doc/developer/signature_doc.rst
new file mode 100644
index 000000000..a6c703dab
--- /dev/null
+++ b/sources/pyside6/doc/developer/signature_doc.rst
@@ -0,0 +1,361 @@
+.. _signature-extension:
+
+The signature C extension
+=========================
+
+This module is a C extension for CPython 3.5 and up, and CPython 2.7.
+Its purpose is to provide support for the ``__signature__`` attribute
+of builtin PyCFunction objects.
+
+
+Short Introduction to the Topic
+-------------------------------
+
+Beginning with CPython 3.5, Python functions began to grow a ``__signature__``
+attribute for normal Python functions. This is totally optional and just
+a nice-to-have feature in Python.
+
+PySide, on the other hand, could use ``__signature__`` very much, because the
+typing info for the 15000+ PySide functions is really missing, and it
+would be nice to have this info directly available.
+
+
+The Idea to Support Signatures
+------------------------------
+
+We want to have an additional ``__signature__`` attribute in all PySide
+methods, without changing lots of generated code.
+Therefore, we did not change any of the existing data structures,
+but supported the new attribute by a global dictionary.
+
+When the ``__signature__`` property is requested, a method is called that
+does a lookup in the global dict. This is a flexible approach with little impact
+to the rest of the project. It has very limited overhead compared to direct
+attribute access, but for the need of a signature access from time to time,
+this is an adequate compromise.
+
+
+How this Code Works
+~~~~~~~~~~~~~~~~~~~
+
+Signatures are supported for regular Python functions, only. Creating signatures
+for ``PyCFunction`` objects would require quite some extra effort in Python.
+
+Fortunately, we found this special *stealth* technique, that saves us most of the
+needed effort:
+
+The basic idea is to create a dummy Python function with **varnames**, **defaults**
+and **annotations** properties, and then to use the inspect
+module to create a signature object. This object is returned as the computed
+result of the ``__signature__`` attribute of the real ``PyCFunction`` object.
+
+There is one thing that really changes Python a bit:
+
+* We added the ``__signature__`` attribute to every function.
+
+That is a little change to Python that does not harm, but it saves us
+tons of code, that was needed in the early versions of the module.
+
+The internal work is done in two steps:
+
+* All functions of a class get the *signature text* when the module is imported.
+ This is only a very small overhead added to the startup time. It is a single
+ string for each whole class.
+* The actual signature object is created later, when the attribute is really
+ requested. Signatures are cached and only created on first access.
+
+Example:
+
+The ``PyCFunction`` ``QtWidgets.QApplication.palette`` is interrogated for its
+signature. That means ``pyside_sm_get___signature__()`` is called.
+It calls ``GetSignature_Function`` which returns the signature if it is found.
+
+
+Why this Code is Fast
+~~~~~~~~~~~~~~~~~~~~~
+
+It costs a little time (maybe 6 seconds) to run through every single signature
+object, since these are more than 25000 Python objects. But all the signature
+objects will be rarely accessed but in special applications.
+The normal case are only a few accesses, and these are working pretty fast.
+
+The key to make this signature module fast is to avoid computation as much as
+possible. When no signature objects are used, then almost no time is lost in
+initialization. Only the above mentioned strings and some support modules are
+additionally loaded on ``import PySide6``.
+When it comes to signature usage, then late initialization is used and cached.
+This technique is also known as *full laziness* in haskell.
+
+There are actually two locations where late initialization occurs:
+
+* ``dict`` can be no dict but a tuple. That is the initial argument tuple that
+ was saved by ``PySide_BuildSignatureArgs`` at module load time.
+ If so, then ``pyside_type_init`` in parser.py will be called,
+ which parses the string and creates the dict.
+* ``props`` can be empty. Then ``create_signature`` in loader.py
+ is called, which uses a dummy function to produce a signature instance
+ with the inspect module.
+
+The initialization that is always done is just two dictionary writes
+per class, and we have about 1000 classes.
+To measure the additional overhead, we have simulated what happens
+when ``from PySide6 import *`` is performed.
+It turned out that the overhead is below 0.5 ms.
+
+
+The Signature Package Structure
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The C++ code involved with the signature module is completely in the file
+shiboken6/libshiboken/signature.cpp . All other functionality is implemented in
+the ``signature`` Python package. It has the following structure::
+
+ sources/shiboken6/shibokenmodule/files.dir/shibokensupport
+ ├── __init__.py
+ ├── feature.py
+ ├── fix-complaints.py
+ ├── shibokensupport.pyproject
+ └── signature
+ ├── PSF-3.7.0.txt
+ ├── __init__.py
+ ├── errorhandler.py
+ ├── importhandler.py
+ ├── layout.py
+ ├── lib
+ │   ├── __init__.py
+ │   ├── enum_sig.py
+ │   ├── pyi_generator.py
+ │   └── tool.py
+ ├── loader.py
+ ├── mapping.py
+ ├── parser.py
+ └── qt_attribution.json
+
+Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**,
+**layout** and **loader** modules. The rest is needed to create Python 2 compatibility
+or be compatible with embedding and installers.
+
+
+**loader.py**
+ This module assembles and imports the ``inspect`` module, and then exports the
+ ``create_signature`` function. This function takes a fake function and some
+ attributes and builds a ``__signature__`` object with the inspect module.
+
+
+**parser.py**
+ This module takes a class signatures string from C++ and parses it into the
+ needed properties for the ``create_signature`` function. Its entry point is the
+ ``pyside_type_init`` function, which is called from the C module via ``loader.py``.
+
+
+**mapping.py**
+ The purpose of the mapping module is maintaining a list of replacement strings
+ that map from the *signature text* in C to the property strings that Python
+ needs. A lot of mappings are resolved by rather complex expressions in ``parser.py``,
+ but a few hundred cases are better to spell explicitly, here.
+
+**errorhandler.py**
+ Since ``Qt For Python 5.12``, we no longer use the builtin type error messages from C++.
+ Instead, we get much better results with the signature module. At the same time,
+ this enforced supporting shiboken as well, and the signature module was no longer
+ optional.
+
+**enum_sig.py**
+ The diverse applications of the signature module all needed to iterate over modules,
+ classes and functions. In order to centralize this enumeration, the process has
+ been factored out as a context manager. The user has only to supply functions
+ that do the actual formatting.
+
+ See for example the .pyi generator ``pyside6/PySide6/support/generate_pyi.py``.
+
+**layout.py**
+ As more applications used the signature module, different formatting of signatures
+ was needed. To support that, we created the function ``create_signature``, which
+ has a parameter to choose from some predefined layouts.
+
+
+**typing27.py**
+ Python 2 has no typing module at all. This is a backport of the minimum that is needed.
+
+
+**backport_inspect.py**
+ Python 2 has an inspect module, but lacks the signature functions, completely.
+ This module adds the missing functionality, which is merged at runtime into
+ the inspect module.
+
+
+Multiple Arities
+~~~~~~~~~~~~~~~~
+
+One aspect that was ignored so far was *multiple arities*: How to handle it when
+a function has more than one signature?
+
+I did not find any note on how multiple signatures should be treated in Python,
+but this simple rules seem to work well:
+
+* If there is a list, then it is a multi-signature.
+* Otherwise, it is a simple signature.
+
+
+Impacts of The Signature Module
+-------------------------------
+
+The signature module has a number of impacts to other PySide modules, which were
+created as a consequence of its existence, and there will be a few more in the
+future:
+
+
+existence_test.py
+~~~~~~~~~~~~~~~~~
+
+The file ``pyside6/tests/registry/existence_test.py`` was written using the
+signatures from the signatures module. The idea is that there are some 15000
+functions with a certain signature.
+
+These functions should not get lost by some bad check-in. Therefore, a list
+of all existing signatures is kept as a module that assembles a
+dictionary. The function existence is checked, and also the exact arity.
+
+This module exists for every PySide release and every platform. The initial
+module is generated once and saved as ``exists_{plat}_{version}.py``.
+
+An error is normally only reported as a warning, but:
+
+
+Interaction With The Coin Module
+++++++++++++++++++++++++++++++++
+
+When this test program is run in COIN, then the warnings are turned into
+errors. The reason is that only in COIN, we have a stable configuration
+of PySide modules that can reliably be compared.
+
+These modules have the name ``exists_{platf}_{version}_ci.py``, and as a big
+exception for generated code, these files are *intentionally* checked in.
+
+
+What Happens When a List is Missing?
+++++++++++++++++++++++++++++++++++++
+
+When a new version of PySide gets created, then the existence test files
+initially do not exist.
+
+When a COIN test is run, then it will complain about the error and create
+the missing module on standard output.
+But since COIN tests are run multiple times, the output that was generated
+by the first test will still exist at the subsequent runs.
+(If COIN was properly implemented, we could not take that advantage and
+would need to implement that as an extra exception.)
+
+As a result, a missing module will be reported as a test which partially
+succeeded (called "FLAKY"). To avoid further flaky tests and to activate as a real test,
+we can now capture the error output of COIN and check the generated module
+in.
+
+
+Explicitly Enforcing Recreation
++++++++++++++++++++++++++++++++
+
+The former way to regenerate the registry files was to remove the files
+and check that in. This has the desired effect, but creates huge deltas.
+As a more efficient way, we have prepared a comment in the first line
+that contains the word "recreate".
+By uncommenting this line, a NameError is triggered, which has the same
+effect.
+
+
+init_platform.py
+++++++++++++++++
+
+For generating the ``exists_{platf}_{version}`` modules, the module
+``pyside6/tests/registry/init_platform.py`` was written. It can be used
+standalone from the commandline, to check the compatibility of some
+changes, directly.
+
+
+scrape_testresults.py
+~~~~~~~~~~~~~~~~~~~~~
+
+To simplify and automate the process of extracting the ``exists_{platf}_{version}_ci.py``
+files, the script ``pyside6/tests/registry/scrape_testresults.py`` has been written.
+
+This script scans the whole testresults website for PySide, that is::
+
+ https://testresults.qt.io/coin/api/results/pyside/pyside-setup/
+
+On the first scan, the script runs less than 30 minutes. After that, a cache
+is generated and the scan works *much* faster. The test results are placed
+into the folder ``pyside6/tests/registry/testresults/embedded/`` with a
+unique name that allows for easy sorting. Example::
+
+ testresults/embedded/2018_09_10_10_40_34-test_1536891759-exists_linux_5_11_2_ci.py
+
+These files are created only once. If they already exist, they are not touched, again.
+The file `pyside6/tests/registry/known_urls.json`` holds all scanned URLs after
+a successful scan. The ``testresults/embedded`` folder can be kept for reference
+or can be removed. Important is only the json file.
+
+The result of a scan is then directly placed into the ``pyside6/tests/registry/``
+folder. It should be reviewed and then eventually checked in.
+
+
+generate_pyi.py
+~~~~~~~~~~~~~~~
+
+``pyside6/PySide6/support/generate_pyi.py`` is still under development.
+This module generates so-called hinting stubs for integration of PySide
+with diverse *Python IDEs*.
+
+Although this module creates the stubs as an add-on, the
+impact on the quality of the signature module is considerable:
+
+The module must create syntactically correct ``.pyi`` files which contain
+not only signatures but also constants and enums of all PySide modules.
+This serves as an extra challenge that has a very positive effect on
+the completeness and correctness of signatures.
+
+The module has a ``--feature`` option to generate modified .pyi files.
+A shortcut for this command is ``pyside6-genpyi``.
+
+A useful command to change all .pyi files to use all features is
+
+.. code-block:: bash
+
+ pyside6-genpyi all --feature snake_case true_property
+
+
+pyi_generator.py
+~~~~~~~~~~~~~~~~
+
+``shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py``
+has been extracted from ``generate_pyi.py``. It allows the generation of ``.pyi``
+files from arbitrary extension modules created with shiboken.
+
+A shortcut for this command is ``shiboken6-genpyi``.
+
+
+Current Extensions
+~~~~~~~~~~~~~~~~~~
+
+Before the signature module was written, there already existed the concept of
+signatures, but in a more C++ - centric way. From that time, there existed
+the error messages, which are created when a function gets wrong argument types.
+
+These error messages were replaced by text generated on demand by
+the signature module, in order to be more consistent and correct.
+This was implemented in ``Qt For Python 5.12.0``.
+
+Additionally, the ``__doc__`` attribute of PySide methods was not set.
+It was easy to get a nice ``help()`` feature by creating signatures
+as default content for docstrings.
+This was implemented in ``Qt For Python 5.12.1``.
+
+
+Literature
+----------
+
+* `PEP 362 – Function Signature Object <https://www.python.org/dev/peps/pep-0362/>`__
+* `PEP 484 – Type Hints <https://www.python.org/dev/peps/pep-0484/>`__
+* `PEP 3107 – Function Annotations <https://www.python.org/dev/peps/pep-3107/>`__
+
+
+*Personal Remark: This module is dedicated to our lovebird "Püppi", who died on 2017-09-15.*