Client Capabilities¶
The initialize request at the start of an LSP session allows the client and server to exchange information about each other. Of particular interest is the ClientCapabilities field which is used to inform the server which parts of the specification the client supports.
Setting this field to the right value pytest-lsp
can pretend to be a particular editor at a particular version and check to see if the server adapts accordingly.
Supported Clients¶
pytest-lsp
currently supports the following clients and versions.
Client |
Versions |
---|---|
Neovim |
0.6.1, 0.7.0, 0.8.0, 0.9.1 |
Visual Studio Code |
1.65.2 |
Emacs |
29.1 |
The client_capabilities()
function can be used to load the capabilities corresponding to a given client name
@pytest_lsp.fixture(
config=ClientServerConfig(server_command=[sys.executable, "server.py"]),
)
async def client(lsp_client: LanguageClient):
# Setup
await lsp_client.initialize_session(
InitializeParams(
capabilities=client_capabilities("neovim"),
),
)
yield
# Teardown
await lsp_client.shutdown_session()
Specification Compliance Checks¶
By setting the client’s capabilities to anything other than ClientCapabilities()
, pytest-lsp
will automatically enable checks to ensure that the server respects the capabilities published by the client.
If any issues are found, pytest-lsp
will emit an LspSpecificationWarning
.
Tip
For full details on the checks that have been implemented see the pytest_lsp.checks
module.
As an example, let’s write a test for the following language server.
@server.feature(TEXT_DOCUMENT_COMPLETION)
def completion(ls: LanguageServer, params: CompletionParams):
return [
CompletionItem(
label="greet",
insert_text='"Hello, ${1:name}!"$0',
insert_text_format=InsertTextFormat.Snippet,
),
]
When it receives a completion request it returns a single item called greet
which, when selected, expands into a snippet making it easier to type the sequence "Hello, world!"
.
Let’s write a test to confirm it works as expected.
async def test_completions(client: LanguageClient):
"""Ensure that the server implements completions correctly."""
results = await client.text_document_completion_async(
params=CompletionParams(
position=Position(line=1, character=0),
text_document=TextDocumentIdentifier(uri="file:///path/to/file.txt"),
)
)
assert results is not None
if isinstance(results, CompletionList):
items = results.items
else:
items = results
labels = [item.label for item in items]
assert labels == ["greet"]
Running this test while pretending to be neovim
we should see that while it passes, pytest-lsp
will emit a warning saying that neovim does not support snippets.
Note
Vanilla Neovim v0.6.1 does not support snippets, though there are many plugins that can be installed to enable support for them.
$ pytest
======================================== test session starts ========================================
platform linux -- Python 3.11.3, pytest-7.2.0, pluggy-1.0.0
rootdir: test_client_capabilities0, configfile: tox.ini
plugins: typeguard-2.13.3, asyncio-0.20.2, lsp-0.2.1
asyncio: mode=Mode.AUTO
collected 1 item
test_server.py . [100%]
========================================= warnings summary ==========================================
test_server.py::test_completions
test_client_capabilities0/test_server.py:35: LspSpecificationWarning: Client does not support snippets.
assert False
results = await client.text_document_completion_async(
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================== 1 passed, 1 warning in 1.02s ====================================
Strict Checks¶
You can upgrade these warnings to be errors if you wish by passing -W error::pytest_lsp.LspSpecificationWarning
to pytest.
$ pytest -W error::pytest_lsp.LspSpecificationWarning
======================================== test session starts ========================================
platform linux -- Python 3.11.3, pytest-7.2.0, pluggy-1.0.0
rootdir: test_client_capabilities_error0, configfile: tox.ini
plugins: typeguard-2.13.3, asyncio-0.20.2, lsp-0.2.1
asyncio: mode=Mode.AUTO
collected 1 item
test_server.py F [100%]
============================================= FAILURES ==============================================
_________________________________________ test_completions __________________________________________
...
try:
result_checker(capabilities, result)
except AssertionError as e:
> warnings.warn(str(e), LspSpecificationWarning, stacklevel=4)
E pytest_lsp.checks.LspSpecificationWarning: Client does not support snippets.
E assert False
/.../site-packages/pytest_lsp/checks.py:73: LspSpecificationWarning
====================================== short test summary info ======================================
FAILED test_server.py::test_completions - pytest_lsp.checks.LspSpecificationWarning: Client does n...
========================================= 1 failed in 1.16s =========================================
Disabling Checks¶
Alternatively, you can ignore these warnings by passing -W ignore::pytest_lsp.LspSpecificationWarning
to pytest.
$ pytest -W ignore::pytest_lsp.LspSpecificationWarning
======================================== test session starts ========================================
platform linux -- Python 3.11.3, pytest-7.2.0, pluggy-1.0.0
rootdir: test_client_capabilities_ignore0, configfile: tox.ini
plugins: typeguard-2.13.3, asyncio-0.20.2, lsp-0.2.1
asyncio: mode=Mode.AUTO
collected 1 item
test_server.py . [100%]
========================================= 1 passed in 1.02s =========================================
See also
- Controlling warnings
Pytest’s documentation on configuring how warnings should be handled
- The Warnings Filter
Python’s built in warning filter syntax