microsoft / pyright

Static Type Checker for Python
Other
13.09k stars 1.4k forks source link

Generic types are reused / only bound once #7988

Closed mtomassoli closed 1 month ago

mtomassoli commented 3 months ago

Please consider the following code:

from typing import TypeVar, cast

L1 = TypeVar('L1')
L2 = TypeVar('L2')

def f(lock1: L1, lock2: L2, x: list[L1], y: list[L2]) -> None:
    f(lock2, lock1, cast(list[L1], 0), cast(list[L2], 0))

Shouldn't the last line above give an error since the two locks are swapped? Mypy does give an error, but pyright doesn't.

To get an error in pyright, one has to duplicate f:

from typing import TypeVar, cast

L1 = TypeVar('L1')
L2 = TypeVar('L2')

def f(lock1: L1, lock2: L2, x: list[L1], y: list[L2]) -> None:
    ...

def f2(lock1: L1, lock2: L2, x: list[L1], y: list[L2]) -> None:
    f(lock2, lock1, cast(list[L1], 0), cast(list[L2], 0))

Unless this is expected behavior and mypy is wrong, the problem seems to be that in the code

def f(lock1: L1, lock2: L2, x: list[L1], y: list[L2]) -> None:
    f(lock2, lock1, cast(list[L1], 0), cast(list[L2], 0))

there's just a single copy of L1 and L2 involved. To get two independent instances, one has to basically create another function.

The same happens in other contexts. Here's again some code I posted some time ago (that you didn't have time to read):

def concat[X1: Digit, X2: Digit, X3: Digit, X4: Digit,
           Y1: Digit, Y2: Digit, Y3: Digit, Y4: Digit,
           Z1: Digit, Z2: Digit, Z3: Digit, Z4: Digit,
           C1: Digit, C2: Digit, C3: Digit
](
    self: NList[X4, X3, X2, X1],
    nlist2: NList[Y4, Y3, Y2, Y1], *,
    # NOTE: No, we CANNOT use the same `adc` (add with carry) because
    #   otherwise its generic types will be bound once and reused for all
    #   its instances, which is NOT what we want here.
    #   BUG in Pyright?
    # NOTE: The trick with the default argument (`= adc1`) does NOT work
    #   recursively, that is, we CANNOT use `concat` itself as a default
    #   arg in another function. The problem is that Pyright gives up and
    #   doesn't resolve it.
    _adc_digit1: Callable[[X1, Y1, L0], tuple[Z1, C1]] = adc1,
    _adc_digit2: Callable[[X2, Y2, C1], tuple[Z2, C2]] = adc2,
    _adc_digit3: Callable[[X3, Y3, C2], tuple[Z3, C3]] = adc3,
    _adc_digit4: Callable[[X4, Y4, C3], tuple[Z4, L0]] = adc4,
) -> NList[Z4, Z3, Z2, Z1]:
    """Concatenates 2 nlists of the same length."""
    ...

The point is that adc1, ..., adc4 are copies of the same function. I had to write the same function 4 times!

I really hope this is a bug and not intended behavior!

erictraut commented 3 months ago

Yes, this is a bug. It's the same root cause as #7507 and #7369.

JamesHutchison commented 3 months ago

Possibly related.

PyLance now fails to interpret the generic type of this staticmethod (arg is type[T]) that is part of a generic class.

    @no_type_check
    @staticmethod
    def it(
        spec: type[T] | None = None,
        *,
        spec_set: bool = True,
...

I checked and the @no_type_check decorator makes no difference on the result.

If I change to a brand new typevar the type hint changes to remove the Any but it still doesn't properly give the type.

Expected value is T | MegaMock. Using T TypeVar which is used throughout the file, I get Any | MegaMock. Using W which I created just for this method, I get simply MegaMock. Using PyLance 2024.5.1. PyRight is not explicitly installed.

TypeVar(T) T-typevar

TypeVar(W) W-typevar

You can see by the coloring it doesn't know what bar is and doesn't provide it as an option for autocomplete. It does show all the MegaMock methods.

Library is MegaMock: https://github.com/JamesHutchison/megamock

erictraut commented 3 months ago

@JamesHutchison, sorry but I don't understand your post. It's definitely not related to this issue. If you'd like to file a separate bug report, please open a new issue and include a minimal, self-contained code sample that demonstrates the problem you're seeing. Your code sample above is incomplete, so I'm not sure what it's trying to demonstrate. If you have questions about pylance language server features like semantic highlighting, the pylance-release project is a good place to post.

erictraut commented 1 month ago

This is addressed in pyright 1.1.373.