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

Pylance reports reportUnboundVariable when using walrus operator with a while loop that rebinds an input (intermittent) #864

Closed
eternaldensity opened this issue Jan 22, 2021 · 2 comments
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@eternaldensity
Copy link

Environment data

  • Language Server version: v2021.1.1
  • OS and version: Windows 10 Build 19041 - but using WSL 1, so it's actually in Ubuntu 20.04.1 LTS
  • Python version: 3.9.0 (installed on WSL 1 with pip, using Poetry virtual environment)

Expected behaviour

No variable in this code (a cut down toy example, which functions correctly) should be marked as an unbound variable:

import re


def find_s(text):
    return re.search('s', text)


def remove_s(text):
    while match := find_s(text):
        i = match.start(0)
        text = text[:i] + text[i+1:]
    return text

(Yes this isn't a great way of removing instances of 's' from a string. My original code that I found this bug on was structurally the same and performed a more complex task. I simplified it for easier testing.)

Actual behaviour

Both instances of i in the line text = text[:i] + text[i+1:] are marked as unbound by pylance. 'i' is obviously bound on the previous line.

Sometimes editing the code and changing it back makes the unbound variable reports go away temporarily. If that happens, adding and removing a space makes it come back.

Logs

Nothing that looks useful.

Background analysis message: setFileOpened
Background analysis message: markFilesDirty
Background analysis message: analyze
[BG(1)] analyzing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py ...
[BG(1)]   parsing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (1ms)
[BG(1)]   binding: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (0ms)
[BG(1)]   checking: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (13ms)
[BG(1)] analyzing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (15ms)
Background analysis message: resumeAnalysis
Background analysis message: getSemanticTokens
[FG] parsing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (1ms)
[FG] binding: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (1ms)
Background analysis message: setFileOpened
Background analysis message: markFilesDirty
Background analysis message: getDiagnosticsForRange
Background analysis message: getDiagnosticsForRange
[FG] parsing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (2ms)
[FG] binding: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (0ms)
Background analysis message: getSemanticTokens
[BG(1)] parsing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (1ms)
[BG(1)] binding: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (0ms)
Background analysis message: analyze
[BG(1)] analyzing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py ...
[BG(1)]   checking: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (2ms)
[BG(1)] analyzing: /mnt/c/dev/crash-release/crashsite/fiction/undefined.py (2ms)
Background analysis message: resumeAnalysis
Background analysis message: getDiagnosticsForRange
Background analysis message: getDiagnosticsForRange

Code Snippet / Additional information

This problem seems to be very specific as the following (incorrect) snippets are not marked with any variables unbound (or any warnings or errors at all).

Not changing the value of 'text':

import re


def find_s(text):
    return re.search('s', text)


def remove_s(text):
    while match := find_s(text):
        i = match.start(0)
        text[:i] + text[i+1:]
    return text

setting 'i' to a variable instead of a field of the variable:

import re


def find_s(text):
    return re.search('s', text)


def remove_s(text):
    while match := find_s(text):
        i = match
        text = text[:i] + text[i+1:]
    return text

Adding a break (or return) statement also prevents the warning. However, only if it is unconditional.

import re


def find_s(text):
    return re.search('s', text)


def remove_s(text):
    while match := find_s(text):
        i = match.start(0)
        text = text[:i] + text[i+1:]
        break
    return text

The warning also does not occur if I change 'while' to 'if'.

@erictraut
Copy link
Contributor

Thanks for the bug report. This one turned out to be a really difficult bug to find. The circumstances in which it occurred were very rare, and it depends on the ordering in which types are resolved, which explains why you were able to eliminate the problem by perturbing the code in small ways.

The fix will appear in the next release.

@erictraut erictraut added bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version and removed triage labels Jan 22, 2021
@jakebailey
Copy link
Member

This issue has been fixed in version 2021.1.3, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/main/CHANGELOG.md#202113-27-january-2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version
Projects
None yet
Development

No branches or pull requests

3 participants