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

Release pytest-lsp #181

Merged
merged 8 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
rev: v0.4.8
hooks:
- id: ruff
args: [--fix]
Expand Down
66 changes: 44 additions & 22 deletions docs/pytest-lsp/guide/getting-started-fail-output.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
$ pytest
================================================ test session starts ================================================
platform linux -- Python 3.11.3, pytest-7.2.0, pluggy-1.0.0
rootdir: /tmp/pytest-of-alex/pytest-38/test_getting_started_fail0, configfile: tox.ini
plugins: asyncio-0.21.0, typeguard-3.0.2, lsp-0.3.0
============================= test session starts ==============================
platform linux -- Python 3.11.9, pytest-7.4.4, pluggy-1.5.0
rootdir: /tmp/pytest-of-alex/pytest-12/test_getting_started_fail0
configfile: tox.ini
plugins: lsp-0.4.2, asyncio-0.23.8
asyncio: mode=Mode.AUTO
collected 1 item

test_server.py E [100%]
test_server.py E [100%]

====================================================== ERRORS =======================================================
________________________________________ ERROR at setup of test_completions _________________________________________
==================================== ERRORS ====================================
______________________ ERROR at setup of test_completions ______________________

lsp_client = <pytest_lsp.client.LanguageClient object at 0x7fa2c4168310>
lsp_client = <pytest_lsp.client.LanguageClient object at 0x7ff8e78d7f10>

@pytest_lsp.fixture(
config=ClientServerConfig(server_command=[sys.executable, "server.py"]),
Expand All @@ -22,24 +23,45 @@ lsp_client = <pytest_lsp.client.LanguageClient object at 0x7fa2c4168310>
> await lsp_client.initialize_session(params)

test_server.py:21:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/var/home/alex/Projects/lsp-devtools/.env/lib64/python3.11/site-packages/pytest_lsp/client.py:137: in initialize_sess
ion
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/var/home/alex/Projects/swyddfa/lsp-devtools/release/lib/pytest-lsp/pytest_lsp/client.py:245: in initialize_session
response = await self.initialize_async(params)
/var/home/alex/Projects/lsp-devtools/.env/lib64/python3.11/site-packages/pygls/lsp/client.py:349: in initialize_async
/var/home/alex/.local/share/hatch/env/virtual/pytest-lsp/oa_H1-lS/hatch-test.py3.11-7/lib/python3.11/site-packages/pygls/lsp/client.py:244: in initialize_async
return await self.protocol.send_request_async("initialize", params)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pytest_lsp.protocol.LanguageClientProtocol object at 0x7fa2c417a190>, method = 'initialize'
params = InitializeParams(capabilities=ClientCapabilities(workspace=None, text_document=None, notebook_document=None,
window=No..., root_path=None, root_uri=None, initialization_options=None, trace=None, work_done_token=None, workspac
e_folders=None)
self = <pytest_lsp.protocol.LanguageClientProtocol object at 0x7ff8e78ebf90>
method = 'initialize'
params = InitializeParams(capabilities=ClientCapabilities(workspace=None, text_document=None, notebook_document=None, window=No..., root_path=None, root_uri=None, initialization_options=None, trace=None, work_done_token=None, workspace_folders=None)

async def send_request_async(self, method, params=None):
"""Wrap pygls' ``send_request_async`` implementation. This will

- Check the params to see if they're compatible with the client's stated
capabilities
- Check the result to see if it's compatible with the client's stated
capabilities

Parameters
----------
method
The method name of the request to send

params
The associated parameters to go with the request

Returns
-------
Any
The result
"""
check_params_against_client_capabilities(
self._server.capabilities, method, params
)
> result = await super().send_request_async(method, params)
E asyncio.exceptions.CancelledError: Server process exited with return code: 0
E RuntimeError: Server process 42326 exited with code: 0

/var/home/alex/Projects/lsp-devtools/.env/lib64/python3.11/site-packages/pytest_lsp/protocol.py:42: CancelledError
============================================== short test summary info ==============================================
ERROR test_server.py::test_completions - asyncio.exceptions.CancelledError: Server process exited with return code: 0
================================================= 1 error in 1.15s ==================================================
/var/home/alex/Projects/swyddfa/lsp-devtools/release/lib/pytest-lsp/pytest_lsp/protocol.py:81: RuntimeError
=========================== short test summary info ============================
ERROR test_server.py::test_completions - RuntimeError: Server process 42326 e...
=============================== 1 error in 1.12s ===============================
2 changes: 1 addition & 1 deletion lib/lsp-devtools/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ extra-dependencies = ["pytest-asyncio"]

[envs.hatch-static-analysis]
config-path = "ruff_defaults.toml"
dependencies = ["ruff==0.4.4"]
dependencies = ["ruff==0.4.8"]
1 change: 1 addition & 0 deletions lib/pytest-lsp/changes/173.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The client now waits for the server process to gracefully exit by @OhioDschungel6
2 changes: 1 addition & 1 deletion lib/pytest-lsp/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ matrix.pytest.dependencies = [

[envs.hatch-static-analysis]
config-path = "ruff_defaults.toml"
dependencies = ["ruff==0.4.4"]
dependencies = ["ruff==0.4.8"]
17 changes: 11 additions & 6 deletions lib/pytest-lsp/pytest_lsp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,18 @@ async def stop(self):

async def server_exit(self, server: asyncio.subprocess.Process):
"""Called when the server process exits."""
logger.debug("Server process exited with code: %s", server.returncode)

if self._stop_event.is_set():
return

loop = asyncio.get_running_loop()
loop.call_soon(
cancel_all_tasks,
f"Server process exited with return code: {server.returncode}",
)
# TODO: Should the upstream base client be doing this?
# Cancel any pending futures.
reason = f"Server process {server.pid} exited with code: {server.returncode}"

for id_, fut in self.protocol._request_futures.items():
if not fut.done():
fut.set_exception(RuntimeError(reason))
logger.debug("Cancelled pending request '%s': %s", id_, reason)

def report_server_error(
self, error: Exception, source: Union[PyglsError, JsonRpcException]
Expand Down Expand Up @@ -261,7 +263,10 @@ async def shutdown_session(self) -> None:
return

await self.shutdown_async(None)

self.exit(None)
if self._server:
await self._server.wait()

async def wait_for_notification(self, method: str):
"""Block until a notification with the given method is received.
Expand Down
9 changes: 2 additions & 7 deletions lib/pytest-lsp/tests/test_examples.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pathlib
import sys

import pytest

Expand Down Expand Up @@ -102,12 +101,8 @@ def test_getting_started_fail(pytester: pytest.Pytester):
results = pytester.runpytest()
results.assert_outcomes(errors=1)

if sys.version_info < (3, 9):
message = "E*CancelledError"
else:
message = "E*asyncio.exceptions.CancelledError: Server process exited with return code: 0"

results.stdout.fnmatch_lines(message)
message = r"E\s+RuntimeError: Server process \d+ exited with code: 0"
results.stdout.re_match_lines(message)


def test_generic_rpc(pytester: pytest.Pytester):
Expand Down
28 changes: 7 additions & 21 deletions lib/pytest-lsp/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,8 @@ async def test_capabilities(client):

results.assert_outcomes(errors=1)

if sys.version_info < (3, 9):
message = "E*CancelledError"
else:
message = "E*asyncio.exceptions.CancelledError: Server process exited with return code: 0"

results.stdout.fnmatch_lines(message)
message = r"E\s+RuntimeError: Server process \d+ exited with code: 0"
results.stdout.re_match_lines(message)


def test_detect_server_exit_mid_request(pytester: pytest.Pytester):
Expand Down Expand Up @@ -165,12 +161,8 @@ async def test_capabilities(client):

results.assert_outcomes(failed=1, errors=1)

if sys.version_info < (3, 9):
message = "E*CancelledError"
else:
message = "E*asyncio.exceptions.CancelledError: Server process exited with return code: 0"

results.stdout.fnmatch_lines(message)
message = r"E\s+RuntimeError: Server process \d+ exited with code: 0"
results.stdout.re_match_lines(message)
results.stdout.fnmatch_lines("E*RuntimeError: Client has been stopped.")


Expand All @@ -190,15 +182,9 @@ async def test_capabilities(client):

results.assert_outcomes(errors=1)

if sys.version_info < (3, 9):
message = "E*CancelledError"
else:
message = [
"E*asyncio.exceptions.CancelledError: Server process exited with return code: 1",
"ZeroDivisionError: division by zero",
]

results.stdout.fnmatch_lines(message)
message = r"E\s+RuntimeError: Server process \d+ exited with code: 1"
results.stdout.re_match_lines(message)
results.stdout.fnmatch_lines("ZeroDivisionError: division by zero")


def test_detect_invalid_json(pytester: pytest.Pytester):
Expand Down