You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			164 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
import json
 | 
						|
import os
 | 
						|
import pathlib
 | 
						|
import shutil
 | 
						|
from pathlib import Path
 | 
						|
from typing import Text
 | 
						|
 | 
						|
from jupyter_server.serverapp import ServerApp
 | 
						|
from pytest import fixture
 | 
						|
from tornado.httpserver import HTTPRequest
 | 
						|
from tornado.httputil import HTTPServerRequest
 | 
						|
from tornado.queues import Queue
 | 
						|
from tornado.web import Application
 | 
						|
 | 
						|
# local imports
 | 
						|
from jupyter_lsp import LanguageServerManager
 | 
						|
from jupyter_lsp.constants import APP_CONFIG_D_SECTIONS
 | 
						|
from jupyter_lsp.handlers import LanguageServersHandler, LanguageServerWebSocketHandler
 | 
						|
 | 
						|
# these should always be available in a test environment
 | 
						|
KNOWN_SERVERS = [
 | 
						|
    "bash-language-server",
 | 
						|
    "dockerfile-language-server-nodejs",
 | 
						|
    "typescript-language-server",
 | 
						|
    "pylsp",
 | 
						|
    "unified-language-server",
 | 
						|
    "sql-language-server",
 | 
						|
    "vscode-css-languageserver-bin",
 | 
						|
    "vscode-html-languageserver-bin",
 | 
						|
    "vscode-json-languageserver-bin",
 | 
						|
    "yaml-language-server",
 | 
						|
]
 | 
						|
 | 
						|
CMD_BASED_SERVERS = {
 | 
						|
    "Rscript": ["r-languageserver"],
 | 
						|
    "texlab": ["texlab"],
 | 
						|
    "jedi-language-server": ["jedi-language-server"],
 | 
						|
    "julia": ["julia-language-server"],
 | 
						|
}
 | 
						|
 | 
						|
KNOWN_SERVERS += sum(
 | 
						|
    [langs for cmd, langs in CMD_BASED_SERVERS.items() if shutil.which(cmd)], []
 | 
						|
)
 | 
						|
 | 
						|
KNOWN_UNKNOWN_SERVERS = ["foo-language-server"]
 | 
						|
 | 
						|
 | 
						|
def extra_node_roots():
 | 
						|
    root = Path(os.environ.get("JLSP_TEST_ROOT") or Path.cwd())
 | 
						|
    return dict(extra_node_roots=[str(root)] if root else [])
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def manager() -> LanguageServerManager:
 | 
						|
    return LanguageServerManager(**extra_node_roots())
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def echo_spec():
 | 
						|
    return {"argv": ["echo", "no server here"], "languages": ["klingon"], "version": 2}
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def echo_conf_json(echo_spec) -> str:
 | 
						|
    return json.dumps(
 | 
						|
        {"LanguageServerManager": {"language_servers": {"_echo_": echo_spec}}},
 | 
						|
        indent=2,
 | 
						|
        sort_keys=True,
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
@fixture(params=sorted(APP_CONFIG_D_SECTIONS))
 | 
						|
def app_config_d(request, tmp_path, monkeypatch) -> pathlib.Path:
 | 
						|
    conf_d = tmp_path / f"jupyter{request.param}config.d"
 | 
						|
    conf_d.mkdir()
 | 
						|
    monkeypatch.setenv("JUPYTER_CONFIG_PATH", f"{tmp_path}")
 | 
						|
    return conf_d
 | 
						|
 | 
						|
 | 
						|
@fixture(params=sorted(KNOWN_SERVERS))
 | 
						|
def known_server(request):
 | 
						|
    return request.param
 | 
						|
 | 
						|
 | 
						|
@fixture(params=sorted(KNOWN_UNKNOWN_SERVERS))
 | 
						|
def known_unknown_server(request):
 | 
						|
    return request.param
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def handlers(manager):
 | 
						|
    ws_handler = MockWebsocketHandler()
 | 
						|
    ws_handler.initialize(manager)
 | 
						|
    handler = MockHandler()
 | 
						|
    handler.initialize(manager)
 | 
						|
    return handler, ws_handler
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def jsonrpc_init_msg():
 | 
						|
    return json.dumps(
 | 
						|
        {
 | 
						|
            "id": 0,
 | 
						|
            "jsonrpc": "2.0",
 | 
						|
            "method": "initialize",
 | 
						|
            "params": {
 | 
						|
                "capabilities": {
 | 
						|
                    # see: https://github.com/julia-vscode/LanguageServer.jl/issues/1008
 | 
						|
                    # LanguageServer.jl assumes that it is not missing
 | 
						|
                    "workspace": {"didChangeConfiguration": {}},
 | 
						|
                    # LanguageServer.jl assumes that it is not missing
 | 
						|
                    "textDocument": {},
 | 
						|
                },
 | 
						|
                "initializationOptions": None,
 | 
						|
                "processId": None,
 | 
						|
                "rootUri": pathlib.Path(__file__).parent.as_uri(),
 | 
						|
                "workspaceFolders": None,
 | 
						|
            },
 | 
						|
        }
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
@fixture
 | 
						|
def app():
 | 
						|
    return MockServerApp()
 | 
						|
 | 
						|
 | 
						|
# mocks
 | 
						|
class MockWebsocketHandler(LanguageServerWebSocketHandler):
 | 
						|
    _messages_wrote = None  # type: Queue
 | 
						|
    _ping_sent = None  # type: bool
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        self.request = HTTPServerRequest()
 | 
						|
        self.application = Application()
 | 
						|
 | 
						|
    def initialize(self, manager):
 | 
						|
        super().initialize(manager)
 | 
						|
        self._messages_wrote = Queue()
 | 
						|
        self._ping_sent = False
 | 
						|
 | 
						|
    def write_message(self, message: Text) -> None:  # type: ignore
 | 
						|
        self.log.warning("write_message %s", message)
 | 
						|
        self._messages_wrote.put_nowait(message)
 | 
						|
 | 
						|
    def send_ping(self):
 | 
						|
        self._ping_sent = True
 | 
						|
 | 
						|
 | 
						|
class MockHandler(LanguageServersHandler):
 | 
						|
    _payload = None
 | 
						|
    _jupyter_current_user = "foo"  # type:ignore[assignment]
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        self.request = HTTPRequest("GET")
 | 
						|
        self.application = Application()
 | 
						|
 | 
						|
    def finish(self, payload):
 | 
						|
        self._payload = payload
 | 
						|
 | 
						|
 | 
						|
class MockServerApp(ServerApp):
 | 
						|
    pass
 |