microsoft / pyright

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

is not `None` type narrowing doesn't work when variable is accessed in while loop #8709

Closed btruhand closed 1 month ago

btruhand commented 1 month ago

Describe the bug

I have a variable of type str | None which I access in a while loop (as part of the condition and within it). Prior to the while loop, I perform a is None check and if the variable is None, then I return early

Weirdly enough, the variable in the while loop is still reported to have str | None as its type, both through Pylance in VSCode and the CLI. But I expect type narrowing to have worked and pyright would identify the type as str after the type narrowing.

Interestingly, the same issue doesn't happen when the variable is accessed in a for loop.

Code or Screenshots

from typing import reveal_type

def is_something(a: str):
    return True

def get_random_string() -> str | None:
    return "random"

def main():
    random_string = get_random_string()
    if random_string is None:
        return None

    for i in range(0, len(random_string)):
        print(i)

    x = 0
    while x < 6 and is_something(random_string):
        a = random_string
        reveal_type(a)
        x += 1
        random_string = get_random_string()

main()

Output of the code above when run:

0
1
2
3
4
5
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'
Runtime type is 'str'

VS Code extension or command-line

Result from VSCode:

Screenshot 2024-08-08 at 9 22 13 PM

Result from command line:

test.py:26:34 - error: Argument of type "str | None" cannot be assigned to parameter "a" of type "str" in function "is_something"
    Type "str | None" is incompatible with type "str"
      "None" is incompatible with "str" (reportArgumentType)
  test.py:28:21 - information: Type of "a" is "str | None"
1 error, 0 warnings, 1 information 
btruhand commented 1 month ago

Ah, figured it out myself. It's because of the reassignment in the while loop. Closing issue since it's not a bug :)