diff --git a/pyls/dispatcher.py b/pyls/dispatcher.py new file mode 100644 index 00000000..0711045f --- /dev/null +++ b/pyls/dispatcher.py @@ -0,0 +1,30 @@ +# Copyright 2018 Palantir Technologies, Inc. +import re + +from .rpc_manager import MissingMethodException + +_RE_FIRST_CAP = re.compile('(.)([A-Z][a-z]+)') +_RE_ALL_CAP = re.compile('([a-z0-9])([A-Z])') + + +class JSONRPCMethodDispatcher(object): + """JSON RPC method dispatcher that calls methods on itself with params.""" + + def __init__(self, delegate): + self._delegate = delegate + + def __call__(self, method, params): + method_name = "m_" + _method_to_string(method) + if not hasattr(self._delegate, method_name): + raise MissingMethodException(method_name) + + return getattr(self._delegate, method_name)(**params) + + +def _method_to_string(method): + return _camel_to_underscore(method.replace("/", "__").replace("$", "")) + + +def _camel_to_underscore(string): + s1 = _RE_FIRST_CAP.sub(r'\1_\2', string) + return _RE_ALL_CAP.sub(r'\1_\2', s1).lower() diff --git a/pyls/python_ls.py b/pyls/python_ls.py index e1c9297f..54b28826 100644 --- a/pyls/python_ls.py +++ b/pyls/python_ls.py @@ -1,19 +1,16 @@ # Copyright 2017 Palantir Technologies, Inc. import logging import socketserver -import re from . import lsp, _utils, uris from .config import config +from .dispatcher import JSONRPCMethodDispatcher from .json_rpc_server import JSONRPCServer from .rpc_manager import JSONRPCManager, MissingMethodException from .workspace import Workspace log = logging.getLogger(__name__) -_RE_FIRST_CAP = re.compile('(.)([A-Z][a-z]+)') -_RE_ALL_CAP = re.compile('([a-z0-9])([A-Z])') - LINT_DEBOUNCE_S = 0.5 # 500 ms @@ -77,28 +74,13 @@ def start(self): self.rpc_manager.start() def handle_request(self, method, params): - """Provides callables to handle requests or responses to those reqeuests - - Args: - method (str): name of the message - params (dict): body of the message - - Returns: - Callable if method is to be handled - - Raises: - KeyError: Handler for method is not found - """ - - method_call = 'm_{}'.format(_method_to_string(method)) - if hasattr(self, method_call): - return getattr(self, method_call)(**params) - elif self._dispatchers: - for dispatcher in self._dispatchers: - if method_call in dispatcher: - return dispatcher[method_call](**params) - - raise MissingMethodException('No handler for for method {}'.format(method)) + """Handles a method request by calling ourselves then falling back through registered dispatchers.""" + for dispatcher in [JSONRPCMethodDispatcher(self)] + self._dispatchers: + try: + return dispatcher(method, params) + except MissingMethodException: + pass + raise MissingMethodException(method) def m__cancel_request(self, **kwargs): self.rpc_manager.cancel(kwargs['id']) @@ -277,15 +259,6 @@ def m_workspace__execute_command(self, command=None, arguments=None): return self.execute_command(command, arguments) -def _method_to_string(method): - return _camel_to_underscore(method.replace("/", "__").replace("$", "")) - - -def _camel_to_underscore(string): - s1 = _RE_FIRST_CAP.sub(r'\1_\2', string) - return _RE_ALL_CAP.sub(r'\1_\2', s1).lower() - - def flatten(list_of_lists): return [item for lst in list_of_lists for item in lst] diff --git a/pyls/rpc_manager.py b/pyls/rpc_manager.py index 3015a8a3..82b39d3c 100644 --- a/pyls/rpc_manager.py +++ b/pyls/rpc_manager.py @@ -19,7 +19,8 @@ class MissingMethodException(Exception): - pass + def __init__(self, method): + super(MissingMethodException, self).__init__("No such method '%s'" % method) class JSONRPCManager(object): @@ -113,7 +114,7 @@ def _handle_request(self, request): output = None try: - maybe_handler = self._message_handler(request.method, request.params if request.params is not None else {}) + maybe_handler = self._message_handler(request.method, request.params or {}) except MissingMethodException as e: log.debug(e) # Do not need to notify client of failure with notifications diff --git a/test/test_rpc_manager.py b/test/test_rpc_manager.py index 123805d8..066f0435 100644 --- a/test/test_rpc_manager.py +++ b/test/test_rpc_manager.py @@ -82,7 +82,7 @@ def wrapper(): def test_handle_request_unknown_method(rpc_management): rpc_manager, message_manager, message_handler = rpc_management - message_handler.configure_mock(side_effect=MissingMethodException) + message_handler.configure_mock(side_effect=MissingMethodException('test')) rpc_manager.start() message_manager.get_messages.assert_any_call()