diff options
author | Christian Tismer <tismer@stackless.com> | 2024-10-23 12:27:03 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-10-31 20:30:28 +0000 |
commit | 5ff2e48b114f566663893a4b8c9abb4ff892f2ff (patch) | |
tree | f410e77e57b898d2a5d83fd586167b9adc74bf35 | |
parent | c83e15fe6a52b2ce484f1e41f8306df2e5de1c7c (diff) |
type hints: Add some mypy explanations to developer docs6.8.0
First attempt to explain a little how mypy errors were treated.
Task-number: PYSIDE-2846
Change-Id: I13a0d2b8298d5fd1637d3bca9e2b979c7062b811
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
(cherry picked from commit c37642f241fb21ae81bb7ff8721deb8a423ef0c1)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 91c015fdc43bf58c829a043f54b50e3cebabce55)
-rw-r--r-- | sources/pyside6/doc/developer/index.rst | 1 | ||||
-rw-r--r-- | sources/pyside6/doc/developer/mypy-correctness.rst | 186 | ||||
-rw-r--r-- | sources/pyside6/doc/developer/signature_doc.rst | 57 |
3 files changed, 224 insertions, 20 deletions
diff --git a/sources/pyside6/doc/developer/index.rst b/sources/pyside6/doc/developer/index.rst index d158b6690..bddd39d91 100644 --- a/sources/pyside6/doc/developer/index.rst +++ b/sources/pyside6/doc/developer/index.rst @@ -34,4 +34,5 @@ many features and implementation details that the project has: enumfeatures_doc.rst limited_api.rst signature_doc.rst + mypy-correctness.rst feature-motivation.rst diff --git a/sources/pyside6/doc/developer/mypy-correctness.rst b/sources/pyside6/doc/developer/mypy-correctness.rst new file mode 100644 index 000000000..8f0d63a1d --- /dev/null +++ b/sources/pyside6/doc/developer/mypy-correctness.rst @@ -0,0 +1,186 @@ +.. mypy-correctness: + +Improving the Quality of Signatures with mypy +============================================= + +Preliminary +----------- + +The Python Interface files of PySide are generated by a few scripts. +When ``.pyi`` files were started in 2017, a minimal syntax check was +possible because these files could be run in ``Python`` itself. + +Some changes to the format of ``.pyi`` files made that impossible, leaving +``PySide``'s ``.pyi`` files quite unchecked for years. Only correct parsing of +all functions could be checked by the generator. + +The introduction of the ``mypy`` tool as a rigorous error checker for the +generated files brought many improvements, but also some surprizes. + + +Running the mypy Tests +---------------------- + +The ``mypy`` tests are automatically run by the Qt company CI framework (COIN). +When you have ``mypy`` installed, the tests are run when building with tests. +In debug mode, this can take more than 30 s, therefore we provide the +translation option + +.. code-block:: shell + + --skip-mypy-test + +which can be used when repeatedly translating. But note that ``mypy`` has a +good cache that suppresses analysis of unchanged ``.pyi`` files. + + +Types of mypy Errors +-------------------- + +Duplication Errors +~~~~~~~~~~~~~~~~~~ + +Many functions have multiple signatures, which are later translated to multiple +``typing.overload`` versions in the ``.pyi`` file. +Due to the mapping of ``C++`` functions to ``Python`` it sometimes happens +that similar ``C++`` functions become ``Python`` duplicates. This was simple +to filter out, but ``mypy`` still finds duplicates which differ only in parameter +names. This is now handled by the function ``remove_ambiguous_signatures()`` +in module ``layout`` that compares the so-called ``annotations`` which ignore +parameter names. + + +Shadowing Errors +~~~~~~~~~~~~~~~~ + +A quite subtle error type is the shadowing of multiple signatures. This is due +to the sequential nature of ``.pyi`` files:: + + * In ``C++``, the order of functions does not matter at all. The best fit is + automatically used. + + * In Python stub files, the alternatives of multiple signatures are sequentially + checked in ``@typing.overload`` chains of functions. + This can produce shadowing when an annotation contains another. + +An Example: :class:`PySide6.QtCore.QCborSimpleType` is shadowed by int +when int is listed first. That is due to the Method Resolution Order ``mro()``:: + + * int.mro() [<class 'int'>, <class 'object'>] + + * QCborSimpleType.mro() [<enum 'QCborSimpleType'>, <enum 'IntEnum'>, + <class 'int'>, <enum 'ReprEnum'>, + <enum 'Enum'>, <class 'object'>] + +You see that the ``mro()`` has an ordering effect on the multiple signatures. +The enum inherits from ``int`` and should come before the ``int`` entry. +The whole task of bringing the multiple signatures into a conflict-free order +is a sort of ``Topological Sorting``. + +We build a sorting key using the length of the ``mro`` of the argument annotations +and some additional heuristics. They can be inspected in function ``get_ordering_key()`` +that is called by ``sort_by_inheritance()`` in module ``layout``. + + +Unsolvable Errors +----------------- + +Some errors are pointed out by mypy that we cannot solve. The only chance we have is +to disable these errors partially or even completely. They are marked in the ``.pyi`` files, +see below. + + +Contradiction to Qt +~~~~~~~~~~~~~~~~~~~ + +Errors are found by mypy where Qt has a different opinion. The error types +"override" and "overload-overlap" needed to be disabled because we cannot +change what Qt thinks is right. + +Examples: + +:: + + Error code "override" cannot be fixed because the problem + is situated in Qt itself: + + Signature of "open" incompatible with supertype "QFile" + + Error code "overload-overlap" also cannot be fixed because + we have no chance to modify return-types: + + Overloaded function signatures 1 and 6 overlap with + incompatible return types + +They are globally disabled by the comment:: + + # mypy: disable-error-code="override, overload-overlap" + +Other errors like "misc" are too broad to be prematurely disabled. +See below how we handle them. + + +Disagreement with __add__ and __iadd__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are internal rules for ``Python`` which can only be recognized when +``mypy`` points them out as "misc". There are functions which come in pairs: + +.. code-block:: python + + __add__, __iadd__, __sub__, __isub__, __mul__, __imul__, ... + +and more. There is this rule:: + + if __add__ and __iadd__ exist in a type, the signatures must be the same. + +In 95 % this rule is fulfilled, but in a few cases it is not. There we have +to compute these cases, and if they disagree we generate a disabling ``mypy`` +inline comment "# type: ignore[misc]". You can see this functionality in +``ExactEnumerator.klass`` of module ``enum_sig``. + + +Disagreement with inconsistent overloads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If there is a mixed overloading of methods and static or class methods, mypy +believes this is an error. In a few cases we have this situation, and since +this is again treated as a "misc" error, we only disable this when it +happens. See function ``is_inconsistent_overload()`` of module +``pyi_generator`` which checks if "self" is always or never an argument. +This is again marked by an inline comment "# type: ignore[misc]". + + +Conclusion and Future +--------------------- + +This effort has brought the reported ``mypy`` errors from 601 down to zero, which +is really an improvement. But there can be done more. Although we now know that we +are generating syntactically and semantically quite correct files, we still do not know +whether the real types really fulfil the requirements of ``mypy``. + +There is a ``stubtest`` module in ``mypy`` which we might perhaps use to do even +more tests. These would check if the implementation and stub files agree. + + +Literature +---------- + +* `mypy error codes <https://mypy.readthedocs.io/en/stable/error_code_list.html>`__ + We use these by default enabled codes. + +* `typing — Support for type hints <https://docs.python.org/3/library/typing.html>`__ + The Python documentation of the typing module + +* `Typing cheat sheet <https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`__ + A quick overview of type hints (hosted at the mypy docs) + +* "Type System Reference" section of `the mypy docs <https://mypy.readthedocs.io/en/stable/index.html>`__ + The Python typing system is standardised via PEPs, so this reference should + broadly apply to most Python type checkers. (Some parts may still be specific to mypy.) + +* `Static Typing with Python <https://typing.readthedocs.io/en/latest/>`__ + Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices. + +* `Specification for the Python type system <https://typing.readthedocs.io/en/latest/spec/index.html>`__ + The complete specification. Quite exhaustive. diff --git a/sources/pyside6/doc/developer/signature_doc.rst b/sources/pyside6/doc/developer/signature_doc.rst index a6c703dab..fa1127240 100644 --- a/sources/pyside6/doc/developer/signature_doc.rst +++ b/sources/pyside6/doc/developer/signature_doc.rst @@ -110,26 +110,26 @@ 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 + 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 @@ -350,6 +350,23 @@ as default content for docstrings. This was implemented in ``Qt For Python 5.12.1``. +Update and Future of the Signature Module +----------------------------------------- + +.. code-block:: bash + + PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter. + +End of 2022, a change to the rlcompleter module made it impossible to further +support an unofficial ``__signature__`` attribute in PySide. From then on, +the functionality of signatures was kept by a ``get_signature`` function. + +Over the years, the requirements for the correctness of the generated pyi files +have increased drastically, and a lot of effort went into making the generated +``.pyi`` files correct for the current ``mypy`` tool. Mode information +about the kind of errors corrected can be found in the :ref:`mypy-correctnes` section. + + Literature ---------- |