Skip to content

Commit

Permalink
Fix Slot() to correctly support method inside class (instead of just …
Browse files Browse the repository at this point in the history
…functions)
  • Loading branch information
bluebird75 committed Jul 26, 2023
1 parent 07b4311 commit 2cd206a
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 110 deletions.
56 changes: 28 additions & 28 deletions PySide2-stubs/QtCore.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = ...) \
Expand Down
3 changes: 2 additions & 1 deletion tests/signal_slot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down
161 changes: 87 additions & 74 deletions tests/slot.py
Original file line number Diff line number Diff line change
@@ -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]



Expand Down
7 changes: 0 additions & 7 deletions tests/slotInsideClass.py

This file was deleted.

0 comments on commit 2cd206a

Please sign in to comment.