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

Documentation updates #67

Merged
merged 4 commits into from
Jul 1, 2023
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
17 changes: 17 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
from docutils import nodes # noqa: E402
from sphinx.application import Sphinx # noqa: E402

DEV_BUILD = os.getenv("BUILDDIR", None) == "latest"
BRANCH = "develop" if DEV_BUILD else "release"

project = "LSP Devtools"
copyright = "2023, Alex Carney"
author = "Alex Carney"
Expand All @@ -24,6 +27,7 @@
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.intersphinx",
"sphinx_copybutton",
"sphinx_design",
"supported_clients",
]
Expand All @@ -47,6 +51,19 @@
html_theme = "furo"
html_title = "LSP Devtools"
# html_static_path = ["_static"]
html_theme_options = {
"source_repository": "https://github.com/swyddfa/lsp-devtools/",
"source_branch": BRANCH,
"source_directory": "docs/",
}

if DEV_BUILD:
html_theme_options["announcement"] = (
"This is the unstable version of the documentation, features may change or "
"be removed without warning. "
'<a href="/lsp-devtools/docs/stable/en/">Click here</a> '
"to view the released version"
)


def lsp_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
Expand Down
1 change: 1 addition & 0 deletions docs/pytest-lsp/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ User Guide
guide/language-client
guide/client-capabilities
guide/fixtures
guide/troubleshooting
30 changes: 0 additions & 30 deletions docs/pytest-lsp/guide/fixtures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,6 @@ Fixtures

.. highlight:: none

Fixture Scope
-------------

Setting your client `fixture's scope <https://docs.pytest.org/en/7.1.x/how-to/fixtures.html#scope-sharing-fixtures-across-classes-modules-packages-or-session>`__ to something like ``session`` will allow you to reuse the same client-server connection across multiple test cases.
However, you're likely to encounter an error like the following::

__________________________ ERROR at setup of test_capabilities _________________________
ScopeMismatch: You tried to access the function scoped fixture event_loop with a session
scoped request object, involved factories:
/.../site-packages/pytest_lsp/plugin.py:201: def the_fixture(request)


This is due to the default `event_loop <https://pytest-asyncio.readthedocs.io/en/latest/reference/fixtures.html#event-loop>`__ fixture provided by `pytest-asyncio`_ not living long enough to support your client.
To fix this you can override the ``event_loop`` fixture, setting its scope to match that of your client.

.. code-block:: python

@pytest.fixture(scope="session")
def event_loop():
"""Redefine `pytest-asyncio's default event_loop fixture to match the scope
of our client fixture."""
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
yield loop
loop.close()


.. _pytest-asyncio: https://github.com/pytest-dev/pytest-asyncio


Parameterised Fixtures
----------------------

Expand Down
3 changes: 1 addition & 2 deletions docs/pytest-lsp/guide/getting-started-fail-output.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
$ pytest
================================================ test session starts =========================
=======================
================================================ 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
Expand Down
2 changes: 1 addition & 1 deletion docs/pytest-lsp/guide/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ With the framework in place, we can go ahead and define our first test case

.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/getting-started/t_server.py
:language: python
:start-at: async def test_
:start-at: @pytest.mark.asyncio

All that's left is to run the test suite!

Expand Down
8 changes: 4 additions & 4 deletions docs/pytest-lsp/guide/language-client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The client maintains a record of any :attr:`~pytest_lsp.LanguageClient.diagnosti

.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/diagnostics/t_server.py
:language: python
:start-at: async def test_
:start-at: @pytest.mark.asyncio


.. note::
Expand All @@ -40,7 +40,7 @@ Any :lsp:`window/logMessage` notifications sent from the server will be accessib

.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/window-log-message/t_server.py
:language: python
:start-at: async def test_
:start-at: @pytest.mark.asyncio

.. card:: server.py

Expand Down Expand Up @@ -92,7 +92,7 @@ Similar to ``window/logMessage`` above, the client records any :lsp:`window/show

.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/window-show-document/t_server.py
:language: python
:start-at: async def test_
:start-at: @pytest.mark.asyncio

.. card:: server.py

Expand All @@ -111,7 +111,7 @@ Similar to ``window/logMessage`` above, the client records any :lsp:`window/show

.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/window-show-message/t_server.py
:language: python
:start-at: async def test_
:start-at: @pytest.mark.asyncio

.. card:: server.py

Expand Down
118 changes: 118 additions & 0 deletions docs/pytest-lsp/guide/troubleshooting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
Troubleshooting
===============

My tests won't run!
-------------------

You may encounter an issue where some of your test cases that use ``pytest-lsp`` are unexpectedly skipped.

.. code-block:: none

================================ test session starts =================================
platform linux -- Python 3.10.6, pytest-7.3.2, pluggy-1.1.0
rootdir: /home/username/projects/lsp/pytest-lsp
plugins: lsp-0.3.0, typeguard-3.0.2, asyncio-0.21.0
asyncio: mode=strict
collected 1 item

test_server.py s [100%]

================================== warnings summary ==================================
test_server.py::test_completions
/home/username/projects/lsp/pytest-lsp/venv/lib/python3.10/site-packages/_pytest/python.py:183: PytestUnhandledCoroutineWarning: async def functions are not natively supported and have been skipped.
You need to install a suitable plugin for your async framework, for example:
- anyio
- pytest-asyncio
- pytest-tornasync
- pytest-trio
- pytest-twisted
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))

=========================== 1 skipped, 1 warning in 0.64s ============================

It's likely that you forgot to add a ``@pytest.mark.asyncio`` marker to your test function(s)

.. code-block:: python

import pytest

@pytest.mark.asyncio
async def test_server(client: LanguageClient):
...

Alternatively, if you prefer, you can set the following configuration option in your project's ``pyproject.toml``

.. code-block:: toml

[tool.pytest.ini_options]
asyncio_mode = "auto"

In which case `pytest-asyncio`_ will automatically collect and run any ``async`` test function in your test suite.

``ScopeMismatch`` Error
-----------------------

Setting your client `fixture's scope <https://docs.pytest.org/en/7.1.x/how-to/fixtures.html#scope-sharing-fixtures-across-classes-modules-packages-or-session>`__ to something like ``session`` will allow you to reuse the same client-server connection across multiple test cases.
However, you're likely to encounter an error like the following::

__________________________ ERROR at setup of test_capabilities _________________________
ScopeMismatch: You tried to access the function scoped fixture event_loop with a session
scoped request object, involved factories:
/.../site-packages/pytest_lsp/plugin.py:201: def the_fixture(request)


This is due to the default `event_loop <https://pytest-asyncio.readthedocs.io/en/latest/reference/fixtures.html#event-loop>`__ fixture provided by `pytest-asyncio`_ not living long enough to support your client.
To fix this you can override the ``event_loop`` fixture, setting its scope to match that of your client.

.. code-block:: python

@pytest.fixture(scope="session")
def event_loop():
"""Redefine `pytest-asyncio's default event_loop fixture to match the scope
of our client fixture."""
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
yield loop
loop.close()


.. _pytest-asyncio: https://github.com/pytest-dev/pytest-asyncio

``DeprecationWarning``: Unclosed event loop
-------------------------------------------

Depending on the version of ``pygls`` (the LSP implementation used by ``pytest-lsp``) you have installed, you may encounter a ``DeprecationWarning`` abount an unclosed event loop.

.. code-block:: none

================================ test session starts =================================
platform linux -- Python 3.10.6, pytest-7.3.2, pluggy-1.1.0
rootdir: /home/username/projects/lsp/pytest-lsp
plugins: lsp-0.3.0, typeguard-3.0.2, asyncio-0.21.0
asyncio: mode=strict
collected 1 item

test_server.py . [100%]

================================== warnings summary ==================================
test_server.py::test_completions
/home/username/projects/lsp/pytest-lsp/venv/lib/python3.10/site-packages/pytest_asyncio/plugin.py:444: DeprecationWarning: pytest-asyncio detected an unclosed event loop when tearing down the event_loop
fixture: <_UnixSelectorEventLoop running=False closed=False debug=False>
pytest-asyncio will close the event loop for you, but future versions of the
library will no longer do so. In order to ensure compatibility with future
versions, please make sure that:
1. Any custom "event_loop" fixture properly closes the loop after yielding it
5. Your code does not modify the event loop in async fixtures or tests

warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== 1 passed, 1 warning in 0.64s =============================

This is a known issue in ``pygls v1.0.2`` and older, upgrading your ``pygls`` version to ``TBD`` should resolve the issue.

.. note::

While this issue has been `fixed <https://github.com/openlawlibrary/pygls/pull/336>`_ upstream, it is not yet generally available.
However, the warning itself is fairly mild - ``pytest-lsp``/``pygls`` are not cleaning the event loop up correctly but are otherwise working as expected.
It should be safe to ignore this while waiting for the fix to become available.
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This assumes you are running the pip install command from the root of the repo e.g.
# $ pip install -r docs/requirements.txt
sphinx
sphinx-copybutton
sphinx-design
furo
git+https://github.com/openlawlibrary/pygls.git#egg=pygls
Expand Down
2 changes: 2 additions & 0 deletions lib/pytest-lsp/tests/examples/diagnostics/t_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from lsprotocol.types import InitializeParams
from lsprotocol.types import TextDocumentItem

import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig
from pytest_lsp import LanguageClient
Expand All @@ -25,6 +26,7 @@ async def client(lsp_client: LanguageClient):
await lsp_client.shutdown_session()


@pytest.mark.asyncio
async def test_diagnostics(client: LanguageClient):
"""Ensure that the server implements diagnostics correctly."""

Expand Down
2 changes: 2 additions & 0 deletions lib/pytest-lsp/tests/examples/getting-started/t_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lsprotocol.types import Position
from lsprotocol.types import TextDocumentIdentifier

import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig
from pytest_lsp import LanguageClient
Expand All @@ -26,6 +27,7 @@ async def client(lsp_client: LanguageClient):
await lsp_client.shutdown_session()


@pytest.mark.asyncio
async def test_completions(client: LanguageClient):
"""Ensure that the server implements completions correctly."""

Expand Down
2 changes: 2 additions & 0 deletions lib/pytest-lsp/tests/examples/window-log-message/t_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lsprotocol.types import Position
from lsprotocol.types import TextDocumentIdentifier

import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig
from pytest_lsp import LanguageClient
Expand All @@ -26,6 +27,7 @@ async def client(lsp_client: LanguageClient):
await lsp_client.shutdown_session()


@pytest.mark.asyncio
async def test_completions(client: LanguageClient):
results = await client.text_document_completion_async(
params=CompletionParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lsprotocol.types import Position
from lsprotocol.types import TextDocumentIdentifier

import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig
from pytest_lsp import LanguageClient
Expand All @@ -26,6 +27,7 @@ async def client(lsp_client: LanguageClient):
await lsp_client.shutdown_session()


@pytest.mark.asyncio
async def test_completions(client: LanguageClient):
test_uri = "file:///path/to/file.txt"
results = await client.text_document_completion_async(
Expand Down
2 changes: 2 additions & 0 deletions lib/pytest-lsp/tests/examples/window-show-message/t_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lsprotocol.types import Position
from lsprotocol.types import TextDocumentIdentifier

import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig
from pytest_lsp import LanguageClient
Expand All @@ -26,6 +27,7 @@ async def client(lsp_client: LanguageClient):
await lsp_client.shutdown_session()


@pytest.mark.asyncio
async def test_completions(client: LanguageClient):
results = await client.text_document_completion_async(
params=CompletionParams(
Expand Down
Loading