keyboard_controller_2026.py
from __future__ import annotations
import argparse
import platform
import sys
import time
from dataclasses import dataclass
from typing import Callable
@dataclass(frozen=True)
class Action:
"""A keyboard action that can be previewed or executed."""
description: str
callback: Callable[[object], None]
def import_keyboard():
"""Import keyboard lazily so --dry-run works in headless/test environments."""
try:
import keyboard
except ImportError as exc:
raise SystemExit("Install the dependency first: pip install keyboard") from exc
return keyboard
def platform_hint() -> str:
"""Return a short note about common platform requirements."""
system = platform.system().lower()
if system == "linux":
return "Linux usually needs root privileges or access to /dev/input for global hooks."
if system == "darwin":
return "macOS support in the keyboard package is limited; pynput or PyAutoGUI is often a better fit."
if system == "windows":
return "Windows is the smoothest platform for the keyboard package."
return "Global keyboard hooks depend on your operating system and permissions."
def preview_or_run(action: Action, keyboard_module: object | None, execute: bool) -> None:
"""Print what would happen, or run the action when --execute is set."""
if not execute:
print(f"DRY RUN: {action.description}")
return
if keyboard_module is None:
raise RuntimeError("keyboard module is required when execute=True")
print(f"RUNNING: {action.description}")
action.callback(keyboard_module)
def demo_typing(execute: bool) -> None:
"""Type text into the currently focused window."""
keyboard = import_keyboard() if execute else None
actions = [
Action(
"type 'Hello from Python!' with a small delay",
lambda kb: kb.write("Hello from Python!", delay=0.03),
),
Action("press Enter", lambda kb: kb.press_and_release("enter")),
Action("send Ctrl+A", lambda kb: kb.send("ctrl+a")),
]
for action in actions:
preview_or_run(action, keyboard, execute)
def demo_hotkeys(execute: bool) -> None:
"""Register a global hotkey and an abbreviation."""
keyboard = import_keyboard() if execute else None
if not execute:
print("DRY RUN: register Ctrl+Alt+H to print a message")
print("DRY RUN: expand '@email' into 'name@example.com'")
print("DRY RUN: wait until Esc is pressed")
return
keyboard.add_hotkey("ctrl+alt+h", lambda: print("Ctrl+Alt+H pressed"))
keyboard.add_abbreviation("@email", "name@example.com")
print("Hotkeys are active. Type @email + space in another app, or press Ctrl+Alt+H.")
print("Press Esc to stop.")
keyboard.wait("esc")
keyboard.unhook_all()
def demo_record(execute: bool) -> None:
"""Record keyboard events until Esc, then replay them after confirmation."""
keyboard = import_keyboard() if execute else None
if not execute:
print("DRY RUN: record keyboard events until Esc")
print("DRY RUN: show typed strings")
print("DRY RUN: ask before replaying events")
return
print("Recording now. Press Esc to stop.")
events = keyboard.record("esc")
typed = list(keyboard.get_typed_strings(events))
print("Typed strings:", typed)
answer = input("Replay the recorded events? [y/N] ").strip().lower()
if answer == "y":
print("Replaying in 3 seconds. Focus the target window now.")
time.sleep(3)
keyboard.play(events)
keyboard.unhook_all()
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Safe examples for controlling the keyboard in Python.")
parser.add_argument(
"demo",
choices=("typing", "hotkeys", "record"),
help="Which demo to run",
)
parser.add_argument(
"--execute",
action="store_true",
help="Actually send/listen to keyboard events. Without this flag, the script only prints what it would do.",
)
args = parser.parse_args(argv)
print(platform_hint())
if not args.execute:
print("Running in dry-run mode. Add --execute on your own desktop to perform the actions.\n")
if args.demo == "typing":
demo_typing(args.execute)
elif args.demo == "hotkeys":
demo_hotkeys(args.execute)
elif args.demo == "record":
demo_record(args.execute)
return 0
if __name__ == "__main__":
raise SystemExit(main())