Pylance-release: contextlib.suppress results in following code being marked as unreachable

Created on 15 Oct 2020  Â·  13Comments  Â·  Source: microsoft/pylance-release

Environment data

  • Language Server version: v2020.10.1
  • OS and version: Ubuntu Linux 20.04
  • Python version: tested with python 3.7 and 3.8

Expected behaviour

Using contextlib.suppress should not result in following code being marked as unreachable.

Actual behaviour

Code following the suppress context block is marked as unreachable if there is a return statement in the block.

Code Snippet / Additional information

image

from contextlib import suppress

def test():

    class A():
        b: str

    a = A()
    with suppress(AttributeError):
        return a.b
    return "I am reachable"

Logs

User belongs to experiment group 'AlwaysDisplayTestExplorer - control'
User belongs to experiment group 'ShowPlayIcon - start'
User belongs to experiment group 'DebugAdapterFactory - experiment'
User belongs to experiment group 'PtvsdWheels37 - experiment'
User belongs to experiment group 'UseTerminalToGetActivatedEnvVars - control'
User belongs to experiment group 'LocalZMQKernel - experiment'
User belongs to experiment group 'CollectLSRequestTiming - control'
User belongs to experiment group 'CollectNodeLSRequestTiming - experiment'
User belongs to experiment group 'EnableIPyWidgets - experiment'
User belongs to experiment group 'RunByLine - experiment'
User belongs to experiment group 'CustomEditorSupport - control'
User belongs to experiment group 'pythonaacf'
Error 2020-10-15 12:57:23: Exception while attempting zmq : [Error: /snap/core/current/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.22' not found (required by /home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/node_modules/zeromq/prebuilds/linux-x64/electron.napi.glibc.node)
    at process.func [as dlopen] (electron/js2c/asar.js:140:31)
    at Object.Module._extensions..node (internal/modules/cjs/loader.js:1081:18)
    at Object.func [as .node] (electron/js2c/asar.js:140:31)
    at Module.load (internal/modules/cjs/loader.js:862:32)
    at Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.Module._load (electron/js2c/asar.js:769:28)
    at Function.t._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1057:776)
    at Function.i._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1027:486)
    at Function.n._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1023:767)
    at Module.require (internal/modules/cjs/loader.js:899:19)
    at r (/snap/code/47/usr/share/code/resources/app/out/vs/loader.js:17:346)
    at load (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/node_modules/node-gyp-build/index.js:21:10)
    at Object.<anonymous> (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/node_modules/zeromq/lib/native.js:6:43)
    at Module.i._compile (/snap/code/47/usr/share/code/resources/app/out/vs/loader.js:17:571)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1051:10)
    at Module.load (internal/modules/cjs/loader.js:862:32)
    at Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.Module._load (electron/js2c/asar.js:769:28)
    at Function.t._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1057:776)
    at Function.i._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1027:486)
    at Function.n._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1023:767)
    at Module.require (internal/modules/cjs/loader.js:899:19)
    at r (/snap/code/47/usr/share/code/resources/app/out/vs/loader.js:17:346)
    at Object.<anonymous> (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/node_modules/zeromq/lib/index.js:3:16)
    at Module.i._compile (/snap/code/47/usr/share/code/resources/app/out/vs/loader.js:17:571)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1051:10)
    at Module.load (internal/modules/cjs/loader.js:862:32)
    at Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.Module._load (electron/js2c/asar.js:769:28)
    at Function.t._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1057:776)
    at Function.i._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1027:486)
    at Function.n._load (/snap/code/47/usr/share/code/resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:1023:767)
    at Module.require (internal/modules/cjs/loader.js:899:19)
    at r (/snap/code/47/usr/share/code/resources/app/out/vs/loader.js:17:346)
    at Object.<anonymous> (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/extension.js:24:302772)
    at n (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/extension.js:1:186)
    at /home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/extension.js:56:731654
    at processTicksAndRejections (internal/process/task_queues.js:94:5)
    at async d.zmqSupportedImpl (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/extension.js:56:731621)]
> conda --version
> pyenv root
> python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> python3.6 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> python3 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> python2 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> python ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import sys;print(sys.executable)"
> conda info --json
> conda env list
> conda env list
Starting Pylance language server.
Python interpreter path: /usr/bin/python3.7
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/run_adapter.py discover pytest -- --rootdir ~/Documents/Code/vscode-bugs -s --cache-clear
cwd: ~/Documents/Code/vscode-bugs
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/run_adapter.py discover pytest -- --rootdir ~/Documents/Code/vscode-bugs -s --cache-clear
cwd: ~/Documents/Code/vscode-bugs
Error 2020-10-15 12:57:25: Failed to parse discovered Test [r [Error]: Traceback (most recent call last):
  File "/home/user/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/run_adapter.py", line 17, in <module>
    from testing_tools.adapter.__main__ import parse_args, main
  File "/home/user/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/adapter/__main__.py", line 9, in <module>
    from . import pytest, report
  File "/home/user/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/adapter/pytest/__init__.py", line 7, in <module>
    from ._discovery import discover
  File "/home/user/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/testing_tools/adapter/pytest/_discovery.py", line 8, in <module>
    import pytest
ModuleNotFoundError: No module named 'pytest'

    at ChildProcess.<anonymous> (/home/user/.vscode/extensions/ms-python.python-2020.9.114305/out/client/extension.js:9:53541)
    at Object.onceWrapper (events.js:313:26)
    at ChildProcess.emit (events.js:223:5)
    at maybeClose (internal/child_process.js:1021:16)
    at Socket.<anonymous> (internal/child_process.js:430:11)
    at Socket.emit (events.js:223:5)
    at Pipe.<anonymous> (net.js:664:12)]
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import pytest"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import pytest"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py mypy --ignore-missing-imports --follow-imports=silent --show-column-numbers ~/Documents/Code/vscode-bugs/test.py
cwd: ~/Documents/Code/vscode-bugs
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py mypy --ignore-missing-imports --follow-imports=silent --show-column-numbers ~/Documents/Code/vscode-bugs/test.py
cwd: ~/Documents/Code/vscode-bugs
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import mypy"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import mypy"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import mypy"
> /usr/bin/python3.7 ~/.vscode/extensions/ms-python.python-2020.9.114305/pythonFiles/pyvsc-run-isolated.py -c "import mypy"

Linter 'mypy' is not installed. Please install it or select another linter".
Error: Module 'mypy' not installed.
enhancement fixed in next version

Most helpful comment

Thanks for the bug report. You are correct, I introduced a regression in this code when fixing another bug. The nature of the regression was such that it eluded our unit tests. I've fixed the regression and improved our unit tests so this won't happen again in the future. This will be fixed in the next release. Apologies for any inconvenience.

All 13 comments

@erictraut I may be misremembering, but I had thought the code flow analysis had been modified to treat context managers with an __exit__ that returns bool as swallowing exceptions. Is that not the case? I know that's the assumption that typeshed makes.

Yeah, that's not the case. That's a very large change and will have impacts on perf and (potentially) correctness in some cases. It effectively means that every with statement needs to be treated as a try/catch/finally statement in the code flow graph, which is built prior to knowing any type information. We'd be trading off what is a relatively minor annoyance (grayed-out code in some rare cases) for false positive errors in some cases.

Since this convention was never ratified in a PEP — and because of the tradeoffs it entails, I haven't implemented it.

If the grayed-out code bothers users, there is an easy workaround — use a try/catch block rather than relying on a context manager to handle exceptions.

We'd be trading off what is a relatively minor annoyance (grayed-out code in some rare cases) for false positive errors in some cases.

The catch here to me is that because the code flow is "proving" that the code below is unreachable, it's not just a visual problem but impacts the code below. It was my impression that LSP features, inference, additional flow, etc, inside grayed-out code is limited because the analysis believes the code to be dead.

Yeah, there are potentially false negatives — actual errors in that code that are not reported. In general, false positives are worse than false negatives, but I agree that both are undesirable.

I'll mark it as an enhancement for now; I think there's some discussion across type checkers to be had here as to what is expected from them, especially if this isn't in any specification.

Adding a "Me too" here. and yes using a try/except is a workaround, but just dropping exceptions with with suppress is far cleaner.

This will be addressed in the next release.

This issue has been fixed in version 2020.12.1, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/master/CHANGELOG.md#2020121-9-december-2020

This issue just came back for me with version 2021.1.1.

I downloaded the VSIX file for the last 5 versions and from 2020.12.1 to 2021.1.0 everything was fine. Interestingly enough I also do not have the issue with 2020.12.0.

@Cielquan, I agree with you. It reappeared in version 2021.1.1, but not in version 2021.1.0.

Thanks for the bug report. You are correct, I introduced a regression in this code when fixing another bug. The nature of the regression was such that it eluded our unit tests. I've fixed the regression and improved our unit tests so this won't happen again in the future. This will be fixed in the next release. Apologies for any inconvenience.

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

Was this page helpful?
0 / 5 - 0 ratings