microsoft / pylance-release

Documentation and issues for Pylance
Creative Commons Attribution 4.0 International
1.72k stars 766 forks source link

Derived method completions use incorrect return type annotations when differing by `async` modifier. #5542

Open DanielRosenwasser opened 8 months ago

DanielRosenwasser commented 8 months ago

Possibly related to #4831?

Environment data

Code Snippet

Minimal example:

from typing import Awaitable

class Base:
    def yadda(self) -> Awaitable[int]:
        ...

class Derived(Base):
    async def ya$

Repro Steps

Assume $ is the cursor. Request completions at the cursor. Commit to the completion entry for __aenter__.

Expected behavior

For minimal example:

class Base:
    def yadda(self) -> Awaitable[int]:
        ...

class Derived(Base):
    async def yadda(self) -> int:
        return super().yadda()

Actual behavior

Pylance annotates uses the type that works for the non-async base, but one that does not work for the async derived type. The derived method is marked as async, so the method shouldn't declare it returns an Awaitable, a Coroutine, or whatever.

class Base:
    def yadda(self) -> Awaitable[int]:
        ...

class Derived(Base):
    async def yadda(self) -> Awaitable[int]:
        return super().yadda()

Here's something closer to what I actually ran into:

from typing import AsyncContextManager

class Yadda(AsyncContextManager):
    async def _$

Expected

class Yadda(AsyncContextManager):
    async def __aenter__(self) -> Self:
        return await super().__aenter__()

Actual

class Yadda(AsyncContextManager):
    async def __aenter__(self) -> Coroutine[Any, Any, _T_co]:
        return await super().__aenter__()
DanielRosenwasser commented 8 months ago

(generator functions might not have this issue since I don't think they become generators unless they have a yield statement)

DanielRosenwasser commented 8 months ago

also, it's not entirely about whether the modifiers differ - in the second example I gave, __aenter__ is declared as async but has no explicit return type annotation.

debonte commented 8 months ago

@DanielRosenwasser, can you check your "minimal example" above? It is missing the $ and seems like it might be a copy of the "actual behavior" code.

DanielRosenwasser commented 8 months ago

My apologies - I started out with the bottom example, figured it should be more minimal, but got sloppy. Fixed now.

Also, here's the same example on the Pyright playground if you want an easy repro.