moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.82k stars 251 forks source link

Mouse listener crashes in Windows 11 (NotImplementedError) #614

Open n-mou opened 1 month ago

n-mou commented 1 month ago

Description

Installed pynput in a new virtual environment and tried to track mouse and keyboard events. Keyboard presses were handled successfully but when a mouse event was triggered the program crashed with a NotImplementedError. I created a new main.py without the rest of my app's overhead and the same error happened. Searched for help in StackOverflow and got a snippet to track both mouse and keyboard events (to check if it was my code's fault) and the same error happened. Finally I moved out of the virtual environment in a clean Python install, added pynput through pip and run the snippet. The same error happened.

Platform and pynput version

To Reproduce

from pynput.keyboard import Listener  as KeyboardListener
from pynput.mouse    import Listener  as MouseListener
from pynput.keyboard import Key
import logging
import sys

logging.basicConfig(filename=("log.txt"), level=logging.DEBUG, format='%(asctime)s: %(message)s')

def end_rec(key):
    logging.info(str(key))

def on_press(key):
    if key == Key.esc:
        sys.exit()
    logging.info(str(key))

def on_move(x, y):
    logging.info("Mouse moved to ({0}, {1})".format(x, y))

def on_click(x, y, button, pressed):
    if pressed:
        logging.info('Mouse clicked at ({0}, {1}) with {2}'.format(x, y, button))

def on_scroll(x, y, dx, dy):
    logging.info('Mouse scrolled at ({0}, {1})({2}, {3})'.format(x, y, dx, dy))

with MouseListener(on_click=on_click, on_scroll=on_scroll) as listener:
    with KeyboardListener(on_press=on_press) as listener:
        listener.join()
2024-10-19 14:52:39,314: Key.up
2024-10-19 14:52:39,598: Key.down
2024-10-19 14:52:39,906: Key.left
2024-10-19 14:52:40,171: Key.right
2024-10-19 14:52:41,065: Unhandled exception in listener callback
Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pynput\_util\win32.py", line 386, in _handler
    converted = self._convert(code, msg, lpdata)
  File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pynput\_util\win32.py", line 401, in _convert
    raise NotImplementedError()
NotImplementedError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pynput\_util\__init__.py", line 229, in inner
    return f(self, *args, **kwargs)
  File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pynput\_util\win32.py", line 390, in _handler
    self._handle(code, msg, lpdata)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
TypeError: '_thread._ThreadHandle' object is not callable
zxt12138 commented 2 weeks ago

me too and I want known how old the version it is to avoid this issue

moses-palmer commented 2 weeks ago

Thank you for your report.

I cannot reproduce this error, but the stack trace seems to indicate that the problem may be internal changes to the Python threading library in Python 3.13 on Windows; self._handle is a method of the listener class, but it seems to have been overwritten by the threading library once the thread is started.

In retrospect, letting the listener classes be subclasses of threading.Thread was a poor choice, but one that probably cannot be undone until the next major version of this library. In the mean time, I have prepared a branch with a possible fix: fixup/win32-mouse-listener. Do you have any change of testing it out?

therealadityashankar commented 1 week ago

This doesn't work in macos either, this seems to be related to python 3.13 rather than windows

therealadityashankar commented 1 week ago

I tested fixup/win32-mouse-listener and that does not fix this in macos

moses-palmer commented 1 week ago

@therealadityashankar, the feature branch only contains fixes for win32.

I have prepared a new feature branch, fixup/listener-thread-handle that addresses this issue for all platforms. If you find the time, please try it!

n-mou commented 5 days ago

Thanks for your attention. I'll try out this branch once I've figured our how to install it in a local environment (any help in that aspect woul de really appreciated).

n-mou commented 5 days ago

Ok, I've managed to install it by running uv pip install "pynput @ ." at the repo main directory with the specified branch and it works (tested on python 3.13 in a virtual environment created by uv in the same conditions as those posted on the 1st comment on this issue):

Output:

2024-11-21 20:09:52,204: Mouse clicked at (4037, 1065) with Button.left
2024-11-21 20:09:52,732: Mouse clicked at (3268, 971) with Button.left
2024-11-21 20:09:53,228: Mouse clicked at (2810, 476) with Button.left
2024-11-21 20:09:54,231: Key.down
2024-11-21 20:09:54,376: Key.right
2024-11-21 20:09:54,510: Key.up
2024-11-21 20:09:54,662: Key.left
2024-11-21 20:09:54,744: Key.down
2024-11-21 20:09:55,942: Mouse clicked at (4636, 1143) with Button.left
2024-11-21 20:09:56,614: Key.ctrl_l
2024-11-21 20:09:56,798: '\x03'
2024-11-21 20:09:57,900: Mouse clicked at (4557, 1084) with Button.left
2024-11-21 20:09:58,686: Mouse clicked at (4545, 1103) with Button.left
2024-11-21 20:09:59,654: Key.ctrl_l
2024-11-21 20:10:00,014: '\x03'
2024-11-21 20:10:03,404: Mouse clicked at (4474, 1081) with Button.left
2024-11-21 20:10:03,620: Mouse clicked at (4475, 1069) with Button.left
2024-11-21 20:10:03,804: Mouse clicked at (4475, 1069) with Button.left
2024-11-21 20:10:04,388: Mouse clicked at (4410, 995) with Button.left
2024-11-21 20:10:05,726: Key.ctrl_l
2024-11-21 20:10:05,926: '\x03'
2024-11-21 20:10:06,640: 'q'
2024-11-21 20:10:06,798: 'q'
2024-11-21 20:14:54,310: Key.shift_r
2024-11-21 20:14:54,431: 'T'
2024-11-21 20:14:54,583: 'h'
2024-11-21 20:14:54,702: 'i'
2024-11-21 20:14:54,798: 's'
2024-11-21 20:14:54,950: Key.space
2024-11-21 20:14:55,094: 'i'
2024-11-21 20:14:55,214: 's'
2024-11-21 20:14:55,358: Key.space
2024-11-21 20:14:55,566: 'a'
2024-11-21 20:14:55,678: Key.space
2024-11-21 20:14:55,815: 't'
2024-11-21 20:14:55,950: 'e'
2024-11-21 20:14:56,135: 's'
2024-11-21 20:14:56,279: 't'
2024-11-21 20:14:56,895: Key.left
2024-11-21 20:14:56,982: Key.up
2024-11-21 20:14:57,134: Key.right
2024-11-21 20:14:57,238: Key.down
2024-11-21 20:14:57,326: Key.left
2024-11-21 20:14:57,446: Key.up
2024-11-21 20:14:57,534: Key.right
2024-11-21 20:14:57,640: Key.left
2024-11-21 20:14:57,879: Key.up
2024-11-21 20:14:57,881: Key.right
2024-11-21 20:14:57,882: Key.down
2024-11-21 20:14:58,024: Key.left
2024-11-21 20:14:58,494: Key.shift_r
2024-11-21 20:14:58,670: Key.shift_r
2024-11-21 20:14:58,815: Key.shift_r
2024-11-21 20:14:59,646: Key.shift_r
2024-11-21 20:14:59,766: '!'
2024-11-21 20:14:59,848: '"'
2024-11-21 20:14:59,950: '!'
2024-11-21 20:15:00,046: '"'
2024-11-21 20:15:00,270: Key.shift_r
2024-11-21 20:15:00,398: Key.shift_r
2024-11-21 20:15:01,290: Mouse clicked at (4415, 563) with Button.left
2024-11-21 20:15:01,674: Mouse clicked at (3846, 298) with Button.left
2024-11-21 20:15:02,170: Mouse clicked at (2901, 298) with Button.left
2024-11-21 20:15:02,634: Mouse clicked at (2778, 494) with Button.left
2024-11-21 20:15:03,162: Mouse clicked at (4349, 291) with Button.left
2024-11-21 20:15:05,238: Key.ctrl_l
2024-11-21 20:15:05,479: '\x03'
2024-11-21 20:15:06,222: 'q'
2024-11-21 20:15:07,819: Mouse clicked at (4391, 526) with Button.left
2024-11-21 20:15:08,078: 'q'
2024-11-21 20:15:10,072: Mouse clicked at (4317, 571) with Button.left
2024-11-21 20:15:10,390: 'q'
2024-11-21 20:20:32,350: Mouse clicked at (4531, 376) with Button.left
2024-11-21 20:20:32,645: Mouse clicked at (4531, 376) with Button.right
2024-11-21 20:20:34,238: Mouse clicked at (4580, 874) with Button.left
2024-11-21 20:20:34,988: Mouse clicked at (3348, 617) with Button.left
2024-11-21 20:20:35,268: Mouse clicked at (3338, 610) with Button.right
2024-11-21 20:20:35,774: Mouse clicked at (3287, 510) with Button.left
2024-11-21 20:20:36,316: Mouse clicked at (2974, 693) with Button.left
2024-11-21 20:20:36,918: Mouse clicked at (2988, 1081) with Button.left
2024-11-21 20:20:37,380: Mouse clicked at (4424, 983) with Button.left
2024-11-21 20:20:38,263: 's'
2024-11-21 20:20:38,303: 'd'
2024-11-21 20:20:38,438: 'w'
2024-11-21 20:20:38,440: 'p'
2024-11-21 20:20:38,454: 'e'
2024-11-21 20:20:38,545: 'f'
2024-11-21 20:20:38,568: 'j'
2024-11-21 20:20:38,638: 'w'
2024-11-21 20:20:38,655: 'e'
2024-11-21 20:20:38,743: 'f'
2024-11-21 20:20:38,768: 'j'
2024-11-21 20:20:38,769: 'o'
2024-11-21 20:20:38,854: 'w'
2024-11-21 20:20:38,855: 'p'
2024-11-21 20:20:38,878: 'e'
2024-11-21 20:20:38,880: 'o'
2024-11-21 20:20:38,935: 'f'
2024-11-21 20:20:38,959: 'j'
2024-11-21 20:20:39,050: 'e'
2024-11-21 20:20:39,070: 'r'
2024-11-21 20:20:39,072: 'p'
2024-11-21 20:20:39,087: 'i'
2024-11-21 20:20:39,142: 'h'
2024-11-21 20:20:39,206: 'w'
2024-11-21 20:20:39,294: 'g'
2024-11-21 20:20:39,336: 'w'
2024-11-21 20:20:39,336: 'p'
2024-11-21 20:20:39,422: 'r'
2024-11-21 20:20:39,473: 'f'
2024-11-21 20:20:39,473: 'j'
2024-11-21 20:20:40,582: Key.ctrl_l
2024-11-21 20:20:40,806: '\x03'
2024-11-21 20:20:41,702: Key.ctrl_l
2024-11-21 20:20:41,950: '\x03'
2024-11-21 20:20:42,454: 'q'
2024-11-21 20:20:42,606: 'q'
2024-11-21 20:21:11,764: Mouse clicked at (3046, 673) with Button.left
2024-11-21 20:21:12,020: Mouse clicked at (3046, 673) with Button.right
2024-11-21 20:21:12,564: Mouse clicked at (3055, 589) with Button.left
2024-11-21 20:21:13,100: Mouse clicked at (3014, 519) with Button.right
2024-11-21 20:21:13,804: Mouse clicked at (1811, 1218) with Button.left
2024-11-21 20:21:14,204: Mouse clicked at (1811, 1218) with Button.right
2024-11-21 20:21:14,932: Mouse clicked at (4093, 1025) with Button.left
2024-11-21 20:21:15,942: 'w'
2024-11-21 20:21:15,974: 'r'
2024-11-21 20:21:16,046: 'p'
2024-11-21 20:21:16,110: 'i'
2024-11-21 20:21:16,134: 'w'
2024-11-21 20:21:16,150: 'e'
2024-11-21 20:21:16,151: 'q'
2024-11-21 20:21:16,241: 'f'
2024-11-21 20:21:16,310: 'p'
2024-11-21 20:21:16,326: 'i'
2024-11-21 20:21:16,398: 'h'
2024-11-21 20:21:16,455: 'w'
2024-11-21 20:21:16,486: 'p'
2024-11-21 20:21:16,510: 'i'
2024-11-21 20:21:16,551: 'g'
2024-11-21 20:21:16,590: 'b'
2024-11-21 20:21:16,686: 'p'
2024-11-21 20:21:16,726: 'e'
2024-11-21 20:21:16,774: 'i'
2024-11-21 20:21:16,798: 'g'
2024-11-21 20:21:16,800: 'b'
2024-11-21 20:21:17,462: Key.up
2024-11-21 20:21:17,502: Key.left
2024-11-21 20:21:17,686: Key.right
2024-11-21 20:21:17,838: Key.down
2024-11-21 20:21:17,926: Key.left
2024-11-21 20:21:18,112: Key.up
2024-11-21 20:21:18,114: Key.right
2024-11-21 20:21:18,270: Key.left
2024-11-21 20:21:18,360: Key.down
2024-11-21 20:21:19,086: Key.shift_r
2024-11-21 20:21:19,246: '!'
2024-11-21 20:21:19,311: '"'
2024-11-21 20:21:19,366: '�'
2024-11-21 20:21:19,479: '"'
2024-11-21 20:21:19,480: '!'
2024-11-21 20:21:19,584: '�'
2024-11-21 20:21:19,702: '!'
2024-11-21 20:21:19,806: '�'
2024-11-21 20:21:21,879: Key.alt_l
2024-11-21 20:21:22,104: 'q'
2024-11-21 20:21:22,126: 'w'
2024-11-21 20:21:22,214: 'd'
2024-11-21 20:21:22,313: 'q'
2024-11-21 20:21:22,334: 'w'
2024-11-21 20:21:22,438: 'd'
2024-11-21 20:21:22,502: 'q'
2024-11-21 20:21:22,726: '2'
2024-11-21 20:21:22,729: '1'
2024-11-21 20:21:22,854: '3'
2024-11-21 20:21:22,943: '2'
2024-11-21 20:21:22,945: '1'
2024-11-21 20:21:23,047: '3'
2024-11-21 20:21:23,150: '1'
2024-11-21 20:21:23,190: '2'
2024-11-21 20:21:23,254: '3'
2024-11-21 20:21:24,254: Key.ctrl_l
2024-11-21 20:21:24,718: '\x03'
2024-11-21 20:21:25,647: '\x03'
2024-11-21 20:21:25,894: '\x03'

Also, I think this is a flaw with the snippet I've posted but when a keyboard exception is raised (pressing Ctrl+C) the program doesn't stop, but when I stop the program by pressing the Escape key, the unhandled exception is printed to stderr (as if the listener prevented the stderr output from updating and when calling sys.exit() the output stream suddenly was updated)

  File "C:\Users\User\Dev\pynput_test\pynput\app.py", line 30, in <module>
    listener.join()
    ~~~~~~~~~~~~~^^
  File "C:\Users\User\Dev\pynput_test\pynput\.venv\Lib\site-packages\pynput\_util\__init__.py", line 271, in join
    super(AbstractListener, self).join(timeout, *args)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "C:\Users\Master\AppData\Roaming\uv\python\cpython-3.13.0-windows-x86_64-none\Lib\threading.py", line 1092, in join
    self._handle.join(timeout)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^
KeyboardInterrupt

I've solved by surrounding the listener.join() with a try/catch block. But maybe there's a more idiomatic way of doing it. Also, is there a way to setup the keyboard listener to stop when a KeyboardInterrupt exception is raised so I don't need to code edge cases in the on_press method?

therealadityashankar commented 3 days ago

@moses-palmer I tested this out on macos, and it still does not work, sorry!

I used the following script to test this out

from pynput import keyboard

def on_press(key):
    try:
        print(f"Key pressed: {key.char}")
    except AttributeError:
        # Special keys (e.g., arrow keys, function keys)
        print(f"Special key pressed: {key}")

def on_release(key):
    print(f"Key released: {key}")
    if key == keyboard.Key.esc:
        # Stop listener when 'Esc' key is released
        print("Escape key released, exiting...")
        return False

# Start the listener
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    print("Key logger is running. Press 'Esc' to stop.")
    listener.join()

I get the following error upon a key press

Key logger is running. Press 'Esc' to stop.
Unhandled exception in listener callback
Traceback (most recent call last):
  File "/Users/riversnow/Desktop/job-automation/venv/lib/python3.13/site-packages/pynput/_util/__init__.py", line 229, in inner
    return f(self, *args, **kwargs)
  File "/Users/riversnow/Desktop/job-automation/venv/lib/python3.13/site-packages/pynput/_util/darwin.py", line 283, in _handler
    self._handle(proxy, event_type, event, refcon)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: '_thread._ThreadHandle' object is not callable
FujiwaraChoki commented 15 hours ago

I'm getting the same issue.

FujiwaraChoki commented 14 hours ago

I found the issue, simply downgrade Python to 3.11.6.

For UV:

uv venv --python 3.11.6

@therealadityashanka