microsoft / vscode-python

Python extension for Visual Studio Code
https://aka.ms/pvsc-marketplace
MIT License
4.34k stars 1.19k forks source link

pipe connection error when using pytest-django and pytest-xdist #24044

Open piotr-kubiak opened 2 months ago

piotr-kubiak commented 2 months ago

Type: Bug

Behaviour

I have the following setup: pyproject.toml:

[tool.poetry]
name = "django-vscode-pytest"
version = "0.1.0"
description = ""
authors = "Piotr Kubiak"
readme = "README.md"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.dependencies]
python = "^3.11"
pytest = "^8.3.2"
pytest-xdist = "^3.6.1"
pytest-django = "^4.9.0"

_testFoo.py:

import pytest

@pytest.mark.django_db
def test_pass():
    assert True

I have also enabled the pythonTestAdapter experiment. When I run this test (using the Testing panel), I get the result:

Received JSON data in run script
Running pytest with args: ['-p', 'vscode_pytest', '--rootdir=h:\\python\\django-vscode-pytest', 'h:\\python\\django-vscode-pytest\\test_Foo.py::test_pass']
============================= test session starts =============================
platform win32 -- Python 3.11.7, pytest-8.3.2, pluggy-1.5.0
rootdir: h:\python\django-vscode-pytest
configfile: pyproject.toml
plugins: django-4.9.0, xdist-3.6.1
collected 1 item

test_Foo.py s                                                            [100%]

============================= 1 skipped in 0.03s ==============================
Finished running tests!

The test is skipped (that is expected, I removed Django to create minimal example, and pytest-django skips tests that require Django). Next, I add the -n 4 option to pytest:

[tool.pytest.ini_options]
addopts = [
    "-n 4",
]

Now, when I run the test exactly as before, the test fails unexpetedly with error vscode_pytest.VSCodePytestError: Error attempting to connect to extension named pipe.

Steps to reproduce:

As above.

Diagnostic data

Output for Python in the Output panel (ViewOutput, change the drop-down the upper-right of the Output panel to Python) ``` Received JSON data in run script Running pytest with args: ['-p', 'vscode_pytest', '--rootdir=h:\\python\\django-vscode-pytest', 'h:\\python\\django-vscode-pytest\\test_Foo.py::test_pass'] ============================= test session starts ============================= platform win32 -- Python 3.11.7, pytest-8.3.2, pluggy-1.5.0 rootdir: h:\python\django-vscode-pytest configfile: pyproject.toml plugins: django-4.9.0, xdist-3.6.1 created: 4/4 workers 4 workers [1 item] s [100%]Error attempting to connect to extension named pipe \\.\pipe\python-test-results-475b03408672b927dda0-sock[vscode-pytest]: [Errno 2] No such file or directory: '\\\\.\\pipe\\python-test-results-475b03408672b927dda0-sock' If you are on a Windows machine, this error may be occurring if any of your tests clear environment variables as they are required to communicate with the extension. Please reference https://docs.pytest.org/en/stable/how-to/monkeypatch.html#monkeypatching-environment-variablesfor the correct way to clear environment variables during testing. Traceback (most recent call last): File "c:\Users\Piotr\.vscode\extensions\ms-python.python-2024.12.2-win32-x64\python_files\vscode_pytest\__init__.py", line 857, in send_post_request __writer.connect() File "c:\Users\Piotr\.vscode\extensions\ms-python.python-2024.12.2-win32-x64\python_files\testing_tools\socket_manager.py", line 24, in connect self._writer = open(self.name, "w", encoding="utf-8") # noqa: SIM115, PTH123 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: '\\\\.\\pipe\\python-test-results-475b03408672b927dda0-sock' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "c:\Users\Piotr\.vscode\extensions\ms-python.python-2024.12.2-win32-x64\python_files\vscode_pytest\run_pytest_script.py", line 73, in pytest.main(arg_array) File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\config\__init__.py", line 175, in main ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall raise exception.with_traceback(exception.__traceback__) File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall res = hook_impl.function(*args) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\main.py", line 330, in pytest_cmdline_main return wrap_session(config, _main) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\main.py", line 318, in wrap_session config.hook.pytest_sessionfinish( File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall raise exception.with_traceback(exception.__traceback__) File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall teardown.throw(exception) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\logging.py", line 870, in pytest_sessionfinish return (yield) ^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall teardown.throw(exception) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\terminal.py", line 893, in pytest_sessionfinish result = yield ^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall teardown.throw(exception) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\_pytest\warnings.py", line 141, in pytest_sessionfinish return (yield) ^^^^^ File "h:\python\django-vscode-pytest\.venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall res = hook_impl.function(*args) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "c:\Users\Piotr\.vscode\extensions\ms-python.python-2024.12.2-win32-x64\python_files\vscode_pytest\__init__.py", line 425, in pytest_sessionfinish send_post_request(payload) File "c:\Users\Piotr\.vscode\extensions\ms-python.python-2024.12.2-win32-x64\python_files\vscode_pytest\__init__.py", line 868, in send_post_request raise VSCodePytestError(error_msg) from error vscode_pytest.VSCodePytestError: Error attempting to connect to extension named pipe \\.\pipe\python-test-results-475b03408672b927dda0-sock[vscode-pytest]: [Errno 2] No such file or directory: '\\\\.\\pipe\\python-test-results-475b03408672b927dda0-sock' Finished running tests! ```

Extension version: 2024.12.2 VS Code version: Code 1.92.2 (fee1edb8d6d72a0ddff41e5f71a671c23ed924b9, 2024-08-14T17:29:30.058Z) OS version: Windows_NT x64 10.0.22631 Modes:

User Settings

``` languageServer: "Pylance" testing • pytestEnabled: true experiments • optInto: ["pythonTestAdapter"] ```

Installed Extensions |Extension Name|Extension Id|Version| |---|---|---| |JavaScript Debugger|ms-vscode.js-debug|1.92.0| |JavaScript Debugger Companion Extension|ms-vscode.js-debug-companion|1.1.3| |Pylance|ms-python.vscode-pylance|2024.8.2| |Python|ms-python.python|2024.12.3| |Python Debugger|ms-python.debugpy|2024.10.0| |Table Visualizer for JavaScript Profiles|ms-vscode.vscode-js-profile-table|1.0.9|
eleanorjboyd commented 2 months ago

i will give this a try myself - I am not sure if the problem is multiple connections coming from the multiple runners or an ongoing issue we have seen with named pipes that we are switching off of shortly. I will let you know, thanks!

github-actions[bot] commented 1 month ago

Because we have not heard back with the information we requested, we are closing this issue for now. If you are able to provide the info later on, then we will be happy to re-open this issue to pick up where we left off.

Happy Coding!

piotr-kubiak commented 1 month ago

@karthiknadig @eleanorjboyd Why this issue was closed? I am not aware of any request for more information.

karthiknadig commented 1 month ago

This got accidentally closed due to info-needed flag. Reopening this.

piotr-kubiak commented 1 month ago

@karthiknadig If there is anything I can help with, I am more than happy to do so.

karthiknadig commented 1 month ago

@piotr-kubiak @eleanorjboyd and I are working on a fix for a communication problem between python extension. We can share a build when we have it working, and we any help testing this is greatly appreciated.

Details on the problem

To summarize, we need to use a communication mechanism that does not use stdio, sockets, and works on detached processes (so no injected streams). We can't use stdio because it is difficult to parse the results and errors out of it, and any structured communication is broken if any package decides to write to stdout on import. sockets can't be used because testing network python packages gets broken by this. We can't use custom streams for IPC to inject into the process, because we don't have control over how it is invoked when running under the debugger.

We have two options left, force pytest to write out a structured result document like Junit, or use an alternative mechanism Named Pipes. Junit method is bad for the reason that you don't have live progress, also, it is complicated to map test ids in Junit to pytest test ids. The last option is Named Pipes, on windows this uses builtin named pipes, but on non-windows this has two implementation UDS (unix domain sockets) and fifo. UDS is again created on top of sockets, and runs into the same issues with regular sockets. Additionally, with WSL there are two variants of UDS, Win32 UDS and Unix Sockets. We are only left with fifo for unix and standard named pipes for windows.

Now we come to multi-connection problem, since the two modes of communications are distinct, we did not have a good way to handle multiple connections for both cases. We are exploring, how to handle these for each scenario, we have a rough solution. We will share more details as we get to something that we can share.