Skip to content
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

Generic types are reused / only bound once #7988

Open
mtomassoli opened this issue May 24, 2024 · 3 comments
Open

Generic types are reused / only bound once #7988

mtomassoli opened this issue May 24, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@mtomassoli
Copy link

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!

@mtomassoli mtomassoli added the bug Something isn't working label May 24, 2024
@erictraut
Copy link
Collaborator

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

@JamesHutchison
Copy link

JamesHutchison commented May 28, 2024

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
Copy link
Collaborator

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants