Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR: Contributing type hints to pytest-qt #432

Closed
adam-grant-hendry opened this issue Jun 15, 2022 · 3 comments
Closed

FR: Contributing type hints to pytest-qt #432

adam-grant-hendry opened this issue Jun 15, 2022 · 3 comments

Comments

@adam-grant-hendry
Copy link

I would like to contribute type hinting to pytest-qt.

@The-Compiler If you are comfortable with this,

  1. Do you want a separate stub-only package (pytest-qt-stubs or types-pytest-qt) or as part of the code?
  2. Do you want separate stub files, or have all type hints in the source?

Options are specified here: PEP 561

For starters, here is my stub for qtbot.py:

from __future__ import annotations

from collections.abc import Generator
from types import TracebackType
from typing import Any, Callable, Optional, TypeVar

from _pytest.fixtures import SubRequest
from pytestqt.exceptions import TimeoutError as TimeoutError
from pytestqt.qt_compat import qt_api as qt_api
from pytestqt.wait_signal import CallbackBlocker as CallbackBlocker
from pytestqt.wait_signal import CallbackCalledTwiceError as CallbackCalledTwiceError
from pytestqt.wait_signal import MultiSignalBlocker as MultiSignalBlocker
from pytestqt.wait_signal import SignalBlocker as SignalBlocker
from pytestqt.wait_signal import SignalEmittedError as SignalEmittedError
from pytestqt.wait_signal import SignalEmittedSpy as SignalEmittedSpy

AssertReturn = Optional[bool | AssertionError]

class QtBot:
    def __init__(
        self,
        request: SubRequest,
    ) -> None: ...
    def addWidget(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        before_close_func: Optional[Callable[..., Any]] = ...,
    ) -> None: ...
    def waitActive(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        timeout: int = ...,
    ) -> _WaitWidgetContextManager: ...
    def waitExposed(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        timeout: int = ...,
    ) -> _WaitWidgetContextManager: ...
    def waitForWindowShown(
        self,
        widget: qt_api.QtWidgets.QWidget,
    ) -> bool: ...
    def stop(self) -> None: ...
    def waitSignal(
        self,
        signal: qt_api.QtCore.SignalInstance,
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
        check_params_cb: Callable[..., bool] = ...,
    ) -> SignalBlocker: ...
    def waitSignals(
        self,
        signals: list[qt_api.QtCore.SignalInstance],
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
        check_params_cbs: Callable[..., bool] = ...,
        order: str = ...,
    ) -> MultiSignalBlocker: ...
    def wait(
        self,
        ms: int,
    ) -> None: ...
    def assertNotEmitted(
        self,
        signal: qt_api.QtCore.SignalInstance,
        *,
        wait: int = ...,
    ) -> Generator[None, None, None]: ...
    def waitUntil(
        self,
        callback: Callable[
            ...,
            AssertReturn,
        ],
        *,
        timeout: int = ...,
    ) -> None: ...
    def waitCallback(
        self,
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
    ) -> CallbackBlocker: ...
    def captureExceptions(
        self,
    ) -> Generator[
        list[
            tuple[
                Optional[type[BaseException]],
                Optional[BaseException],
                Optional[TracebackType],
            ]
        ],
        None,
        None,
    ]: ...
    @staticmethod
    def keyClick(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __key: qt_api.QtCore.Qt.Key | str,  # c char
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def keyClicks(
        __widget: qt_api.QtWidgets.QWidget,
        __sequence: qt_api.QtCore.Qt.QString,
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        delay: int = ...,
    ) -> None: ...
    @staticmethod
    def keyEvent(
        __action: qt_api.QtTests.QTest.KeyAction,
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __key_ascii: qt_api.QtGui.Key | str,  # c char
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def keyPress(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __key: qt_api.QtCore.Qt.Key | str,  # c char
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def keyRelease(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __key: qt_api.QtCore.Qt.Key | str,  # c char
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def keySequence(
        widget: qt_api.QtWidgets.QWidget,
        key_sequence: qt_api.QtGui.QKeySequence,
    ) -> None: ...
    @staticmethod
    def keyToAscii(key: qt_api.QtGui.Key | str) -> str: ...
    @staticmethod
    def mouseClick(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __button: qt_api.QtCore.Qt.MouseButton,
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        pos: qt_api.QtCore.QPoint = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def mouseDClick(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __button: qt_api.QtCore.Qt.MouseButton,
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        pos: qt_api.QtCore.QPoint = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def mouseMove(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        pos: qt_api.QtCore.QPoint = ...,
        delay: int = ...,
    ) -> None: ...
    @staticmethod
    def mousePress(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __button: qt_api.QtCore.Qt.MouseButton,
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        pos: qt_api.QtCore.QPoint = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    @staticmethod
    def mouseRelease(
        __widget_window: qt_api.QtWidgets.QWidget | qt_api.QtGui.QWindow,
        __button: qt_api.QtCore.Qt.MouseButton,
        modifier: qt_api.QtCore.Qt.KeyboardModifiers = ...,
        pos: qt_api.QtCore.QPoint = ...,
        delay: int = ...,  # c int
    ) -> None: ...
    def add_widget(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        before_close_func: Optional[Callable[..., Any]] = ...,
    ) -> None: ...
    def wait_active(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        timeout: int = ...,
    ) -> _WaitWidgetContextManager: ...
    def wait_exposed(
        self,
        widget: qt_api.QtWidgets.QWidget,
        *,
        timeout: int = ...,
    ) -> _WaitWidgetContextManager: ...
    def wait_for_window_shown(
        self,
        widget: qt_api.QtWidgets.QWidget,
    ) -> bool: ...
    def wait_signal(
        self,
        signal: qt_api.QtCore.SignalInstance,
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
        check_params_cb: Callable[..., bool] = ...,
    ) -> SignalBlocker: ...
    def wait_signals(
        self,
        signals: list[qt_api.QtCore.SignalInstance],
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
        check_params_cbs: Callable[..., bool] = ...,
        order: str = ...,
    ) -> MultiSignalBlocker: ...
    def assert_not_emitted(
        self,
        signal: qt_api.QtCore.SignalInstance,
        *,
        wait: int = ...,
    ) -> Generator[None, None, None]: ...
    def wait_until(
        self,
        callback: Callable[
            ...,
            AssertReturn,
        ],
        *,
        timeout: int = ...,
    ) -> None: ...
    def wait_callback(
        self,
        *,
        timeout: int = ...,
        raising: Optional[bool] = ...,
    ) -> CallbackBlocker: ...

Self = TypeVar('Self', bound='_WaitWidgetContextManager')

class _WaitWidgetContextManager:
    def __init__(
        self,
        method_name: str,
        adjective_name: str,
        widget: qt_api.QtWidgets.QWidget,
        timeout: int,
    ) -> None: ...
    def __enter__(self: Self) -> Self: ...
    def __exit__(
        self,
        exc_type: Optional[type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> None: ...
@nicoddemus
Copy link
Member

nicoddemus commented Jun 15, 2022

Just to link the issues, there's been a discussion/work in #417 also.

@adam-grant-hendry
Copy link
Author

adam-grant-hendry commented Jun 15, 2022

Thank you, I should have looked more closely. Would you like me to close this issue and continue discussions there?

Specifically, I'm interested in knowing if the pytest-qt maintainers are open to adding type hints, and if so, how they would like to implement them per the available options outlined in PEP 561

@nicoddemus
Copy link
Member

I'm interested in knowing if the pytest-qt maintainers are open to adding type hints

Definitely! 👍

how they would like to implement them per the available options outlined in PEP 561

Inline, as we only support Python 3.7+, and adding them to pytest-qt (as opposed to a stubs/types separate project).

Let's close this then and keep the discussion in #417 only. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants