pyright: Failed to call method "__get__" for descriptor class "LRUAsyncCallable..."
The following code can run, but pyright will report errors. These errors are annoying, it would be great if they could be eliminated.
Thanks for this great library!
import asyncio
import asyncstdlib as astd
class Test:
@astd.cache
async def foo(self, n: int) -> int:
return n
async def bar(self) -> int:
return await self.foo(1)
if __name__ == "__main__":
a = Test()
with asyncio.Runner() as r:
print(r.run(a.bar()))
print(r.run(a.foo(2)))
$ python aaa.py
1
2
$ pyright aaa.py
/liuyifan/aaa.py
/liuyifan/aaa.py:11:27 - error: Cannot access member "foo" for type "Test*"
Failed to call method "__get__" for descriptor class "LRUAsyncCallable[(self: Self@Test, n: int) -> Coroutine[Any, Any, int]]" (reportAttributeAccessIssue)
/liuyifan/aaa.py:17:23 - error: Cannot access member "foo" for type "Test"
Failed to call method "__get__" for descriptor class "LRUAsyncCallable[(self: Test, n: int) -> Coroutine[Any, Any, int]]" (reportAttributeAccessIssue)
2 errors, 0 warnings, 0 information
$ python --version
Python 3.12.1
$ pyright --version
pyright 1.1.348
Thanks for the report! I can confirm that I can reproduce this issue, but I still have to figure out what exactly PyRight is complaining about here.
This is going to take a bit longer. I've made some progress on understand __get__ to the point that MyPy now has full parameter support for methods (#129) - but PyRight still chokes. :/
I might have to build an MRE and ask the PyRight maintainers just what the thing complains about. If anyone has capacities to handle this earlier than me, feel free to move ahead.
I seem to have tracked this down to an issue with capturing the -> Awaitable[R] part of the wrapped callable; a synchronous setup using -> R seems to work.
My current MRE for the descriptor:
AC = TypeVar("AC", bound=Callable[..., Awaitable[Any]])
R = TypeVar("R")
P = ParamSpec("P")
S = TypeVar("S")
class ADescr(Generic[AC]):
__call__: AC
def __init__(self, callable: AC): ...
@overload
def __get__(
self: ADescr[AC], instance: None, owner: type | None = ...
) -> ADescr[AC]: ...
@overload
def __get__(
self: ADescr[Callable[Concatenate[S, P], Awaitable[R]]],
instance: S,
owner: type | None = ...,
) -> Callable[P, Awaitable[R]]: ...
def __get__(self, instance: object | None, owner: type | None = None) -> Any: ...
Which fails for a simple testcase:
class Test:
@ADescr
async def foo(self, a: int) -> int:
return a
async def access(self):
return await self.foo(12)
# Cannot access member "foo" for type "Test*"
# Failed to call method "__get__" for descriptor class "ADescr[(self: Self@Test, a: int) -> Coroutine[Any, Any, int]]" Pylance[reportAttributeAccessIssue]
Notably, MyPy accepts this and infers everything correctly.
I've just reported this as https://github.com/microsoft/pyright/issues/7533.
The solution suggested in the PyRight issue doesn't work for the practical case (AC must be invariant since it is also used in __call__). So the workaround is going to stay.