-
Notifications
You must be signed in to change notification settings - Fork 769
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
asyncio.gather
bad type information (pyright)
#1913
Comments
This is a tradeoff chosen by the typeshed project that pyright uses for type information. The source code of the type hints has this comment: # `gather()` actually returns a list with length equal to the number
# of tasks passed; however, Tuple is used similar to the annotation for
# zip() because typing does not support variadic type variables. See
# typing PR #1550 for discussion. The type system is currently not expressive enough to say: "This returns a list with three elements of the following types: ..." As a workaround, foo, bar = gather([do_foo(), do_bar()]) # do_foo() returns int, do_bar() returns str See python/typeshed#1550 and python/typing#193 for discussion. |
To the point response, many thanks! But then https://mypy.readthedocs.io/en/stable/stubs.html claims "Mypy uses stub files stored in the typeshed repository" - and mypy does not have this challenge (see my screenshot, above). So, from a feature point of view, I wonder how mypy can get this right? |
I can't verify this at the moment, and possibly the pyright maintainers have more insight here, but there is actually one overload for gather, where it's returning a list: when the number of arguments is too high or unknown. In this particular case, I assume that mypy picks that overload, while pyright picks a different one. |
Given the return type says |
This is not the behavior. If there is no matching overload, we report that there is no matching overload. |
I'll investigate the issue later today. I'm busy at the moment. |
My mistake, I didn't see that there wasn't that error above and mixed it up with the case where no overload matches (mypy in this case will return |
Here's what's happening. In this case, the overload is ambiguous. The call expression is using an unpack operator ( Consider the following: @overload
def func(a: int) -> Literal[1]: ...
@overload
def func(a: int, b: int) -> Literal[2]: ...
@overload
def func(a: int, b: int, c: int) -> Literal[3]: ...
def test(val: list[int]):
ret = func(*val)
reveal_type(ret) # ???? What would you expect the type of Both pyright and mypy choose the first overload in the list, and both determine the answer to be However, if we add the following overload, mypy and pyright differ in their behavior: @overload
def func(a: int, b: int, c: int, *args: int) -> Literal[4]:
... When we add this overload to the end, mypy now evaluates a type of PEP 484 doesn't provide much guidance in defining how overloads are supposed to work, especially in ambiguous cases like this, so it's not surprising that different type checkers will make different choices when subtle edge cases are involved. I would normally say that pyright should simply adopt the same behavior as mypy in a case like this, but there will likely be some (perhaps significant) performance impact of doing so. That gives me pause. Pyright's current behavior short-circuits a bunch of extra work if it discovers an overload match. This is a very common case during type evaluation. It appears that mypy always evaluates all overloads even if an earlier overload is a legitimate match. |
Issue Type: Bug
asyncio.gather
is seen as returningTuple[]
, but in reality returnsList[]
. This results in errors on type-checking.To Reproduce
Open the script below in Visual Studio Code, with Pylance extension installed, and with setting
"python.analysis.typeCheckingMode": "basic"
(or more aggressive).Expected
Pylance (pyright) does flag errors on
main1()
.Pylance (pyright) does not flag any errors on
main2()
.Actual
Pylance (pyright) does not flag errors on
main1()
.Pylance (pyright) does flag errors on
main2()
.Basically, the type-checker sees
asyncio.gather()
returnTuple[]
where-as, in fact, it returnsList[]
. The code further down demonstrates thatasyncio.gather()
indeed returnsList[]
Compare the results obtained also against
mypy
, which works correctly:Alas, I cannot tell at which level things are wrong; my understanding is that pyright/pylance and mypy share the same type information, so I am surprised to see this behaviour.
This is with a virtual environment on Windows, Python 3.9.7 from the Windows Store.
Extension version: 2021.10.0
VS Code version: Code 1.60.2 (7f6ab5485bbc008386c4386d08766667e155244e, 2021-09-22T12:00:31.514Z)
OS version: Windows_NT x64 10.0.18363
Restricted Mode: No
System Info
gpu_compositing: enabled
multiple_raster_threads: enabled_on
oop_rasterization: enabled
opengl: enabled_on
rasterization: enabled
skia_renderer: enabled_on
video_decode: enabled
vulkan: disabled_off
webgl: enabled
webgl2: enabled
A/B Experiments
The text was updated successfully, but these errors were encountered: