-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: auto-generate overloads for Client.wait_for
#1017
base: master
Are you sure you want to change the base?
Conversation
This is not ideal, since it bypasses the overloads if there's a typing issue in your wait_for call when using a str event. When using `Event.*`, the `check` and return types are still enforced as they should be.
8d27cad
to
ee43582
Compare
@shiftinv would you please solve conflicts? |
b3b0cef
to
cf276c7
Compare
done, rebased away the commits from the base PR and updated to master 👍 |
a171fab
to
61f85fb
Compare
I hate the concept of putting a ton of overloads in the main |
It's not ideal either way, yeah. This still seems to be the |
How come? |
Instead of mixins maybe |
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 |
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. |
…ait-for-overloads
…ait-for-overloads
# n.b. this one isn't ideal, but there's no way to prevent type-checkers from using the fallback in this case | ||
_ = assert_type(client.wait_for("guild_join", check=lambda: True), Coroutine[Any, Any, Any]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, an overload like
@overload
async def wait_for(
self,
event: Literal["guild_join", "ready", ...] | Event,
*,
check: Callable[..., Any],
timeout: Optional[float] = None,
) -> Never: ...
Could work, but the Literal
would be huge, and it's non-obvious what that signature means/why it got matched
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, interesting idea
And yeah, communicating this clearly to the user is tough. I could see InvalidCheckType = Never
(or w/e) maybe being an option, which would look something like this, but it's not pretty:
fwiw, I think ultimately the approach in this PR is somewhat flawed, but it's the best we can do for now, sadly. In an ideal world (3.0?), every event would be a dataclass/namedtuple, and event names + overloads would be a thing of the past.
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 theEvent
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:disnake/disnake/client.py
Lines 2742 to 2751 in a52790d
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 withEvent.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 withcheck=lambda x, y, z: ...
, and you can't add type annotations to inline lambdas.Somewhat depends on #1016 due to tests.
Checklist
pdm lint
pdm pyright