microsoft / pyright

Static Type Checker for Python
Other
13.04k stars 1.39k forks source link

Incorrect Type Checking Error at generic function #8374

Closed lyj0309 closed 1 month ago

lyj0309 commented 1 month ago

Describe the bug I am encountering an incorrect type checking error when using random.choice on a list of tuples and passing the result to a function that expects a str.

Steps to Reproduce:

Create a list of tuples as follows:

data: list[tuple[str, str]] = [("apple", "red"), ("banana", "yellow"), ("grape", "purple")]

Define a function that expects a str:

def test(s: str):
    print(s)

Use random.choice to select a random tuple from the list and pass it to the function:

import random

random_item = random.choice(data)
test(random.choice(data))

Expected Behavior:

The type checker should raise an error indicating that the argument type tuple[str, str] cannot be assigned to the parameter type str in the test function.

Actual Behavior:

The type checker incorrectly reports an error on the use of random.choice(data) with the following message:

Argument of type "list[tuple[str, str]]" cannot be assigned to parameter "seq" of type "SupportsLenAndGetItem[_T@choice]" in function "choice"
"list[tuple[str, str]]" is incompatible with protocol "SupportsLenAndGetItem[str]"
"__getitem__" is an incompatible type
No overloaded function matches type "(k: int, /) -> _T_co@SupportsLenAndGetItem"

Code or Screenshots image image

import random

# Example list of tuples
data:list[tuple[str,str]] = [("apple", "red"), ("banana", "yellow"), ("grape", "purple")]

def test(s:str):
  print(s)
# Using random.choice to select a random tuple from the list
random_item = random.choice(data)
test(random.choice(data))
erictraut commented 1 month ago

Pyright is working correctly here.

The issue has to do with the way bidirectional type inference works in a static type checker like pyright.

The call to random.choice is expected to return a type compatible with str, since that's the type of the value required to satisfy a call to test. If random.choice returns a str, then the input must be of type SupportsLenAndGetItem[str], since str is a subtype of Sequence[str]. The value data in this case is not compatible with SupportsLenAndGetItem[str], so an error is reported to that effect.