Fixing Type Hints¶
Overview¶
If you notice any type hint issues in your project while using mypy with the PySide6 API, this document provides guidance on how to identify, correct, and maintain accurate type hints. Improving type hints enhances IDE support, enables better code completion, and improves static analysis with tools like mypy.
PySide6 uses Shiboken to generate Python bindings from C++ headers, but:
Some type hints are missing or too generic (e.g.,
Any
)Some are incorrect due to overloads or inheritance issues
Some APIs are dynamic or wrapped in unusual ways
Fixing these improves developer experience for all users of PySide6.
Note
Please refer to our contribution guideline.
Tools and Setup¶
To find and fix the type hints ensure that your development environment has PySide6 installed.
python -m venv venv
source venv/bin/activate
pip install PySide6 mypy
or, the entire PySide6 project can be cloned:
git clone https://code.qt.io/pyside/pyside-setup.git
cd pyside-setup
Finding Type Hints Issues¶
You can locate the type hints issues using a static analysis tool, such as mypy
.
mypy your_project/
How to Fix¶
PySide6 uses Shiboken to generate bindings, and type information can come from either static typesystem XML files or dynamic Python-based signature definitions.
1. Fixing with Typesystem Files¶
Each Qt module has an associated XML typesystem (e.g., typesystem_widgets.xml). You can specify or override type hints using the pyi-type attribute.
Example from typesystem_gui_common.xml:
<modify-function signature="inverted(bool*)const">
<modify-argument index="return" pyi-type="Tuple[PySide6.QtGui.QTransform, bool]">
<replace-type modified-type="PyTuple"/>
</modify-argument>
</modify-function>
2. Fixing with Python Signature Support¶
Python scripts located under shiboken module.
Key Files:
enum_sig.py
: Enumerates all signatures of a classpyi_generator.py
: Generates.pyi
files for arbitrary moduleslayout.py
: Configures differently formatted versions of signaturesmapping.py
: Maps the C++ types to Python equivalentsparser.py
: Parses the signature text and creates properties for the signature objects
The Python scripts here are responsible for parsing signature texts, creating signatures, and applying custom rules to handle special behaviors in the PySide6 API.
Rebuild and Verify¶
After modifying typesystem files or Python signature logic, rebuild PySide6 to regenerate
.pyi
files:
python setup.py build --qtpaths=/path/to/qtpaths --parallel=8 --reuse-build
This regenerates the bindings and updates the stub files used for static analysis.
To verify the changes, you can either run mypy
on your own code:
mypy your_project/
Or use the dedicated script provided by PySide6 to validate the generated stubs:
python sources/pyside6/tests/pysidetest/mypy_correctness_test.py
This tool runs mypy
against the generated stub files to detect type annotation issues and
inconsistencies. It is typically used to validate .pyi
stubs in the build directory after
generation.
Special Cases and Workarounds¶
While most type hints in PySide6 are generated automatically through typesystem XML or parsed function signatures, there are a number of special cases that require manual intervention.
Missing Optional Return Values¶
Some functions in the PySide6 stub files are missing None
as a valid return type. This usually
happens when the C++ method returns a pointer, which may be nullptr
, but the automatic
generator assumes a non-optional return in Python. To fix these cases, PySide6 maintains a list of
such functions in the mapping.py file.
Look for the set named:
missing_optional_return = {}
This is a list of functions where the return type should be wrapped in Optional[...]
. These
entries override the default behavior and ensure that the generated .pyi
stub files correctly
reflect that the function may return None
.
Char* Argument Mapping¶
In C++, char*
is commonly used to represent both binary data (as bytes
) and null-terminated
strings (as str
). By default, PySide6 maps char*
to bytes
in type hints.
However, some Qt functions are known to expect text input and should be treated as accepting str
instead. To handle these exceptions, PySide6 overrides the default mapping for specific functions.
This override is defined in the mapping.py file.
Look for the following logic:
# Special case - char* can either be 'bytes' or 'str'. The default is 'bytes'.
# Here we manually set it to map to 'str'.
type_map_tuple.update({("PySide6.QtCore.QObject.setProperty", "char*"): str})
type_map_tuple.update({("PySide6.QtCore.QObject.property", "char*"): str})
type_map_tuple.update({("PySide6.QtCore.QObject.inherits", "char*"): str})
...
Each entry is a (function_name, argument_type)
tuple mapped to the correct Python type. This
ensures that generated stubs reflect the expected usage of str
rather than bytes
.