disnake
disnake copied to clipboard
feat: auto-generate overloads for `Client.wait_for`
Summary
Part 1/? of event QoL work, adds just a few overloads (106, to be exact) to Client.wait_for, one for each event.
This allows for correct typing of the check parameter and the return type, particularly when used with the Event enum (see below).
These are being auto-generated through yet another custom libcst module. While certainly not ideal, there isn't a better way to do this with Python's current typing system due to several reasons.
We may want to consider merging overloads with the same return types, I'm not sure if that affects pyright performance in any significant way. That one big lookup table is a separate module as part of the library, since it'll be reused and expanded in future PRs with runtime usage.
Some other things:
Fallback for str
One of the overloads takes an unspecific str event. This is meant to be the fallback overload for anyone using the existing event infrastructure in the lib for custom events, since doing that would otherwise run into typing issues:
https://github.com/DisnakeDev/disnake/blob/a52790d008139633723d128c6de9a5d71c367ba4/disnake/client.py#L2742-L2751
This, however, means that incorrectly typing a library event handler also falls back to this, since it's the most generic one - i.e. passing a wrong check type for .wait_for("message") will not show any errors. This is not the case with Event.message, which will correctly show a typing error if necessary.
But what about the other event methods?
No :) Well, maybe.
I partially implemented this, but decided to remove it again; it may or may not affect the performance of pyright(/mypy), and the library typing is already complex enough.
Reason for choosing wait_for in particular is that it's usually used together with check=lambda x, y, z: ..., and you can't add type annotations to inline lambdas.
Somewhat depends on #1016 due to tests.
Checklist
- [x] If code changes were made, then they have been tested
- [ ] I have updated the documentation to reflect the changes
- [x] I have formatted the code properly by running
pdm lint - [x] I have type-checked the code by running
pdm pyright
- [ ] This PR fixes an issue
- [x] This PR adds something new (e.g. new method or parameters)
- [ ] This PR is a breaking change (e.g. methods or parameters removed/renamed)
- [ ] This PR is not a code change (e.g. documentation, README, ...)
@shiftinv would you please solve conflicts?
@shiftinv would you please solve conflicts?
done, rebased away the commits from the base PR and updated to master :+1:
I hate the concept of putting a ton of overloads in the main client.py file. It makes it way more bloated than it needs to be. Can't wait_for and its overloads be put in a mixin in a separate file?
I hate the concept of putting a ton of overloads in the main
client.pyfile. It makes it way more bloated than it needs to be. Can'twait_forand its overloads be put in a mixin in a separate file?
It's not ideal either way, yeah. This still seems to be the ~~least awful~~ best solution, given we unfortunately can't just have a wait_for<T extends keyof typeof Event>(event: T, check: (...args: EventData[T]) => boolean) or something else that would avoid all these overloads in the first place :/
I'd like to avoid mixins as much as possible due to them being mostly incompatible with type checking, moving wait_for into a separate mixin/file would require additional # type: ignores.
I'd like to avoid mixins as much as possible due to them being mostly incompatible with type checking, moving
wait_forinto a separate mixin/file would require additional# type: ignores.
How come?
I hate the concept of putting a ton of overloads in the main
client.pyfile. It makes it way more bloated than it needs to be. Can'twait_forand its overloads be put in a mixin in a separate file?
Instead of mixins maybe .pyi files could be used to achieve this? I don't know much about those though
I hate the concept of putting a ton of overloads in the main
client.pyfile. It makes it way more bloated than it needs to be. Can'twait_forand its overloads be put in a mixin in a separate file?Instead of mixins maybe
.pyifiles could be used to achieve this? I don't know much about those though
Not an expert on the topic either, but i heard that .pyi files are preferred over source code by type checkers, which means if we're going to supply a .pyi file we'll have to type hint the entirety of client.py there (since for TCs can't consume both stubs and source hints), which sounds like another PR to drop source hints altogether.
(mypy reference: https://mypy.readthedocs.io/en/stable/stubs.html#creating-a-stub - "If a directory contains both a .py and a .pyi file for the same module, the .pyi file takes precedence.")
EDIT: Forget what i said, it seems like in theory there is a way to make "partial" stubs: https://peps.python.org/pep-0561/#partial-stub-packages
which means if we're going to supply a .pyi file we'll have to type hint the entirety of client.py there
Yea, not something we want to do. Creating "partial" stubs like you mentioned would be cool, but that mechanism only applies at the file/module level and not the class/func level, which would be needed here.