pyright icon indicating copy to clipboard operation
pyright copied to clipboard

Pyright hangs on code involving polymorphic functions

Open LeeeeT opened this issue 1 year ago • 1 comments

Pyright hangs on the following code:

from collections.abc import Callable
from typing import Protocol


class MyProto(Protocol):
    def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...


p: MyProto = lambda a: lambda b: a(b)

The code contains a type error on purpose.

LeeeeT avatar Sep 13 '24 17:09 LeeeeT

If I fix the type error in the implementation...

p: MyProto = lambda a, b: a(b(a))

...pyright correctly shows no errors.

But if I then curry both the protocol and the implementation using my favorite ✨ curry 💅 function...

def curry[First, *Rest, Result](function: Callable[[First, *Rest], Result]) -> Callable[[*Rest], Callable[[First], Result]]:
    return lambda *rest: lambda first: function(first, *rest)


class MyProto(Protocol):
    @curry
    def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...


p: MyProto = curry(lambda a, b: a(b(a)))

...pyright, again, hangs.

LeeeeT avatar Sep 13 '24 18:09 LeeeeT

def call[A, B](self, a: Callable[[A], B], b: Callable**[[Callable[[A], B]], A])** -> B: ...

try this ([Callable[[A], B]], A]) or this [[Callable[[A], B]], A]]

Viniciusns120 avatar Jan 08 '25 00:01 Viniciusns120

The fix for bug #10000 (which is included in the 1.1.397 release) addresses the hang for the first code sample above, but the hang persists for the second example.

erictraut avatar Mar 20 '25 22:03 erictraut

I've addressed the hang in the second code sample above. However, this code will not type check without error. The problem is that the curry decorator function is being applied to the pre-bound version of __call__, so the First type parameter maps to the self parameter, but the return type of curry eliminates this parameter from the resulting signature. That means the decorated function can no longer be successfully bound to the containing protocol class, making the decorated __call__ method unusable.

You can fix this by defining a curry_method decorator that correctly handles the self parameter in an instance method (or the cls parameter in a class method).

def curry_method[S, First, *Rest, Result](
    function: Callable[[S, First, *Rest], Result],
) -> Callable[[S, *Rest], Callable[[First], Result]]: ...

class MyProto(Protocol):
    @curry_method
    def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...

erictraut avatar Mar 21 '25 00:03 erictraut

Thank you so much for the fix and for your help! ❤️

LeeeeT avatar Mar 21 '25 10:03 LeeeeT

This is addressed in pyright 1.1.398.

erictraut avatar Mar 26 '25 03:03 erictraut