Skip to content

Commit c37642f

Browse files
committed
type hints: Add some mypy explanations to developer docs
First attempt to explain a little how mypy errors were treated. Task-number: PYSIDE-2846 Change-Id: I13a0d2b8298d5fd1637d3bca9e2b979c7062b811 Pick-to: 6.8 6.8.0 Reviewed-by: Cristian Maureira-Fredes <[email protected]>
1 parent fd0b568 commit c37642f

File tree

3 files changed

+224
-20
lines changed

3 files changed

+224
-20
lines changed

sources/pyside6/doc/developer/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ many features and implementation details that the project has:
3434
enumfeatures_doc.rst
3535
limited_api.rst
3636
signature_doc.rst
37+
mypy-correctness.rst
3738
feature-motivation.rst
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
.. mypy-correctness:
2+
3+
Improving the Quality of Signatures with mypy
4+
=============================================
5+
6+
Preliminary
7+
-----------
8+
9+
The Python Interface files of PySide are generated by a few scripts.
10+
When ``.pyi`` files were started in 2017, a minimal syntax check was
11+
possible because these files could be run in ``Python`` itself.
12+
13+
Some changes to the format of ``.pyi`` files made that impossible, leaving
14+
``PySide``'s ``.pyi`` files quite unchecked for years. Only correct parsing of
15+
all functions could be checked by the generator.
16+
17+
The introduction of the ``mypy`` tool as a rigorous error checker for the
18+
generated files brought many improvements, but also some surprizes.
19+
20+
21+
Running the mypy Tests
22+
----------------------
23+
24+
The ``mypy`` tests are automatically run by the Qt company CI framework (COIN).
25+
When you have ``mypy`` installed, the tests are run when building with tests.
26+
In debug mode, this can take more than 30 s, therefore we provide the
27+
translation option
28+
29+
.. code-block:: shell
30+
31+
--skip-mypy-test
32+
33+
which can be used when repeatedly translating. But note that ``mypy`` has a
34+
good cache that suppresses analysis of unchanged ``.pyi`` files.
35+
36+
37+
Types of mypy Errors
38+
--------------------
39+
40+
Duplication Errors
41+
~~~~~~~~~~~~~~~~~~
42+
43+
Many functions have multiple signatures, which are later translated to multiple
44+
``typing.overload`` versions in the ``.pyi`` file.
45+
Due to the mapping of ``C++`` functions to ``Python`` it sometimes happens
46+
that similar ``C++`` functions become ``Python`` duplicates. This was simple
47+
to filter out, but ``mypy`` still finds duplicates which differ only in parameter
48+
names. This is now handled by the function ``remove_ambiguous_signatures()``
49+
in module ``layout`` that compares the so-called ``annotations`` which ignore
50+
parameter names.
51+
52+
53+
Shadowing Errors
54+
~~~~~~~~~~~~~~~~
55+
56+
A quite subtle error type is the shadowing of multiple signatures. This is due
57+
to the sequential nature of ``.pyi`` files::
58+
59+
* In ``C++``, the order of functions does not matter at all. The best fit is
60+
automatically used.
61+
62+
* In Python stub files, the alternatives of multiple signatures are sequentially
63+
checked in ``@typing.overload`` chains of functions.
64+
This can produce shadowing when an annotation contains another.
65+
66+
An Example: :class:`PySide6.QtCore.QCborSimpleType` is shadowed by int
67+
when int is listed first. That is due to the Method Resolution Order ``mro()``::
68+
69+
* int.mro() [<class 'int'>, <class 'object'>]
70+
71+
* QCborSimpleType.mro() [<enum 'QCborSimpleType'>, <enum 'IntEnum'>,
72+
<class 'int'>, <enum 'ReprEnum'>,
73+
<enum 'Enum'>, <class 'object'>]
74+
75+
You see that the ``mro()`` has an ordering effect on the multiple signatures.
76+
The enum inherits from ``int`` and should come before the ``int`` entry.
77+
The whole task of bringing the multiple signatures into a conflict-free order
78+
is a sort of ``Topological Sorting``.
79+
80+
We build a sorting key using the length of the ``mro`` of the argument annotations
81+
and some additional heuristics. They can be inspected in function ``get_ordering_key()``
82+
that is called by ``sort_by_inheritance()`` in module ``layout``.
83+
84+
85+
Unsolvable Errors
86+
-----------------
87+
88+
Some errors are pointed out by mypy that we cannot solve. The only chance we have is
89+
to disable these errors partially or even completely. They are marked in the ``.pyi`` files,
90+
see below.
91+
92+
93+
Contradiction to Qt
94+
~~~~~~~~~~~~~~~~~~~
95+
96+
Errors are found by mypy where Qt has a different opinion. The error types
97+
"override" and "overload-overlap" needed to be disabled because we cannot
98+
change what Qt thinks is right.
99+
100+
Examples:
101+
102+
::
103+
104+
Error code "override" cannot be fixed because the problem
105+
is situated in Qt itself:
106+
107+
Signature of "open" incompatible with supertype "QFile"
108+
109+
Error code "overload-overlap" also cannot be fixed because
110+
we have no chance to modify return-types:
111+
112+
Overloaded function signatures 1 and 6 overlap with
113+
incompatible return types
114+
115+
They are globally disabled by the comment::
116+
117+
# mypy: disable-error-code="override, overload-overlap"
118+
119+
Other errors like "misc" are too broad to be prematurely disabled.
120+
See below how we handle them.
121+
122+
123+
Disagreement with __add__ and __iadd__
124+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125+
126+
There are internal rules for ``Python`` which can only be recognized when
127+
``mypy`` points them out as "misc". There are functions which come in pairs:
128+
129+
.. code-block:: python
130+
131+
__add__, __iadd__, __sub__, __isub__, __mul__, __imul__, ...
132+
133+
and more. There is this rule::
134+
135+
if __add__ and __iadd__ exist in a type, the signatures must be the same.
136+
137+
In 95 % this rule is fulfilled, but in a few cases it is not. There we have
138+
to compute these cases, and if they disagree we generate a disabling ``mypy``
139+
inline comment "# type: ignore[misc]". You can see this functionality in
140+
``ExactEnumerator.klass`` of module ``enum_sig``.
141+
142+
143+
Disagreement with inconsistent overloads
144+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145+
146+
If there is a mixed overloading of methods and static or class methods, mypy
147+
believes this is an error. In a few cases we have this situation, and since
148+
this is again treated as a "misc" error, we only disable this when it
149+
happens. See function ``is_inconsistent_overload()`` of module
150+
``pyi_generator`` which checks if "self" is always or never an argument.
151+
This is again marked by an inline comment "# type: ignore[misc]".
152+
153+
154+
Conclusion and Future
155+
---------------------
156+
157+
This effort has brought the reported ``mypy`` errors from 601 down to zero, which
158+
is really an improvement. But there can be done more. Although we now know that we
159+
are generating syntactically and semantically quite correct files, we still do not know
160+
whether the real types really fulfil the requirements of ``mypy``.
161+
162+
There is a ``stubtest`` module in ``mypy`` which we might perhaps use to do even
163+
more tests. These would check if the implementation and stub files agree.
164+
165+
166+
Literature
167+
----------
168+
169+
* `mypy error codes <https://mypy.readthedocs.io/en/stable/error_code_list.html>`__
170+
We use these by default enabled codes.
171+
172+
* `typing — Support for type hints <https://docs.python.org/3/library/typing.html>`__
173+
The Python documentation of the typing module
174+
175+
* `Typing cheat sheet <https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`__
176+
A quick overview of type hints (hosted at the mypy docs)
177+
178+
* "Type System Reference" section of `the mypy docs <https://mypy.readthedocs.io/en/stable/index.html>`__
179+
The Python typing system is standardised via PEPs, so this reference should
180+
broadly apply to most Python type checkers. (Some parts may still be specific to mypy.)
181+
182+
* `Static Typing with Python <https://typing.readthedocs.io/en/latest/>`__
183+
Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices.
184+
185+
* `Specification for the Python type system <https://typing.readthedocs.io/en/latest/spec/index.html>`__
186+
The complete specification. Quite exhaustive.

sources/pyside6/doc/developer/signature_doc.rst

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -110,26 +110,26 @@ The C++ code involved with the signature module is completely in the file
110110
shiboken6/libshiboken/signature.cpp . All other functionality is implemented in
111111
the ``signature`` Python package. It has the following structure::
112112

113-
sources/shiboken6/shibokenmodule/files.dir/shibokensupport
114-
├── __init__.py
115-
├── feature.py
116-
├── fix-complaints.py
117-
├── shibokensupport.pyproject
118-
└── signature
119-
├── PSF-3.7.0.txt
120-
├── __init__.py
121-
├── errorhandler.py
122-
├── importhandler.py
123-
├── layout.py
124-
├── lib
125-
│   ├── __init__.py
126-
│   ├── enum_sig.py
127-
│   ├── pyi_generator.py
128-
│   └── tool.py
129-
├── loader.py
130-
├── mapping.py
131-
├── parser.py
132-
└── qt_attribution.json
113+
sources/shiboken6/shibokenmodule/files.dir/shibokensupport
114+
├── __init__.py
115+
├── feature.py
116+
├── fix-complaints.py
117+
├── shibokensupport.pyproject
118+
└── signature
119+
├── PSF-3.7.0.txt
120+
├── __init__.py
121+
├── errorhandler.py
122+
├── importhandler.py
123+
├── layout.py
124+
├── lib
125+
├── __init__.py
126+
├── enum_sig.py
127+
├── pyi_generator.py
128+
└── tool.py
129+
├── loader.py
130+
├── mapping.py
131+
├── parser.py
132+
└── qt_attribution.json
133133

134134
Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**,
135135
**layout** and **loader** modules. The rest is needed to create Python 2 compatibility
@@ -350,6 +350,23 @@ as default content for docstrings.
350350
This was implemented in ``Qt For Python 5.12.1``.
351351

352352

353+
Update and Future of the Signature Module
354+
-----------------------------------------
355+
356+
.. code-block:: bash
357+
358+
PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
359+
360+
End of 2022, a change to the rlcompleter module made it impossible to further
361+
support an unofficial ``__signature__`` attribute in PySide. From then on,
362+
the functionality of signatures was kept by a ``get_signature`` function.
363+
364+
Over the years, the requirements for the correctness of the generated pyi files
365+
have increased drastically, and a lot of effort went into making the generated
366+
``.pyi`` files correct for the current ``mypy`` tool. Mode information
367+
about the kind of errors corrected can be found in the :ref:`mypy-correctnes` section.
368+
369+
353370
Literature
354371
----------
355372

0 commit comments

Comments
 (0)