Skip to content

Fix annotations for pynput event callbacks#15857

Merged
srittau merged 6 commits into
python:mainfrom
huntfx:main
Jun 2, 2026
Merged

Fix annotations for pynput event callbacks#15857
srittau merged 6 commits into
python:mainfrom
huntfx:main

Conversation

@huntfx
Copy link
Copy Markdown
Contributor

@huntfx huntfx commented Jun 2, 2026

The event callbacks are designed to accept any number of arguments up to the maximum amount. Also, currently, the injected argument is omitted from all the pyi files, so it'll flag up with an error if you use it.

For example, on_click(), on_click(x, y) and on_click(x, y, button, pressed, injected) are all perfectly valid callbacks.

I've done a union with all possible signatures for the event callbacks, it's a little more verbose, but I'm not aware of another way to handle this.

For reference, here are the full signatures for each callback:

  • on_move(x: int, y: int, injected: bool)
  • on_click(x: int, y: int, button: Button, pressed: bool, injected: bool)
  • on_scroll(x: int, y: int, dx: int, dy: int, injected: bool)
  • on_press(key: Key | KeyCode | None, injected: bool)
  • on_release(key: Key | KeyCode | None, injected: bool)

The test passes when running python tests/regr_test.py pynput. Fixes #15855.

huntfx and others added 2 commits June 2, 2026 12:45
They are designed to accept any number of arguments up to the maximum amount, so `on_click()` and `on_click(x, y)` are both perfectly valid callbacks.

Here are the full signatures for each callback:
- `on_move(x: int, y: int, injected: bool)`
- `on_click(x: int, y: int, button: Button, pressed: bool, injected: bool)`
- `on_scroll(x: int, y: int, dx: int, dy: int, injected: bool)`
- `on_press(key: Key | KeyCode | None, injected: bool)`
- `on_release(key: Key | KeyCode | None, injected: bool)`

Test passes with `python tests/regr_test.py pynput`
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Collaborator

@srittau srittau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a full review, just two things I noticed:

  • We only need/want tests for particular tricky typing situations (for various reasons). In this case, the annotations are a bit unusual, but not particularly tricky, so the tests are not needed.
  • I know this was a pre-existing problem, but callbacks returning None is usually not correct. If the return value of the callback is ignored, we use object instead.

(Also I respect the voodoo magic going on that supports callbacks with varying amounts of arguments.)

@huntfx
Copy link
Copy Markdown
Contributor Author

huntfx commented Jun 2, 2026

Alright cheers, I'll double check the return type and commit those changes then

Only needed for complex cases, which this is not.
@github-actions

This comment has been minimized.

@huntfx
Copy link
Copy Markdown
Contributor Author

huntfx commented Jun 2, 2026

Awkward one with the return value actually. If it's specifically False then it'll raise a StopIteration, but anything else and it works fine.

Not actually sure if that's still best left to object, or something more specific in this case?

https://github.com/moses-palmer/pynput/blob/afc64577b069a51a91474a235f0efbb0e7e0357d/lib/pynput/_util/__init__.py

        def wrapper(f):
            def inner(*args):
                if f(*args) is False:
                    raise self.StopException()

            return inner

@srittau
Copy link
Copy Markdown
Collaborator

srittau commented Jun 2, 2026

Interesting. Maybe using bool | None is best in this case? While technically returning anything else would work (which is why we usually use object), in this case I feel that disallowing arbitrary return types would be best.

huntfx and others added 2 commits June 2, 2026 14:22
Technically they accept any return type, but as the only effect is returning False stops the iteration, then it makes sense to limit it to bool or None.
@huntfx
Copy link
Copy Markdown
Contributor Author

huntfx commented Jun 2, 2026

Yeah that makes sense I reckon - I also realised there was a mismatch with the keyboard ones so I've just updated them to match.

Copy link
Copy Markdown
Collaborator

@srittau srittau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@srittau srittau merged commit d928f9e into python:main Jun 2, 2026
43 of 44 checks passed
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

Successfully merging this pull request may close these issues.

Pynput on_move missing complete signature

2 participants