CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pynput

Monitor and control user input devices across multiple operating systems

Overview
Eval results
Files

keyboard.mddocs/

Keyboard Control and Monitoring

Comprehensive keyboard input simulation and monitoring functionality for programmatic key presses, string typing, modifier key combinations, hotkey detection, and real-time keyboard event listening across multiple operating systems.

Capabilities

Keyboard Controller

The Controller class provides programmatic control over keyboard input, enabling automation of key presses, text typing, and complex key combinations.

class Controller:
    """A controller for sending virtual keyboard events to the system."""

    class InvalidKeyException(Exception):
        """Raised when an invalid key parameter is passed."""
        pass

    class InvalidCharacterException(Exception):
        """Raised when an untypable character is encountered in type()."""
        pass

    def __init__(self):
        """Initialize the keyboard controller."""
        ...

    def press(self, key: Key | KeyCode | str):
        """
        Press a key.

        Args:
            key: The key to press. Can be:
                - Single character string (e.g., 'a', 'A', '1')
                - Key enum member (e.g., Key.enter, Key.ctrl)
                - KeyCode instance

        Raises:
            InvalidKeyException: If the key is invalid
            ValueError: If key is string with length != 1
        """
        ...

    def release(self, key: Key | KeyCode | str):
        """
        Release a key.

        Args:
            key: The key to release (same format as press())

        Raises:
            InvalidKeyException: If the key is invalid
            ValueError: If key is string with length != 1
        """
        ...

    def tap(self, key: Key | KeyCode | str):
        """
        Press and immediately release a key.

        Args:
            key: The key to tap (same format as press())

        Raises:
            InvalidKeyException: If the key is invalid
            ValueError: If key is string with length != 1
        """
        ...

    def touch(self, key: Key | KeyCode | str, is_press: bool):
        """
        Press or release a key based on boolean flag.

        Args:
            key: The key to touch
            is_press (bool): True to press, False to release

        Raises:
            InvalidKeyException: If the key is invalid
        """
        ...

    def pressed(self, *keys) -> ContextManager:
        """
        Context manager that keeps keys pressed for the duration of the block.

        Args:
            *keys: Keys to keep pressed

        Returns:
            ContextManager: Context manager for the key press duration

        Example:
            with keyboard.pressed(Key.ctrl):
                keyboard.tap('c')  # Ctrl+C
        """
        ...

    def type(self, string: str):
        """
        Type a string by sending key press and release events.

        Args:
            string (str): The string to type

        Raises:
            InvalidCharacterException: If an untypable character is encountered
        """
        ...

    @property
    def modifiers(self) -> ContextManager:
        """Context manager for accessing current modifier key state."""
        ...

    @property
    def alt_pressed(self) -> bool:
        """
        Whether any alt key is pressed.

        Note: This reflects only the internal state of this controller.
        See modifiers property for more information.
        """
        ...

    @property
    def alt_gr_pressed(self) -> bool:
        """
        Whether altgr is pressed.

        Note: This reflects only the internal state of this controller.
        See modifiers property for more information.
        """
        ...

    @property
    def ctrl_pressed(self) -> bool:
        """
        Whether any ctrl key is pressed.

        Note: This reflects only the internal state of this controller.
        See modifiers property for more information.
        """
        ...

    @property
    def shift_pressed(self) -> bool:
        """
        Whether any shift key is pressed, or caps lock is toggled.

        Note: This reflects only the internal state of this controller.
        See modifiers property for more information.
        """
        ...

Usage Examples

from pynput.keyboard import Key, Controller

# Create controller
keyboard = Controller()

# Type individual characters
keyboard.press('h')
keyboard.release('h')

# Use tap for press+release
keyboard.tap('i')

# Type special keys
keyboard.press(Key.enter)
keyboard.release(Key.enter)

# Type strings
keyboard.type('Hello, World!')

# Key combinations using context manager
with keyboard.pressed(Key.ctrl):
    keyboard.tap('c')  # Ctrl+C

# Multiple modifiers
with keyboard.pressed(Key.ctrl, Key.shift):
    keyboard.tap('a')  # Ctrl+Shift+A

# Manual modifier handling
keyboard.press(Key.alt)
keyboard.tap(Key.tab)  # Alt+Tab
keyboard.release(Key.alt)

# Uppercase typing
keyboard.tap('A')  # Types uppercase A
# OR
with keyboard.pressed(Key.shift):
    keyboard.tap('a')  # Also types uppercase A

Keyboard Event Listener

The Listener class monitors keyboard events in real-time, providing callbacks for key press and release events.

class Listener:
    """A listener for keyboard events."""

    def __init__(
        self,
        on_press: callable = None,
        on_release: callable = None,
        suppress: bool = False,
        **kwargs
    ):
        """
        Initialize the keyboard event listener.

        Args:
            on_press (callable): Callback for key press events (key, injected)
            on_release (callable): Callback for key release events (key, injected)
            suppress (bool): Whether to suppress events system-wide
            **kwargs: Platform-specific options
        """
        ...

    def start(self):
        """Start the listener thread."""
        ...

    def stop(self):
        """Stop the listener. Cannot be restarted once stopped."""
        ...

    def wait(self):
        """Wait for the listener to become ready."""
        ...

    def join(self, timeout: float = None):
        """
        Wait for the listener thread to complete.

        Args:
            timeout (float): Maximum time to wait in seconds
        """
        ...

    def canonical(self, key) -> Key | KeyCode:
        """Convert a key to its canonical representation."""
        ...

    @property
    def running(self) -> bool:
        """Whether the listener is currently running."""
        ...

    @property
    def suppress(self) -> bool:
        """Whether events are being suppressed system-wide."""
        ...

Usage Examples

from pynput import keyboard

def on_press(key, injected=False):
    """Handle key press events."""
    try:
        # Alphanumeric key
        print(f'Alphanumeric key {key.char} pressed')
    except AttributeError:
        # Special key
        print(f'Special key {key} pressed')

    # Stop on escape
    if key == keyboard.Key.esc:
        return False

def on_release(key, injected=False):
    """Handle key release events."""
    print(f'Key {key} released')

# Context manager usage (recommended)
with keyboard.Listener(
    on_press=on_press,
    on_release=on_release
) as listener:
    listener.join()

# Manual control
listener = keyboard.Listener(
    on_press=on_press,
    on_release=on_release
)
listener.start()
listener.join()

# Non-blocking usage
listener = keyboard.Listener(on_press=on_press)
listener.start()
# ... do other work
listener.stop()

Keyboard Events Iterator

The Events class provides synchronous iteration over keyboard events.

class Events:
    """A keyboard event listener supporting synchronous iteration over events."""

    def __init__(self):
        """Initialize the events iterator."""
        ...

    def __enter__(self):
        """Start the event listener."""
        ...

    def __exit__(self, *args):
        """Stop the event listener."""
        ...

    def __iter__(self):
        """Return iterator interface."""
        ...

    def __next__(self):
        """Get the next event."""
        ...

    def get(self, timeout: float = None):
        """
        Get the next event with optional timeout.

        Args:
            timeout (float): Maximum time to wait for an event

        Returns:
            Event or None: The next event, or None if timeout or stopped
        """
        ...

    class Press:
        """A key press event."""
        def __init__(self, key: Key | KeyCode, injected: bool):
            self.key = key
            self.injected = injected

    class Release:
        """A key release event."""
        def __init__(self, key: Key | KeyCode, injected: bool):
            self.key = key
            self.injected = injected

Usage Examples

from pynput.keyboard import Events

# Process events synchronously
with Events() as events:
    for event in events:
        if isinstance(event, Events.Press):
            print(f'Key {event.key} pressed')
            if str(event.key) == "'q'":  # Exit on 'q'
                break
        elif isinstance(event, Events.Release):
            print(f'Key {event.key} released')

HotKey Support

The HotKey class enables detection of key combinations, while GlobalHotKeys provides a convenient listener for multiple hotkeys.

class HotKey:
    """A combination of keys acting as a hotkey."""

    def __init__(self, keys: set, on_activate: callable):
        """
        Initialize a hotkey.

        Args:
            keys (set): Set of keys that must be pressed simultaneously
            on_activate (callable): Function to call when hotkey is activated
        """
        ...

    @staticmethod
    def parse(keys: str) -> list:
        """
        Parse a key combination string.

        Args:
            keys (str): Key combination string (e.g., '<ctrl>+<alt>+h', '<cmd>+c')

        Returns:
            list: List of key objects

        Raises:
            ValueError: If the key string is invalid
        """
        ...

    def press(self, key: Key | KeyCode):
        """
        Update hotkey state for a pressed key.

        Args:
            key: The key being pressed
        """
        ...

    def release(self, key: Key | KeyCode):
        """
        Update hotkey state for a released key.

        Args:
            key: The key being released
        """
        ...

class GlobalHotKeys(Listener):
    """A keyboard listener supporting multiple global hotkeys."""

    def __init__(self, hotkeys: dict, *args, **kwargs):
        """
        Initialize global hotkeys listener.

        Args:
            hotkeys (dict): Mapping from hotkey strings to callback functions
            *args, **kwargs: Arguments passed to base Listener class

        Raises:
            ValueError: If any hotkey description is invalid
        """
        ...

Usage Examples

from pynput.keyboard import HotKey, GlobalHotKeys, Key

# Single hotkey
def on_hotkey():
    print('Hotkey activated!')

hotkey = HotKey({Key.ctrl, Key.alt, KeyCode.from_char('h')}, on_hotkey)

def on_press(key):
    hotkey.press(key)

def on_release(key):
    hotkey.release(key)

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

# Multiple global hotkeys
def copy_handler():
    print('Copy hotkey pressed')

def paste_handler():
    print('Paste hotkey pressed')

def quit_handler():
    print('Quit hotkey pressed')
    return False  # Stop listener

hotkeys = {
    '<ctrl>+c': copy_handler,
    '<ctrl>+v': paste_handler,
    '<ctrl>+<alt>+q': quit_handler
}

with GlobalHotKeys(hotkeys) as listener:
    listener.join()

# Parse hotkey strings
try:
    keys = HotKey.parse('<ctrl>+<shift>+a')
    print(f'Parsed keys: {keys}')
except ValueError as e:
    print(f'Invalid hotkey string: {e}')

Types

KeyCode Class

class KeyCode:
    """Represents a key code used by the operating system."""

    def __init__(self, vk: int = None, char: str = None, is_dead: bool = False):
        """
        Initialize a KeyCode.

        Args:
            vk (int): Virtual key code
            char (str): Character representation
            is_dead (bool): Whether this is a dead key
        """
        self.vk = vk
        self.char = char
        self.is_dead = is_dead
        self.combining = None  # Set for dead keys

    @classmethod
    def from_vk(cls, vk: int, **kwargs) -> 'KeyCode':
        """
        Create a KeyCode from virtual key code.

        Args:
            vk (int): Virtual key code
            **kwargs: Additional parameters

        Returns:
            KeyCode: New KeyCode instance
        """
        ...

    @classmethod
    def from_char(cls, char: str, **kwargs) -> 'KeyCode':
        """
        Create a KeyCode from character.

        Args:
            char (str): Single character
            **kwargs: Additional parameters

        Returns:
            KeyCode: New KeyCode instance
        """
        ...

    @classmethod
    def from_dead(cls, char: str, **kwargs) -> 'KeyCode':
        """
        Create a dead key KeyCode.

        Args:
            char (str): Dead key character
            **kwargs: Additional parameters

        Returns:
            KeyCode: New dead key KeyCode instance
        """
        ...

    def join(self, key: 'KeyCode') -> 'KeyCode':
        """
        Apply this dead key to another key.

        Args:
            key (KeyCode): Key to join with this dead key

        Returns:
            KeyCode: Combined key result

        Raises:
            ValueError: If keys cannot be joined
        """
        ...

Key Enumeration

class Key(enum.Enum):
    """Special keys that don't correspond to printable characters."""

    # Modifier keys
    alt          # Generic Alt key
    alt_l        # Left Alt key
    alt_r        # Right Alt key
    alt_gr       # AltGr key
    ctrl         # Generic Ctrl key
    ctrl_l       # Left Ctrl key
    ctrl_r       # Right Ctrl key
    shift        # Generic Shift key
    shift_l      # Left Shift key
    shift_r      # Right Shift key
    cmd          # Generic Command/Windows key
    cmd_l        # Left Command/Windows key
    cmd_r        # Right Command/Windows key

    # Navigation keys
    up           # Up arrow
    down         # Down arrow
    left         # Left arrow
    right        # Right arrow
    home         # Home key
    end          # End key
    page_up      # Page Up
    page_down    # Page Down

    # Special keys
    space        # Spacebar
    tab          # Tab key
    enter        # Enter/Return key
    esc          # Escape key
    backspace    # Backspace key
    delete       # Delete key

    # Lock keys
    caps_lock    # Caps Lock
    num_lock     # Num Lock
    scroll_lock  # Scroll Lock

    # Function keys (F1-F20)
    f1
    f2
    f3
    f4
    f5
    f6
    f7
    f8
    f9
    f10
    f11
    f12
    f13
    f14
    f15
    f16
    f17
    f18
    f19
    f20

    # Media keys
    media_play_pause     # Play/Pause toggle
    media_volume_mute    # Volume mute
    media_volume_up      # Volume up
    media_volume_down    # Volume down
    media_previous       # Previous track
    media_next           # Next track

    # System keys (may be undefined on some platforms)
    insert         # Insert key
    menu           # Menu/Application key
    pause          # Pause/Break key
    print_screen   # Print Screen key

Platform-Specific Features

Windows (win32)

# Custom event filtering
def win32_event_filter(msg, data):
    """Filter Windows keyboard events."""
    # Access to KBDLLHOOKSTRUCT data
    # Return False to suppress event from reaching listener
    return True

listener = keyboard.Listener(
    on_press=on_press,
    win32_event_filter=win32_event_filter
)

macOS (darwin)

# Event intercepting and modification
def darwin_intercept(event_type, event):
    """Intercept and modify macOS keyboard events."""
    # Modify event using Quartz.CGEvent functions
    # Return None to suppress event system-wide
    return event

listener = keyboard.Listener(
    on_press=on_press,
    darwin_intercept=darwin_intercept
)

Linux (xorg)

Platform-specific options are available for X11/Xorg systems. The system requires access to the X server and proper DISPLAY environment variable configuration.

Advanced Features

Dead Key Support

Dead keys allow composition of accented characters:

from pynput.keyboard import KeyCode

# Create dead key
dead_tilde = KeyCode.from_dead('~')

# Join with regular key
a_key = KeyCode.from_char('a')
result = dead_tilde.join(a_key)  # Creates 'ã'

# Join with space or same key to get literal character
space_key = KeyCode.from_char(' ')
literal = dead_tilde.join(space_key)  # Creates '~'

Control Code Mapping

The keyboard module automatically maps control characters:

# These are equivalent when typing
keyboard.type('\n')      # Maps to Key.enter
keyboard.type('\t')      # Maps to Key.tab
keyboard.type('\r')      # Maps to Key.enter

Error Handling

Common Issues

  • ImportError: Missing platform dependencies

    • Windows: Requires win32 extensions
    • macOS: Requires PyObjC framework bindings
    • Linux: Requires python-xlib and X11 libraries
  • InvalidKeyException: Invalid key parameters passed to controller methods

  • InvalidCharacterException: Untypable characters in type() method

  • Permission Issues: Some operations may require elevated privileges

Exception Handling Examples

from pynput.keyboard import Controller, Key

keyboard = Controller()

try:
    keyboard.press('a')
    keyboard.release('a')
except Controller.InvalidKeyException as e:
    print(f"Invalid key: {e}")

try:
    keyboard.type('Hello 🌍')  # Emoji might not be typable
except Controller.InvalidCharacterException as e:
    print(f"Cannot type character at position {e.args[0]}: {e.args[1]}")

# Hotkey parsing errors
try:
    from pynput.keyboard import HotKey
    keys = HotKey.parse('<invalid>+<key>')
except ValueError as e:
    print(f"Invalid hotkey string: {e}")

Listener Exception Handling

from pynput import keyboard

def on_press(key):
    # Raise StopException to gracefully stop listener
    if key == keyboard.Key.esc:
        raise keyboard.Listener.StopException()

def on_error():
    print("Listener encountered an error")

try:
    with keyboard.Listener(
        on_press=on_press,
        on_release=lambda key: None
    ) as listener:
        listener.join()
except keyboard.Listener.StopException:
    print("Listener stopped by user")
except Exception as e:
    print(f"Listener error: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-pynput

docs

index.md

keyboard.md

mouse.md

tile.json