-
Notifications
You must be signed in to change notification settings - Fork 24
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
Add plum.overload
#93
Conversation
Pull Request Test Coverage Report for Build 5912664992
💛 - Coveralls |
I have two suggestions:
For the first one, the pyproject.toml would have something like this:
(Note that I've done this kind of thing with setup.cfg, but I'm not 100% sure this is the correct syntax for pyproject.toml.) If you do this, then the Next, to make the import sys
if sys.version_info >= (3, 11):
from typing import get_overloads, overload
else:
from typing_extensions import get_overloads, overload With this change, Python 3.11+ won't require One more thing that would be helpful for pyright users (like me and my team) is to add pyright tests in addition to mypy. |
plum/overload.py
Outdated
T = TypeVar("T", bound=Callable) | ||
|
||
|
||
def dispatch(f: T) -> T: |
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.
def dispatch(f: T) -> T: | |
def dispatch(f: Callable) -> Function: |
Maybe mypy didn't catch this? I'm unsure
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.
Although this is technically correct, I believe this might not have the intended effect. Specifically, I tried changing the type stub to the following:
from typing import Callable, TypeVar, overload
from plum import Function
__all__ = ["overload", "dispatch"]
T = TypeVar("T", bound=Callable)
def dispatch(f: Callable) -> Function: ...
Then mypy
is happy with the following:
from plum.overload import dispatch, overload
@overload
def f(x: int) -> int:
return x
@overload
def f(x: str) -> str:
return x
@dispatch
def f(x):
pass
f(1)
f("1")
f(1.0) # Should complain about this!!
Note that this is wrong, because it should complain about f(1.0)
.
I believe that's what happening is the following. If you say that dispatch
takes in Callable
and gives back a Function
, then f(1.0)
will be matched against Function.__call__
. Function.__call__
takes in any arguments, so f(1.0)
checks out.
However, if you say that dispatch
takes in T
and gives back T
, then mypy
knows that what f
decorates by dispatch
must correspond to the implementation of f
in the file. The @overload
s in the file correspond to the implementation of f
in the file, so mypy
knows that f(1.0)
is wrong and then correctly complains.
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.
Hold on, I screwed up the test on my part! mypy
actually does correctly complain about f(1.0)
with the corrected type stub. I'm now a bit confused, because I don't exactly understand what's going on.
EDIT: I've committed the suggestion, because I think you're right, but I'm still not quite sure what's going on.
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.
I've now added tests for both mypy
and pyright
. It seems that pyright
does indeed require the T -> T
construction. If you'd like to experiment with it, you can run the linter assertion tests with
python check_linter_assertions.py tests/typechecked
from the repo root.
Co-authored-by: Gabriel de Marmiesse <gabrieldemarmiesse@gmail.com>
Co-authored-by: Winston Chang <winston@stdout.org>
docs/integration.md
Outdated
In the above, `plum.overload.overload` is `typing_extensions.overload`. | ||
For this pattern to work in all Python versions, you must use `typing_extensions.overload`, not `typing.overload`. |
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.
I don't think the first line is true based on the try-except in the code.
The second sentence is not really helpful since everything is imported from plum anyway, I'd rather avoir using it unless there is something actionnable on the user side (but I don't see what this is)
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.
You're right! First line is corrected now.
With the second line I intended to explain that things might break if you try to from typing import overload
. I've added a brief second sentence to elaborate.
plum/overload.py
Outdated
|
||
__all__ = ["overload", "dispatch"] | ||
|
||
T = TypeVar("T", bound=Callable[..., 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.
This is unused now.
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.
I've added this back in, because it seems that pyright
does require the T -> T
construction. :( Please see the other conversation.
@wch, I've integrated |
Unless there are any objections, I think that I'll be merging this. :) Let's address further improvements in separate PRs! Thanks @gabrieldemarmiesse and @wch for your suggested improvements and feedback!! |
LGTM! |
I've played a bit around with this, and judging from my personal use case, where dispatch is used to multiple expand a base library, I can say that it's not easy to ensure that the function defined with Also, we had added a while back Can't we make |
@PhilipVinc That's an interesting idea! I've hacked around a bit to see if I could come up with a I'm almost thinking that some kind of rewriting of the source before running |
@gabrieldemarmiesse, @machow, and @wch, I've taken a stab at adding
plum.overload
as described in #89. With this PR, the following would be functional andmypy
-compliant:How would something like this look?