Skip to content

Commit 939fc86

Browse files
authored
Added stubs for keyboard (#8666)
1 parent b6d28ac commit 939fc86

9 files changed

+315
-0
lines changed

pyrightconfig.stricter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"stubs/invoke",
5151
"stubs/jmespath",
5252
"stubs/jsonschema",
53+
"stubs/keyboard",
5354
"stubs/ldap3",
5455
"stubs/Markdown",
5556
"stubs/mock",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# scan_code *should* never be None in real use. This is also according to docs.
2+
keyboard.KeyboardEvent.scan_code
3+
keyboard._keyboard_event.KeyboardEvent.scan_code
4+
# Defaults don't align with possible values
5+
keyboard.mouse.on_button
6+
keyboard.mouse.wait
7+
# Private modules and tests
8+
keyboard.__main__
9+
keyboard._darwinkeyboard
10+
keyboard._darwinmouse
11+
keyboard._keyboard_tests
12+
keyboard._mouse_tests
13+
keyboard._nixcommon
14+
keyboard._nixkeyboard
15+
keyboard._nixmouse
16+
keyboard._winkeyboard
17+
keyboard._winmouse

stubs/keyboard/METADATA.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
version = "0.13.*"
2+
3+
[tool.stubtest]
4+
ignore_missing_stub = false

stubs/keyboard/keyboard/__init__.pyi

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from collections.abc import Callable, Generator, Iterable, Sequence
2+
from queue import Queue
3+
from threading import Event as _UninterruptibleEvent
4+
from typing import Optional
5+
from typing_extensions import TypeAlias
6+
7+
from ._canonical_names import all_modifiers as all_modifiers, sided_modifiers as sided_modifiers
8+
from ._generic import GenericListener as _GenericListener
9+
from ._keyboard_event import KEY_DOWN as KEY_DOWN, KEY_UP as KEY_UP, KeyboardEvent as KeyboardEvent
10+
11+
_Key: TypeAlias = int | str
12+
_ScanCodeList: TypeAlias = list[int] | tuple[int, ...]
13+
_ParseableHotkey: TypeAlias = _Key | list[int | _ScanCodeList] | tuple[int | _ScanCodeList, ...]
14+
_Callback: TypeAlias = Callable[[KeyboardEvent], Optional[bool]] | Callable[[], Optional[bool]]
15+
# mypy doesn't support PEP 646's TypeVarTuple yet: https://github.com/python/mypy/issues/12280
16+
# _Ts = TypeVarTuple("_Ts")
17+
_Ts: TypeAlias = tuple[object, ...]
18+
19+
version: str
20+
21+
class _Event(_UninterruptibleEvent):
22+
def wait(self) -> None: ... # type: ignore[override] # Actual implementation
23+
24+
def is_modifier(key: _Key | None) -> bool: ...
25+
def key_to_scan_codes(key: _ParseableHotkey, error_if_missing: bool = ...) -> tuple[int, ...]: ...
26+
def parse_hotkey(hotkey: _ParseableHotkey) -> tuple[tuple[tuple[int, ...], ...], ...]: ...
27+
def send(hotkey: _ParseableHotkey, do_press: bool = ..., do_release: bool = ...) -> None: ...
28+
29+
press_and_release = send
30+
31+
def press(hotkey: _ParseableHotkey) -> None: ...
32+
def release(hotkey: _ParseableHotkey) -> None: ...
33+
34+
# is_pressed cannot check multi-step hotkeys, so not using _ParseableHotkey
35+
36+
def is_pressed(hotkey: _Key | _ScanCodeList) -> bool: ...
37+
def call_later(fn: Callable[..., None], args: _Ts = ..., delay: float = ...) -> None: ...
38+
def hook(callback: _Callback, suppress: bool = ..., on_remove: Callable[[], None] = ...) -> Callable[[], None]: ...
39+
def on_press(callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ...
40+
def on_release(callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ...
41+
def hook_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ...
42+
def on_press_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ...
43+
def on_release_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ...
44+
def unhook(remove: _Callback) -> None: ...
45+
46+
unhook_key = unhook
47+
48+
def unhook_all() -> None: ...
49+
def block_key(key: _ParseableHotkey) -> Callable[[], None]: ...
50+
51+
unblock_key = unhook_key
52+
53+
def remap_key(src: _ParseableHotkey, dst: _ParseableHotkey) -> Callable[[], None]: ...
54+
55+
unremap_key = unhook_key
56+
57+
def parse_hotkey_combinations(hotkey: _ParseableHotkey) -> tuple[tuple[tuple[int, ...], ...], ...]: ...
58+
def add_hotkey(
59+
hotkey: _ParseableHotkey,
60+
callback: Callable[..., bool | None],
61+
args: _Ts = ...,
62+
suppress: bool = ...,
63+
timeout: float = ...,
64+
trigger_on_release: bool = ...,
65+
) -> Callable[[], None]: ...
66+
67+
register_hotkey = add_hotkey
68+
69+
def remove_hotkey(hotkey_or_callback: _ParseableHotkey | _Callback) -> None: ...
70+
71+
unregister_hotkey = remove_hotkey
72+
clear_hotkey = remove_hotkey
73+
74+
def unhook_all_hotkeys() -> None: ...
75+
76+
unregister_all_hotkeys = unhook_all_hotkeys
77+
remove_all_hotkeys = unhook_all_hotkeys
78+
clear_all_hotkeys = unhook_all_hotkeys
79+
80+
def remap_hotkey(
81+
src: _ParseableHotkey, dst: _ParseableHotkey, suppress: bool = ..., trigger_on_release: bool = ...
82+
) -> Callable[[], None]: ...
83+
84+
unremap_hotkey = remove_hotkey
85+
86+
def stash_state() -> list[int]: ...
87+
def restore_state(scan_codes: Iterable[int]) -> None: ...
88+
def restore_modifiers(scan_codes: Iterable[int]) -> None: ...
89+
def write(text: str, delay: float = ..., restore_state_after: bool = ..., exact: bool | None = ...) -> None: ...
90+
def wait(hotkey: _ParseableHotkey | None = ..., suppress: bool = ..., trigger_on_release: bool = ...) -> None: ...
91+
def get_hotkey_name(names: Iterable[str] | None = ...) -> str: ...
92+
def read_event(suppress: bool = ...) -> KeyboardEvent: ...
93+
def read_key(suppress: bool = ...) -> _Key: ...
94+
def read_hotkey(suppress: bool = ...) -> str: ...
95+
def get_typed_strings(events: Iterable[KeyboardEvent], allow_backspace: bool = ...) -> Generator[str, None, None]: ...
96+
def start_recording(
97+
recorded_events_queue: Queue[KeyboardEvent] | None = ...,
98+
) -> tuple[Queue[KeyboardEvent], Callable[[], None]]: ...
99+
def stop_recording() -> list[KeyboardEvent]: ...
100+
def record(until: str = ..., suppress: bool = ..., trigger_on_release: bool = ...) -> list[KeyboardEvent]: ...
101+
def play(events: Iterable[KeyboardEvent], speed_factor: float = ...) -> None: ...
102+
103+
replay = play
104+
105+
def add_word_listener(
106+
word: str, callback: _Callback, triggers: Sequence[str] = ..., match_suffix: bool = ..., timeout: float = ...
107+
) -> Callable[[], None]: ...
108+
def remove_word_listener(word_or_handler: str | _Callback) -> None: ...
109+
def add_abbreviation(
110+
source_text: str, replacement_text: str, match_suffix: bool = ..., timeout: float = ...
111+
) -> Callable[[], None]: ...
112+
113+
register_word_listener = add_word_listener
114+
register_abbreviation = add_abbreviation
115+
remove_abbreviation = remove_word_listener
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
canonical_names: dict[str, str]
2+
sided_modifiers: set[str]
3+
all_modifiers: set[str]
4+
5+
def normalize_name(name: str) -> str: ...

stubs/keyboard/keyboard/_generic.pyi

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from collections.abc import Callable
2+
from queue import Queue
3+
from threading import Lock, Thread
4+
from typing import ClassVar
5+
from typing_extensions import Literal, TypeAlias
6+
7+
from ._keyboard_event import KeyboardEvent
8+
from ._mouse_event import _MouseEvent
9+
10+
_Event: TypeAlias = KeyboardEvent | _MouseEvent
11+
12+
class GenericListener:
13+
lock: ClassVar[Lock]
14+
handlers: list[Callable[[_Event], bool | None]]
15+
listening: bool
16+
queue: Queue[_Event]
17+
listening_thread: Thread | None
18+
processing_thread: Thread | None
19+
def invoke_handlers(self, event: _Event) -> Literal[1] | None: ...
20+
def start_if_necessary(self) -> None: ...
21+
def pre_process_event(self, event: _Event) -> None: ...
22+
def process(self) -> None: ...
23+
def add_handler(self, handler: Callable[[_Event], bool | None]) -> None: ...
24+
def remove_handler(self, handler: Callable[[_Event], bool | None]) -> None: ...
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from typing_extensions import Literal
2+
3+
from ._canonical_names import canonical_names as canonical_names, normalize_name as normalize_name
4+
5+
KEY_DOWN: Literal["down"]
6+
KEY_UP: Literal["up"]
7+
8+
class KeyboardEvent:
9+
event_type: Literal["down", "up"] | None
10+
scan_code: int
11+
name: str | None
12+
time: float | None
13+
device: str | None
14+
modifiers: tuple[str, ...] | None
15+
is_keypad: bool | None
16+
17+
def __init__(
18+
self,
19+
event_type: Literal["down", "up"] | None,
20+
scan_code: int,
21+
name: str | None = ...,
22+
time: float | None = ...,
23+
device: str | None = ...,
24+
modifiers: tuple[str, ...] | None = ...,
25+
is_keypad: bool | None = ...,
26+
) -> None: ...
27+
def to_json(self, ensure_ascii: bool = ...) -> str: ...
28+
def __eq__(self, other: object) -> bool: ...
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import sys
2+
from typing import NamedTuple
3+
from typing_extensions import Literal, TypeAlias
4+
5+
_MouseEvent: TypeAlias = ButtonEvent | WheelEvent | MoveEvent # noqa: Y047 # Used outside
6+
7+
LEFT: Literal["left"]
8+
RIGHT: Literal["right"]
9+
MIDDLE: Literal["middle"]
10+
X: Literal["x"]
11+
X2: Literal["x2"]
12+
13+
UP: Literal["up"]
14+
DOWN: Literal["down"]
15+
DOUBLE: Literal["double"]
16+
WHEEL: Literal["wheel"]
17+
18+
VERTICAL: Literal["vertical"]
19+
HORIZONTAL: Literal["horizontal"]
20+
21+
if sys.platform == "linux" or sys.platform == "win32":
22+
_MouseButton: TypeAlias = Literal["left", "right", "middle", "x", "x2"]
23+
else:
24+
_MouseButton: TypeAlias = Literal["left", "right", "middle"]
25+
26+
if sys.platform == "win32":
27+
_MouseEventType: TypeAlias = Literal["up", "down", "double", "wheel"]
28+
else:
29+
_MouseEventType: TypeAlias = Literal["up", "down"]
30+
31+
class ButtonEvent(NamedTuple):
32+
event_type: _MouseEventType
33+
button: _MouseButton
34+
time: float
35+
36+
class WheelEvent(NamedTuple):
37+
delta: int
38+
time: float
39+
40+
class MoveEvent(NamedTuple):
41+
x: int
42+
y: int
43+
time: float

stubs/keyboard/keyboard/mouse.pyi

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import sys
2+
from collections.abc import Callable, Iterable
3+
from ctypes import c_long
4+
from typing import SupportsInt, TypeVar
5+
from typing_extensions import Literal, TypeAlias
6+
7+
from ._generic import GenericListener as _GenericListener
8+
from ._mouse_event import (
9+
DOUBLE as DOUBLE,
10+
DOWN as DOWN,
11+
LEFT as LEFT,
12+
MIDDLE as MIDDLE,
13+
RIGHT as RIGHT,
14+
UP as UP,
15+
X2 as X2,
16+
ButtonEvent as ButtonEvent,
17+
MoveEvent as MoveEvent,
18+
WheelEvent as WheelEvent,
19+
X as X,
20+
_MouseButton,
21+
_MouseEvent,
22+
_MouseEventType,
23+
)
24+
25+
# mypy doesn't support PEP 646's TypeVarTuple yet: https://github.com/python/mypy/issues/12280
26+
# _Ts = TypeVarTuple("_Ts")
27+
_Ts: TypeAlias = tuple[object, ...]
28+
_Callback: TypeAlias = Callable[[_MouseEvent], bool | None]
29+
_C = TypeVar("_C", bound=_Callback)
30+
31+
class _MouseListener(_GenericListener):
32+
def init(self) -> None: ...
33+
def pre_process_event( # type: ignore[override] # Mouse specific events and return
34+
self, event: _MouseEvent
35+
) -> Literal[True]: ...
36+
def listen(self) -> None: ...
37+
38+
def is_pressed(button: _MouseButton = ...): ...
39+
def press(button: _MouseButton = ...) -> None: ...
40+
def release(button: _MouseButton = ...) -> None: ...
41+
def click(button: _MouseButton = ...) -> None: ...
42+
def double_click(button: _MouseButton = ...) -> None: ...
43+
def right_click() -> None: ...
44+
def wheel(delta: int = ...) -> None: ...
45+
def move(x: SupportsInt, y: SupportsInt, absolute: bool = ..., duration: float = ...) -> None: ...
46+
def drag(start_x: int, start_y: int, end_x: int, end_y: int, absolute: bool = ..., duration: float = ...) -> None: ...
47+
def on_button(
48+
callback: Callable[..., None],
49+
args: _Ts = ...,
50+
buttons: list[_MouseButton] | tuple[_MouseButton, ...] | _MouseButton = ...,
51+
types: list[_MouseEventType] | tuple[_MouseEventType, ...] | _MouseEventType = ...,
52+
) -> _Callback: ...
53+
def on_click(callback: Callable[..., None], args: _Ts = ...) -> _Callback: ...
54+
def on_double_click(callback: Callable[..., None], args: _Ts = ...) -> _Callback: ...
55+
def on_right_click(callback: Callable[..., None], args: _Ts = ...) -> _Callback: ...
56+
def on_middle_click(callback: Callable[..., None], args: _Ts = ...) -> _Callback: ...
57+
def wait(button: _MouseButton = ..., target_types: tuple[_MouseEventType] = ...) -> None: ...
58+
59+
if sys.platform == "win32":
60+
def get_position() -> tuple[c_long, c_long]: ...
61+
62+
else:
63+
def get_position() -> tuple[int, int]: ...
64+
65+
def hook(callback: _C) -> _C: ...
66+
def unhook(callback: _Callback) -> None: ...
67+
def unhook_all() -> None: ...
68+
def record(button: _MouseButton = ..., target_types: tuple[_MouseEventType] = ...) -> _MouseEvent: ...
69+
def play(
70+
events: Iterable[_MouseEvent],
71+
speed_factor: float = ...,
72+
include_clicks: bool = ...,
73+
include_moves: bool = ...,
74+
include_wheel: bool = ...,
75+
) -> None: ...
76+
77+
replay = play
78+
hold = press

0 commit comments

Comments
 (0)