From 2cd206a8fa81c2add278dc7ebeeb0b3541a1debb Mon Sep 17 00:00:00 2001 From: Philippe Fremy Date: Wed, 26 Jul 2023 17:33:06 +0200 Subject: [PATCH] Fix Slot() to correctly support method inside class (instead of just functions) --- PySide2-stubs/QtCore.pyi | 56 +++++++------- tests/signal_slot.py | 3 +- tests/slot.py | 161 +++++++++++++++++++++------------------ tests/slotInsideClass.py | 7 -- 4 files changed, 117 insertions(+), 110 deletions(-) delete mode 100644 tests/slotInsideClass.py diff --git a/PySide2-stubs/QtCore.pyi b/PySide2-stubs/QtCore.pyi index 665c27a..68092f1 100644 --- a/PySide2-stubs/QtCore.pyi +++ b/PySide2-stubs/QtCore.pyi @@ -13766,12 +13766,12 @@ T3 = typing.TypeVar('T3') T4 = typing.TypeVar('T4') T5 = typing.TypeVar('T5') T6 = typing.TypeVar('T6') -Func_args_T1_return_RetT = typing.Callable[[T1], RetT] -Func_args_T1_T2_returns_RetT = typing.Callable[[T1, T2], RetT] -Func_args_T1_T2_T3_returns_RetT = typing.Callable[[T1, T2, T3], RetT] -Func_args_T1_T2_T3_T4_returns_RetT = typing.Callable[[T1, T2, T3, T4], RetT] -Func_args_T1_T2_T3_T4_T5_returns_RetT = typing.Callable[[T1, T2, T3, T4, T5], RetT] -Func_args_T1_T2_T3_T4_T5_T6_returns_RetT = typing.Callable[[T1, T2, T3, T4, T5, T6], RetT] +Method_args_T1_return_RetT = typing.Callable[[typing.Any, T1], RetT] +Method_args_T1_T2_returns_RetT = typing.Callable[[typing.Any, T1, T2], RetT] +Method_args_T1_T2_T3_returns_RetT = typing.Callable[[typing.Any, T1, T2, T3], RetT] +Method_args_T1_T2_T3_T4_returns_RetT = typing.Callable[[typing.Any, T1, T2, T3, T4], RetT] +Method_args_T1_T2_T3_T4_T5_returns_RetT = typing.Callable[[typing.Any, T1, T2, T3, T4, T5], RetT] +Method_args_T1_T2_T3_T4_T5_T6_returns_RetT = typing.Callable[[typing.Any, T1, T2, T3, T4, T5, T6], RetT] ### Note: Order matters, put the defintions with 'result' after the defintions with multiple arguments ### Note2: This will typecheck up to 5 arguments, with an optional result keyword argument @@ -13781,61 +13781,61 @@ Func_args_T1_T2_T3_T4_T5_T6_returns_RetT = typing.Callable[[T1, T2, T3, T4, T5, @typing.overload def Slot(t1: typing.Type[T1]) \ - -> typing.Callable[ [Func_args_T1_return_RetT[T1, RetT]], - Func_args_T1_return_RetT[T1, RetT] + -> typing.Callable[ [Method_args_T1_return_RetT[T1, RetT]], + Method_args_T1_return_RetT[T1, RetT] ]: return object @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2]) \ - -> typing.Callable[[Func_args_T1_T2_returns_RetT[T1, T2, RetT]], - Func_args_T1_T2_returns_RetT[T1, T2, RetT] + -> typing.Callable[[Method_args_T1_T2_returns_RetT[T1, T2, RetT]], + Method_args_T1_T2_returns_RetT[T1, T2, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3]) \ - -> typing.Callable[[Func_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT]], - Func_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT]], + Method_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], t4: typing.Type[T4]) \ - -> typing.Callable[[Func_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT]], - Func_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT]], + Method_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], t4: typing.Type[T4], t5: typing.Type[T5]) \ - -> typing.Callable[[Func_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT]], - Func_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT]], + Method_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT] ]: ... ### Note: we must declare a T6 version, so that the T5 version + result works correctly @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], t4: typing.Type[T4], t5: typing.Type[T5], t6: typing.Type[T6]) \ - -> typing.Callable[[Func_args_T1_T2_T3_T4_T5_T6_returns_RetT[T1, T2, T3, T4, T5, T6, RetT]], - Func_args_T1_T2_T3_T4_T5_T6_returns_RetT[T1, T2, T3, T4, T5, T6, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_T4_T5_T6_returns_RetT[T1, T2, T3, T4, T5, T6, RetT]], + Method_args_T1_T2_T3_T4_T5_T6_returns_RetT[T1, T2, T3, T4, T5, T6, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], result: typing.Type[RetT]) \ - -> typing.Callable[ [Func_args_T1_return_RetT[T1, RetT]], - Func_args_T1_return_RetT[T1, RetT] + -> typing.Callable[ [Method_args_T1_return_RetT[T1, RetT]], + Method_args_T1_return_RetT[T1, RetT] ]: return object @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], result: typing.Type[RetT]) \ - -> typing.Callable[[Func_args_T1_T2_returns_RetT[T1, T2, RetT]], - Func_args_T1_T2_returns_RetT[T1, T2, RetT] + -> typing.Callable[[Method_args_T1_T2_returns_RetT[T1, T2, RetT]], + Method_args_T1_T2_returns_RetT[T1, T2, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], result: typing.Type[RetT]) \ - -> typing.Callable[[Func_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT]], - Func_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT]], + Method_args_T1_T2_T3_returns_RetT[T1, T2, T3, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], t4: typing.Type[T4], result: typing.Type[RetT]) \ - -> typing.Callable[[Func_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT]], - Func_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT]], + Method_args_T1_T2_T3_T4_returns_RetT[T1, T2, T3, T4, RetT] ]: ... @typing.overload def Slot(t1: typing.Type[T1], t2: typing.Type[T2], t3: typing.Type[T3], t4: typing.Type[T4], t5: typing.Type[T5], result: typing.Type[RetT]) \ - -> typing.Callable[[Func_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT]], - Func_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT] + -> typing.Callable[[Method_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT]], + Method_args_T1_T2_T3_T4_T5_returns_RetT[T1, T2, T3, T4, T5, RetT] ]: ... @typing.overload def Slot(*some_types: typing.Type, result: typing.Type = ...) \ diff --git a/tests/signal_slot.py b/tests/signal_slot.py index 98cda22..0597baa 100644 --- a/tests/signal_slot.py +++ b/tests/signal_slot.py @@ -10,7 +10,6 @@ class SomeClassWithSignal(QObject): def __init__(self) -> None: super().__init__() # note: this is mandatory for mypy to pickup the class attribute access - self.connect(self,SIGNAL('clicked()'),self.my_slot_no_arg) def my_slot_no_arg(self) -> None: pass @@ -21,6 +20,8 @@ def my_slot_str(self, msg: str) -> None: instance = SomeClassWithSignal() +SomeClassWithSignal.connect(instance, SIGNAL('clicked()'), instance.my_slot_no_arg) + connection = True connection = instance.signal_no_arg.connect(instance.my_slot_no_arg) instance.signal_no_arg.emit() diff --git a/tests/slot.py b/tests/slot.py index 877d470..78ae9eb 100644 --- a/tests/slot.py +++ b/tests/slot.py @@ -1,109 +1,122 @@ from typing import Callable, TypeVar, Type, overload, Any -from PySide2.QtCore import Slot +from PySide2.QtCore import Slot, QObject some_str: str some_int: int -@Slot(int) -def f_int_returns_str1(i: int) -> str: - return 'abc' +class SomeClass1(QObject): + @Slot(int) + def f_int_returns_str1(self, i: int) -> str: + return 'abc' -some_str = f_int_returns_str1(33) -some_int = f_int_returns_str1(33) # type: ignore[assignment] -some_str = f_int_returns_str1('abc') # type: ignore[arg-type] +sc1 = SomeClass1() +some_str = sc1.f_int_returns_str1(33) +some_int = sc1.f_int_returns_str1(33) # type: ignore[assignment] +some_str = sc1.f_int_returns_str1('abc') # type: ignore[arg-type] -@Slot(int, result=str) -def f_int_returns_str2(i: int) -> str: - return 'abc' -some_str = f_int_returns_str2(33) -some_int = f_int_returns_str2(33) # type: ignore[assignment] +class SomeClass2(QObject): + @Slot(int, result=str) + def f_int_returns_str2(self, i: int) -> str: + return 'abc' +sc2 = SomeClass2() +some_str = sc2.f_int_returns_str2(33) +some_int = sc2.f_int_returns_str2(33) # type: ignore[assignment] -@Slot(int, result=int) # type: ignore[arg-type] -def f_int_returns_str3(i: int) -> str: - return 'abc' +class SomeClass3(QObject): + @Slot(int, result=int) # type: ignore[arg-type] + def f_int_returns_str3(self, i: int) -> str: + return 'abc' -@Slot(int, float) -def f_int_float_returns_str1(i: int, f: float) -> str: - return 'abc' + @Slot(int, float) + def f_int_float_returns_str1(self, i: int, f: float) -> str: + return 'abc' -some_str = f_int_float_returns_str1(33, 1.0) -some_int = f_int_float_returns_str1(33, 1.0) # type: ignore[assignment] -some_str = f_int_float_returns_str1('abc', 1.0) # type: ignore[arg-type] -some_str = f_int_float_returns_str1(33, 'abc') # type: ignore[arg-type] +sc3 = SomeClass3() +some_str = sc3.f_int_float_returns_str1(33, 1.0) +some_int = sc3.f_int_float_returns_str1(33, 1.0) # type: ignore[assignment] +some_str = sc3.f_int_float_returns_str1('abc', 1.0) # type: ignore[arg-type] +some_str = sc3.f_int_float_returns_str1(33, 'abc') # type: ignore[arg-type] -@Slot(int, float, result=int) # type: ignore[arg-type] -def f_int_float_returns_str2(i: int, f: float) -> str: - return 'abc' +class SomeClass4(QObject): + @Slot(int, float, result=int) # type: ignore[arg-type] + def f_int_float_returns_str2(self, i: int, f: float) -> str: + return 'abc' -@Slot(int, float, str) -def f_int_float_str_returns_str1(i: int, f: float, s: str) -> str: - return 'abc' + @Slot(int, float, str) + def f_int_float_str_returns_str1(self, i: int, f: float, s: str) -> str: + return 'abc' -some_str = f_int_float_str_returns_str1(33, 1.0, 'abc') -some_int = f_int_float_str_returns_str1(33, 1.0, 'abc') # type: ignore[assignment] -some_str = f_int_float_str_returns_str1('abc', 'abc', 33) # type: ignore[arg-type] +sc4 = SomeClass4() +some_str = sc4.f_int_float_str_returns_str1(33, 1.0, 'abc') +some_int = sc4.f_int_float_str_returns_str1(33, 1.0, 'abc') # type: ignore[assignment] +some_str = sc4.f_int_float_str_returns_str1('abc', 'abc', 33) # type: ignore[arg-type] -@Slot(int, float, str, result=float) # type: ignore[arg-type] -def f_int_float_str_returns_str2(i: int, f: float, s: str) -> str: - return 'abc' +class SomeClass5(QObject): + @Slot(int, float, str, result=float) # type: ignore[arg-type] + def f_int_float_str_returns_str2(self, i: int, f: float, s: str) -> str: + return 'abc' -@Slot(int, float, str, int) -def f_int_float_str_int_returns_str1(i: int, f: float, s: str, i2: int) -> str: - return 'abc' + @Slot(int, float, str, int) + def f_int_float_str_int_returns_str1(self, i: int, f: float, s: str, i2: int) -> str: + return 'abc' -some_str = f_int_float_str_int_returns_str1(33, 1.0, 'abc', 33) -some_int = f_int_float_str_int_returns_str1(33, 1.0, 'abc', 33) # type: ignore[assignment] -some_str = f_int_float_str_int_returns_str1(33, 1.0, 'abc', 'abc') # type: ignore[arg-type] +sc5 = SomeClass5() +some_str = sc5.f_int_float_str_int_returns_str1(33, 1.0, 'abc', 33) +some_int = sc5.f_int_float_str_int_returns_str1(33, 1.0, 'abc', 33) # type: ignore[assignment] +some_str = sc5.f_int_float_str_int_returns_str1(33, 1.0, 'abc', 'abc') # type: ignore[arg-type] -@Slot(int, float, str, int, result=float) # type: ignore[arg-type] -def f_int_float_str_int_returns_str2(i: int, f: float, s: str, i2: int) -> str: - return 'abc' +class SomeClass6(QObject): + @Slot(int, float, str, int, result=float) # type: ignore[arg-type] + def f_int_float_str_int_returns_str2(self, i: int, f: float, s: str, i2: int) -> str: + return 'abc' -@Slot(int, float, str, float, result=str) # type: ignore[arg-type] -def f_int_float_str_int_returns_str3(i: int, f: float, s: str, i2: int) -> str: - return 'abc' + @Slot(int, float, str, float, result=str) # type: ignore[arg-type] + def f_int_float_str_int_returns_str3(self, i: int, f: float, s: str, i2: int) -> str: + return 'abc' + @Slot(int, float, str, int, bytes) + def f_int_float_str_int_bytes_returns_str1(self, i: int, f: float, s: str, i2: int, b: bytes) -> str: + return 'abc' +sc6 = SomeClass6() +some_str = sc6.f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, b'12') +some_int = sc6.f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, b'12') # type: ignore[assignment] +some_str = sc6.f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, 'abc') # type: ignore[arg-type] -@Slot(int, float, str, int, bytes) -def f_int_float_str_int_bytes_returns_str1(i: int, f: float, s: str, i2: int, b: bytes) -> str: - return 'abc' +class SomeClass7(QObject): + @Slot(int, float, str, int, bytes, result=float) # type: ignore[arg-type] + def f_int_float_str_int_bytes_returns_str2(self, i: int, f: float, s: str, i2: int, b: bytes) -> str: + return 'abc' -some_str = f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, b'12') -some_int = f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, b'12') # type: ignore[assignment] -some_str = f_int_float_str_int_bytes_returns_str1(33, 1.0, 'abc', 33, 'abc') # type: ignore[arg-type] + @Slot(int, float, str, float, bytes, result=str) # type: ignore[arg-type] + def f_int_float_str_int_bytes_returns_str3(self, i: int, f: float, s: str, i2: int, b: bytes) -> str: + return 'abc' -@Slot(int, float, str, int, bytes, result=float) # type: ignore[arg-type] -def f_int_float_str_int_bytes_returns_str2(i: int, f: float, s: str, i2: int, b: bytes) -> str: - return 'abc' + # For 6 arguments, it still works without the result argument + @Slot(int, float, str, int, bytes, float) + def f_int_float_str_int_bytes_float_returns_str(self, i: int, f: float, s: str, i2: int, b1: bytes, f2: float) -> str: + return 'abc' -@Slot(int, float, str, float, bytes, result=str) # type: ignore[arg-type] -def f_int_float_str_int_bytes_returns_str3(i: int, f: float, s: str, i2: int, b: bytes) -> str: - return 'abc' +sc7 = SomeClass7() +some_str = sc7.f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, b'12', 1.0) +some_int = sc7.f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, b'12', 1.0) # type: ignore[assignment] +some_str = sc7.f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, 'abc', 'abc') # type: ignore[arg-type] + +class SomeClass8(QObject): + # For 6 arguments, with the result argument, arguments are no longer type-checked. + @Slot(int, float, str, int, bytes, float, result=str) + def f_int_float_str_int_bytes_float_returns_str2(self, s: str) -> str: + return 'abc' - -# For 6 arguments, it still works without the result argument -@Slot(int, float, str, int, bytes, float) -def f_int_float_str_int_bytes_float_returns_str(i: int, f: float, s: str, i2: int, b1: bytes, f2: float) -> str: - return 'abc' - -some_str = f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, b'12', 1.0) -some_int = f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, b'12', 1.0) # type: ignore[assignment] -some_str = f_int_float_str_int_bytes_float_returns_str(33, 1.0, 'abc', 33, 'abc', 'abc') # type: ignore[arg-type] - -# For 6 arguments, with the result argument, arguments are no longer type-checked. -@Slot(int, float, str, int, bytes, float, result=str) -def f_int_float_str_int_bytes_float_returns_str2(s: str) -> str: - return 'abc' - +sc8 = SomeClass8() # but return value is still type-checked -some_int = f_int_float_str_int_bytes_float_returns_str2('abc') # type: ignore[assignment] +some_int = sc8.f_int_float_str_int_bytes_float_returns_str2('abc') # type: ignore[assignment] diff --git a/tests/slotInsideClass.py b/tests/slotInsideClass.py deleted file mode 100644 index fcb3ae9..0000000 --- a/tests/slotInsideClass.py +++ /dev/null @@ -1,7 +0,0 @@ -from PySide2.QtCore import Slot, QObject - -class SomeClass(QObject): - - @Slot(str) - def someMethod(self, stra): - ...