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

lsp-devtools: upgrade to pygls 2.0a2 #192

Merged
merged 5 commits into from
Nov 23, 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
8 changes: 4 additions & 4 deletions .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.6.9
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
Expand All @@ -20,15 +20,15 @@ repos:
files: 'lib/.*\.py'

- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.11.2'
rev: 'v1.13.0'
hooks:
- id: mypy
name: mypy (pytest-lsp)
args: [--explicit-package-bases,--check-untyped-defs]
additional_dependencies:
- importlib-resources
- platformdirs
- pygls
- 'pygls>=2.0a2'
- pytest
- pytest-asyncio
- websockets
Expand All @@ -42,7 +42,7 @@ repos:
- attrs
- importlib-resources
- platformdirs
- pygls
- 'pygls>=2.0a2'
- stamina
- textual
- websockets
Expand Down
1 change: 1 addition & 0 deletions lib/lsp-devtools/changes/xxx.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Migrate to pygls `v2.0a2`
5 changes: 4 additions & 1 deletion lib/lsp-devtools/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ packages = ["lsp_devtools"]
[envs.hatch-test]
extra-dependencies = ["pytest-asyncio"]

[envs.hatch-test.env-vars]
UV_PRERELEASE="allow"

[envs.hatch-static-analysis]
config-path = "ruff_defaults.toml"
dependencies = ["ruff==0.5.2"]
dependencies = ["ruff==0.8.0"]
10 changes: 5 additions & 5 deletions lib/lsp-devtools/lsp_devtools/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from typing import Callable
from typing import Union

from pygls.io_ import AsyncReader

MessageHandler = Callable[[bytes], Union[None, Coroutine[Any, Any, None]]]

UTC = timezone.utc
Expand Down Expand Up @@ -74,7 +76,7 @@ def parse_rpc_message(data: bytes) -> RPCMessage:
return RPCMessage(headers, body)


async def aio_readline(reader: asyncio.StreamReader, message_handler: MessageHandler):
async def aio_readline(reader: AsyncReader, message_handler: MessageHandler):
CONTENT_LENGTH_PATTERN = re.compile(rb"^Content-Length: (\d+)\r\n$")

# Initialize message buffer
Expand Down Expand Up @@ -222,12 +224,10 @@ async def stop(self):
except TimeoutError:
self.server.kill()

args = {}
args["msg"] = "lsp-devtools agent is stopping."

# Cancel the tasks connecting client to server
for task in self._tasks:
task.cancel(**args)
logger.debug("cancelling: %s", task)
task.cancel(msg="lsp-devtools agent is stopping.")

if self.writer:
self.writer.close()
96 changes: 16 additions & 80 deletions lib/lsp-devtools/lsp_devtools/agent/client.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
from __future__ import annotations

import asyncio
import inspect
import typing

import stamina
from pygls.client import JsonRPCClient
from pygls.client import aio_readline
from pygls.protocol import default_converter

from lsp_devtools.agent.protocol import AgentProtocol

if typing.TYPE_CHECKING:
from typing import Any

# from websockets.client import WebSocketClientProtocol


# class WebSocketClientTransportAdapter:
# """Protocol adapter for the WebSocket client interface."""

# def __init__(self, ws: WebSocketClientProtocol, loop: asyncio.AbstractEventLoop):
# self._ws = ws
# self._loop = loop

# def close(self) -> None:
# """Stop the WebSocket server."""
# print("-- CLOSING --")
# self._loop.create_task(self._ws.close())

# def write(self, data: Any) -> None:
# """Create a task to write specified data into a WebSocket."""
# asyncio.ensure_future(self._ws.send(data))


class AgentClient(JsonRPCClient):
"""Client for connecting to an AgentServer instance."""
Expand All @@ -44,6 +25,7 @@ def __init__(self):
)
self.connected = False
self._buffer: list[bytes] = []
self._tasks: set[asyncio.Task[Any]] = set()

def _report_server_error(self, error, source):
# Bail on error
Expand All @@ -53,7 +35,6 @@ def _report_server_error(self, error, source):
def feature(self, feature_name: str, options: Any | None = None):
return self.protocol.fm.feature(feature_name, options)

# TODO: Upstream this... or at least something equivalent.
async def start_tcp(self, host: str, port: int):
# The user might not have started the server app immediately and since the
# agent will live as long as the wrapper language server we may as well
Expand All @@ -67,71 +48,26 @@ async def start_tcp(self, host: str, port: int):
)
async for attempt in retries:
with attempt:
reader, writer = await asyncio.open_connection(host, port)

self.protocol.connection_made(writer) # type: ignore[arg-type]
connection = asyncio.create_task(
aio_readline(self._stop_event, reader, self.protocol.data_received)
)
self.connected = True
self._async_tasks.append(connection)
await super().start_tcp(host, port)
self.connected = True

def forward_message(self, message: bytes):
"""Forward the given message to the server instance."""

if not self.connected:
if not self.connected or self.protocol.writer is None:
self._buffer.append(message)
return

if self.protocol.transport is None:
return

# Send any buffered messages
while len(self._buffer) > 0:
self.protocol.transport.write(self._buffer.pop(0))

self.protocol.transport.write(message)

# TODO: Upstream this... or at least something equivalent.
# def start_ws(self, host: str, port: int):
# self.protocol._send_only_body = True # Don't send headers within the payload

# async def client_connection(host: str, port: int):
# """Create and run a client connection."""

# self._client = await websockets.connect( # type: ignore
# f"ws://{host}:{port}"
# )
# loop = asyncio.get_running_loop()
# self.protocol.transport = WebSocketClientTransportAdapter(
# self._client, loop
# )
# message = None

# try:
# while not self._stop_event.is_set():
# try:
# message = await asyncio.wait_for(
# self._client.recv(), timeout=0.5
# )
# self.protocol._procedure_handler(
# json.loads(
# message,
# object_hook=self.protocol._deserialize_message
# )
# )
# except JSONDecodeError:
# print(message or "-- message not found --")
# raise
# except TimeoutError:
# pass
# except Exception:
# raise

# finally:
# await self._client.close()

# try:
# asyncio.run(client_connection(host, port))
# except KeyboardInterrupt:
# pass
res = self.protocol.writer.write(self._buffer.pop(0))
if inspect.isawaitable(res):
task = asyncio.ensure_future(res)
task.add_done_callback(self._tasks.discard)
self._tasks.add(task)

res = self.protocol.writer.write(message)
if inspect.isawaitable(res):
task = asyncio.ensure_future(res)
task.add_done_callback(self._tasks.discard)
self._tasks.add(task)
25 changes: 17 additions & 8 deletions lib/lsp-devtools/lsp_devtools/agent/server.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from __future__ import annotations

import asyncio
import json
import logging
import traceback
import typing

from pygls.protocol import default_converter
from pygls.server import Server
from pygls.server import JsonRPCServer

from lsp_devtools.agent.agent import aio_readline
from lsp_devtools.agent.protocol import AgentProtocol
Expand All @@ -18,7 +19,7 @@
from lsp_devtools.agent.agent import MessageHandler


class AgentServer(Server):
class AgentServer(JsonRPCServer):
"""A pygls server that accepts connections from agents allowing them to send their
collected messages."""

Expand All @@ -40,25 +41,33 @@ def __init__(
super().__init__(*args, **kwargs)

self.logger = logger or logging.getLogger(__name__)
self.handler = handler or self.lsp.data_received
self.handler = handler or self._default_handler
self.db: Database | None = None

self._client_buffer: list[str] = []
self._server_buffer: list[str] = []
self._tcp_server: asyncio.Task | None = None

def _report_server_error(self, exc: Exception, source):
def _default_handler(self, data: bytes):
message = self.protocol.structure_message(json.loads(data))
self.protocol.handle_message(message)

def _report_server_error(self, error: Exception, source):
"""Report internal server errors."""
tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
self.logger.error("%s: %s", type(exc).__name__, exc)
tb = "".join(
traceback.format_exception(type(error), error, error.__traceback__)
)
self.logger.error("%s: %s", type(error).__name__, error)
self.logger.debug("%s", tb)

def feature(self, feature_name: str, options: Any | None = None):
return self.lsp.fm.feature(feature_name, options)

async def start_tcp(self, host: str, port: int) -> None: # type: ignore[override]
async def handle_client(reader, writer):
self.lsp.connection_made(writer)
async def handle_client(
reader: asyncio.StreamReader, writer: asyncio.StreamWriter
):
self.protocol.set_writer(writer)

try:
await aio_readline(reader, self.handler)
Expand Down
2 changes: 1 addition & 1 deletion lib/lsp-devtools/lsp_devtools/client/editor/text_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def edit(self, edit):
version=self.version, uri=self.uri
),
content_changes=[
types.TextDocumentContentChangeEvent_Type1(
types.TextDocumentContentChangePartial(
text=edit.text,
range=types.Range(
start=types.Position(line=start_line, character=start_col),
Expand Down
5 changes: 0 additions & 5 deletions lib/lsp-devtools/lsp_devtools/inspector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ class LSPInspector(App):
BINDINGS = [
("ctrl+b", "toggle_sidebar", "Sidebar"),
("ctrl+c", "quit", "Quit"),
("ctrl+s", "screenshot", "Take Screenshot"),
]

def __init__(self, db: Database, server: AgentServer, *args, **kwargs):
Expand All @@ -169,10 +168,6 @@ def compose(self) -> ComposeResult:
yield Container(ScrollableContainer(messages), Sidebar(viewer))
yield Footer()

def action_screenshot(self):
self.bell()
self.save_screenshot(None, "./")

def action_toggle_sidebar(self) -> None:
sidebar = self.query_one(Sidebar)
self.set_focus(None)
Expand Down
2 changes: 1 addition & 1 deletion lib/lsp-devtools/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ classifiers = [
dependencies = [
"aiosqlite",
"platformdirs",
"pygls>=1.1.0,<2",
"pygls>=2.0a2",
"stamina",
"textual>=0.41.0",
]
Expand Down
6 changes: 3 additions & 3 deletions lib/lsp-devtools/ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ ignore = [
"SIM115", # Use key in dict
"SIM118", # Use key in dict
"SLF001", # private member access
"TCH001", # move import to type checking block
"TCH002", # move import to type checking block
"TCH003", # move import to type checking block
"TC001", # move import to type checking block
"TC002", # move import to type checking block
"TC003", # move import to type checking block
"TID252", # Absolute vs relative imports
"TRY300", # Move statement to else block
]
Expand Down
17 changes: 7 additions & 10 deletions lib/lsp-devtools/ruff_defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ select = [
"E742",
"E743",
"E902",
"E999",
"EM101",
"EM102",
"EM103",
Expand Down Expand Up @@ -244,7 +243,6 @@ select = [
"PLR0133",
"PLR0206",
"PLR0402",
"PLR1701",
"PLR1711",
"PLR1714",
"PLR1722",
Expand Down Expand Up @@ -452,12 +450,12 @@ select = [
"T100",
"T201",
"T203",
"TCH001",
"TCH002",
"TCH003",
"TCH004",
"TCH005",
"TCH010",
"TC001",
"TC002",
"TC003",
"TC004",
"TC005",
"TC010",
"TD004",
"TD005",
"TD006",
Expand All @@ -469,9 +467,9 @@ select = [
"TRY003",
"TRY004",
"TRY201",
"TRY203",
"TRY300",
"TRY301",
"TRY302",
"TRY400",
"TRY401",
"UP001",
Expand All @@ -498,7 +496,6 @@ select = [
"UP024",
"UP025",
"UP026",
"UP027",
"UP028",
"UP029",
"UP030",
Expand Down
Loading