typing: Making pyqtSlot typed
Florian Bruhin
me at the-compiler.org
Thu Jun 29 23:17:13 BST 2023
Hey,
last one for today :)
pyqtSlot is currently typed as returning Any:
def pyqtSlot(
*types,
name: typing.Optional[str] = ...,
result: typing.Optional[str] = ...,
) -> typing.Any: ...
which means that incorrect code such as:
from PyQt6.QtCore import pyqtSlot
@pyqtSlot(int)
def func(a: int) -> None:
print(a)
func("not-an-int")
can't be typechecked because the pyqtSlot decorator loses the type
information:
slot.py:3: error: Untyped decorator makes function "func" untyped [misc]
A minimal improvement wound be:
FuncT = typing.TypeVar("FuncT", bound=typing.Callable)
def pyqtSlot(*types, name: typing.Optional[str] = ..., result:
typing.Optional[str] = ...) -> typing.Callable[[FuncT], FuncT]: ...
read as:
- FuncT is a type variable, representing any callable
- Calling pyqtSlot as e.g. pyqtSlot(int) returns a callable
(the decorator)
- That decorator takes a FuncT as argument, and returns a FuncT
Thus, we can teach the type system that @pyqtSlot does not touch the
decorated function's signature, which then results in mypy catching this
properly:
slot.py:7: error: Argument 1 to "func" has incompatible type "str";
expected "int" [arg-type]
func("not-an-int")
Interestingly, the PyQt6-stubs project has far more complex typing for
pyqtSlot:
https://github.com/python-qt-tools/PyQt6-
stubs/blob/f623a641cd5cdff53342177e4fbbf9cae8172336/PyQt6-
stubs/QtCore.pyi#L7537-L7563
It's unclear to me how that works in detail and if it's really needed.
The minimal fix above fixes all related errors for my project (but I
don't e.g. use QML).
Florian
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20230630/e1d36e31/attachment.sig>
More information about the PyQt
mailing list