Skip to content

Commit acc1994

Browse files
committed
add more typing
1 parent d9c8a5c commit acc1994

File tree

5 files changed

+66
-42
lines changed

5 files changed

+66
-42
lines changed

src/pytestqt/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from typing import cast
2+
13
# _version is automatically generated by setuptools_scm
2-
from pytestqt._version import version
4+
from pytestqt._version import version # type: ignore[import-not-found]
35

4-
__version__ = version
6+
__version__ = cast("str", version)

src/pytestqt/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
import sys
33
import traceback
44
from contextlib import contextmanager
5+
from types import TracebackType
6+
from typing import List, Tuple, Type
57

68
import pytest
79
from pytestqt.utils import get_marker
810

11+
CapturedException = Tuple[Type[BaseException], BaseException, TracebackType]
12+
CapturedExceptions = List[CapturedException]
913

1014
@contextmanager
1115
def capture_exceptions():

src/pytestqt/qt_compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def exec(self, obj, *args, **kwargs):
162162

163163
def get_versions(self):
164164
if self.pytest_qt_api == "pyside6":
165-
import PySide6
165+
import PySide6 # type: ignore[import-not-found]
166166

167167
version = PySide6.__version__
168168

src/pytestqt/qtbot.py

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import contextlib
2+
from types import TracebackType
23
import weakref
34
import warnings
4-
from typing import TYPE_CHECKING, Callable, Optional, Any, cast
5+
from typing import TYPE_CHECKING, Callable, Generator, Iterator, List, Literal, Optional, Any, Self, Type, cast
6+
from pathlib import Path
57

68
from pytestqt.exceptions import TimeoutError, ScreenshotError
79
from pytestqt.qt_compat import qt_api
@@ -12,17 +14,27 @@
1214
SignalEmittedError,
1315
CallbackBlocker,
1416
CallbackCalledTwiceError,
17+
CheckParamsCb,
1518
)
1619

20+
from pytest import FixtureRequest
21+
22+
# Type hint objects until figuring out how to import across qt
23+
# versions possibly using 'qtpy' library.
24+
QWidget = Any
25+
SignalInstance = Any
26+
QRect = Any
27+
QKeySequence = Any
28+
1729
if TYPE_CHECKING:
18-
# Type hint objects until figuring out how to import across qt
19-
# versions possibly using 'qtpy' library.
20-
QWidget = Any
30+
# Keep local import behavior the same.
31+
from pytestqt.exceptions import CapturedExceptions
2132

22-
BeforeCloseFunc = Callable[["QWidget"], None]
33+
BeforeCloseFunc = Callable[[QWidget], None]
34+
WaitSignalsOrder = Literal["none", "simple", "strict"]
2335

2436

25-
def _parse_ini_boolean(value: bool | str) -> bool:
37+
def _parse_ini_boolean(value: Any) -> bool:
2638
if value in (True, False):
2739
return cast("bool", value)
2840
try:
@@ -154,7 +166,7 @@ class QtBot:
154166
155167
"""
156168

157-
def __init__(self, request):
169+
def __init__(self, request: FixtureRequest) -> None:
158170
self._request = request
159171
# pep8 aliases. Set here to automatically use implementations defined in sub-classes for alias creation
160172
self.add_widget = self.addWidget
@@ -168,7 +180,7 @@ def __init__(self, request):
168180
self.wait_until = self.waitUntil
169181
self.wait_callback = self.waitCallback
170182

171-
def _should_raise(self, raising_arg):
183+
def _should_raise(self, raising_arg: Optional[bool]) -> bool:
172184
ini_val = self._request.config.getini("qt_default_raising")
173185

174186
if raising_arg is not None:
@@ -178,7 +190,7 @@ def _should_raise(self, raising_arg):
178190
else:
179191
return True
180192

181-
def addWidget(self, widget, *, before_close_func: Optional[BeforeCloseFunc] = None):
193+
def addWidget(self, widget: QWidget, *, before_close_func: Optional[BeforeCloseFunc] = None) -> None:
182194
"""
183195
Adds a widget to be tracked by this bot. This is not required, but will ensure that the
184196
widget gets closed by the end of the test, so it is highly recommended.
@@ -196,7 +208,7 @@ def addWidget(self, widget, *, before_close_func: Optional[BeforeCloseFunc] = No
196208
raise TypeError(f"Need to pass a QWidget to addWidget: {widget!r}")
197209
_add_widget(self._request.node, widget, before_close_func=before_close_func)
198210

199-
def waitActive(self, widget, *, timeout: int = 5000):
211+
def waitActive(self, widget: QWidget, *, timeout: int = 5000) -> "_WaitWidgetContextManager":
200212
"""
201213
Context manager that waits for ``timeout`` milliseconds or until the window is active.
202214
If window is not exposed within ``timeout`` milliseconds, raise
@@ -223,7 +235,7 @@ def waitActive(self, widget, *, timeout: int = 5000):
223235
"qWaitForWindowActive", "activated", widget, timeout
224236
)
225237

226-
def waitExposed(self, widget, *, timeout=5000):
238+
def waitExposed(self, widget: QWidget, *, timeout: int=5000) -> "_WaitWidgetContextManager":
227239
"""
228240
Context manager that waits for ``timeout`` milliseconds or until the window is exposed.
229241
If the window is not exposed within ``timeout`` milliseconds, raise
@@ -250,7 +262,7 @@ def waitExposed(self, widget, *, timeout=5000):
250262
"qWaitForWindowExposed", "exposed", widget, timeout
251263
)
252264

253-
def waitForWindowShown(self, widget):
265+
def waitForWindowShown(self, widget: QWidget) -> bool:
254266
"""
255267
Waits until the window is shown in the screen. This is mainly useful for asynchronous
256268
systems like X11, where a window will be mapped to screen some time after being asked to
@@ -282,7 +294,7 @@ def waitForWindowShown(self, widget):
282294
)
283295
return qt_api.QtTest.QTest.qWaitForWindowExposed(widget)
284296

285-
def stop(self):
297+
def stop(self) -> None:
286298
"""
287299
Stops the current test flow, letting the user interact with any visible widget.
288300
@@ -303,7 +315,14 @@ def stop(self):
303315
for widget, visible in widget_and_visibility:
304316
widget.setVisible(visible)
305317

306-
def waitSignal(self, signal, *, timeout=5000, raising=None, check_params_cb=None):
318+
def waitSignal(
319+
self,
320+
signal: "SignalInstance",
321+
*,
322+
timeout: int = 5000,
323+
raising: Optional[bool] = None,
324+
check_params_cb: Optional[CheckParamsCb] = None,
325+
) -> "SignalBlocker":
307326
"""
308327
.. versionadded:: 1.2
309328
@@ -366,13 +385,13 @@ def waitSignal(self, signal, *, timeout=5000, raising=None, check_params_cb=None
366385

367386
def waitSignals(
368387
self,
369-
signals,
388+
signals: List[SignalInstance],
370389
*,
371-
timeout=5000,
372-
raising=None,
373-
check_params_cbs=None,
374-
order="none",
375-
):
390+
timeout: int=5000,
391+
raising:Optional[bool]=None,
392+
check_params_cbs:Optional[List[CheckParamsCb]] =None,
393+
order: WaitSignalsOrder="none",
394+
) -> "MultiSignalBlocker":
376395
"""
377396
.. versionadded:: 1.4
378397
@@ -454,7 +473,7 @@ def waitSignals(
454473
blocker.add_signals(signals)
455474
return blocker
456475

457-
def wait(self, ms):
476+
def wait(self, ms: int) -> None:
458477
"""
459478
.. versionadded:: 1.9
460479
@@ -467,7 +486,7 @@ def wait(self, ms):
467486
blocker.wait()
468487

469488
@contextlib.contextmanager
470-
def assertNotEmitted(self, signal, *, wait=0):
489+
def assertNotEmitted(self, signal: SignalInstance, *, wait: int=0) -> Generator[None, None, None]:
471490
"""
472491
.. versionadded:: 1.11
473492
@@ -488,7 +507,7 @@ def assertNotEmitted(self, signal, *, wait=0):
488507
yield
489508
spy.assert_not_emitted()
490509

491-
def waitUntil(self, callback, *, timeout=5000):
510+
def waitUntil(self, callback: Callable[[], Optional[bool]], *, timeout: int=5000) -> None:
492511
"""
493512
.. versionadded:: 2.0
494513
@@ -559,7 +578,7 @@ def timed_out():
559578
raise TimeoutError(timeout_msg)
560579
self.wait(10)
561580

562-
def waitCallback(self, *, timeout=5000, raising=None):
581+
def waitCallback(self, *, timeout: int = 5000, raising: Optional[bool] = None) -> "CallbackBlocker":
563582
"""
564583
.. versionadded:: 3.1
565584
@@ -601,7 +620,7 @@ def waitCallback(self, *, timeout=5000, raising=None):
601620
return blocker
602621

603622
@contextlib.contextmanager
604-
def captureExceptions(self):
623+
def captureExceptions(self) -> Generator["CapturedExceptions", None, None]:
605624
"""
606625
.. versionadded:: 2.1
607626
@@ -625,9 +644,7 @@ def captureExceptions(self):
625644
with capture_exceptions() as exceptions:
626645
yield exceptions
627646

628-
capture_exceptions = captureExceptions
629-
630-
def screenshot(self, widget, suffix="", region=None):
647+
def screenshot(self, widget: QWidget, suffix: str="", region: Optional[QRect]=None) -> Path:
631648
"""
632649
.. versionadded:: 4.1
633650
@@ -700,13 +717,13 @@ def keyRelease(*args, **kwargs):
700717
qt_api.QtTest.QTest.keyRelease(*args, **kwargs)
701718

702719
@staticmethod
703-
def keySequence(widget, key_sequence):
720+
def keySequence(widget: QWidget, key_sequence: QKeySequence) -> None:
704721
if not hasattr(qt_api.QtTest.QTest, "keySequence"):
705722
raise NotImplementedError("This method is available from Qt 5.10 upwards.")
706723
qt_api.QtTest.QTest.keySequence(widget, key_sequence)
707724

708725
@staticmethod
709-
def keyToAscii(key):
726+
def keyToAscii(key: Any) -> None:
710727
if not hasattr(qt_api.QtTest.QTest, "keyToAscii"):
711728
raise NotImplementedError("This method isn't available on PyQt5.")
712729
qt_api.QtTest.QTest.keyToAscii(key)
@@ -740,11 +757,11 @@ def mouseRelease(*args, **kwargs):
740757

741758

742759
def _add_widget(
743-
item,
744-
widget,
760+
item: Any,
761+
widget: QWidget,
745762
*,
746763
before_close_func: Optional[BeforeCloseFunc] = None,
747-
):
764+
) -> None:
748765
"""
749766
Register a widget into the given pytest item for later closing.
750767
"""
@@ -753,7 +770,7 @@ def _add_widget(
753770
item.qt_widgets = qt_widgets
754771

755772

756-
def _close_widgets(item):
773+
def _close_widgets(item: Any) -> None:
757774
"""
758775
Close all widgets registered in the pytest item.
759776
"""
@@ -769,7 +786,7 @@ def _close_widgets(item):
769786
del item.qt_widgets
770787

771788

772-
def _iter_widgets(item):
789+
def _iter_widgets(item: Any) -> Iterator[weakref.ReferenceType[QWidget]]:
773790
"""
774791
Iterates over widgets registered in the given pytest item.
775792
"""
@@ -782,7 +799,7 @@ class _WaitWidgetContextManager:
782799
Context manager implementation used by ``waitActive`` and ``waitExposed`` methods.
783800
"""
784801

785-
def __init__(self, method_name, adjective_name, widget, timeout):
802+
def __init__(self, method_name: str, adjective_name: str, widget: QWidget, timeout: int) -> None:
786803
"""
787804
:param str method_name: name to the ``QtTest`` method to call to check if widget is active/exposed.
788805
:param str adjective_name: "activated" or "exposed".
@@ -794,11 +811,11 @@ def __init__(self, method_name, adjective_name, widget, timeout):
794811
self._widget = widget
795812
self._timeout = timeout
796813

797-
def __enter__(self):
814+
def __enter__(self) -> Self:
798815
__tracebackhide__ = True
799816
return self
800817

801-
def __exit__(self, exc_type, exc_val, exc_tb):
818+
def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]) -> None:
802819
__tracebackhide__ = True
803820
try:
804821
if exc_type is None:

src/pytestqt/wait_signal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import functools
22
import dataclasses
3-
from typing import Any
3+
from typing import Any, Callable
44

55
from pytestqt.exceptions import TimeoutError
66
from pytestqt.qt_compat import qt_api
77

8+
CheckParamsCb = Callable[..., bool]
89

910
class _AbstractSignalBlocker:
1011
"""

0 commit comments

Comments
 (0)