From ea43ead5f431bdb0dd1cd154da93ce75fafe8906 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Fri, 1 Jun 2018 12:22:40 +0100 Subject: [PATCH 1/2] Preload modules configurable --- pyls/jsonrpc/__init__.py | 0 pyls/jsonrpc/dispatchers.py | 35 ---- pyls/jsonrpc/endpoint.py | 237 ------------------------- pyls/jsonrpc/exceptions.py | 110 ------------ pyls/jsonrpc/streams.py | 102 ----------- setup.py | 1 + test/jsonrpc/__init__.py | 0 test/jsonrpc/test_endpoint.py | 318 ---------------------------------- test/jsonrpc/test_streams.py | 92 ---------- test/test_language_server.py | 4 +- 10 files changed, 4 insertions(+), 895 deletions(-) delete mode 100644 pyls/jsonrpc/__init__.py delete mode 100644 pyls/jsonrpc/dispatchers.py delete mode 100644 pyls/jsonrpc/endpoint.py delete mode 100644 pyls/jsonrpc/exceptions.py delete mode 100644 pyls/jsonrpc/streams.py delete mode 100644 test/jsonrpc/__init__.py delete mode 100644 test/jsonrpc/test_endpoint.py delete mode 100644 test/jsonrpc/test_streams.py diff --git a/pyls/jsonrpc/__init__.py b/pyls/jsonrpc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pyls/jsonrpc/dispatchers.py b/pyls/jsonrpc/dispatchers.py deleted file mode 100644 index 22c8a3b5..00000000 --- a/pyls/jsonrpc/dispatchers.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -import functools -import re - -_RE_FIRST_CAP = re.compile('(.)([A-Z][a-z]+)') -_RE_ALL_CAP = re.compile('([a-z0-9])([A-Z])') - - -class MethodDispatcher(object): - """JSON RPC dispatcher that calls methods on itself. - - Method names are computed by converting camel case to snake case, slashes with double underscores, and removing - dollar signs. - """ - - def __getitem__(self, item): - method_name = 'm_{}'.format(_method_to_string(item)) - if hasattr(self, method_name): - method = getattr(self, method_name) - - @functools.wraps(method) - def handler(params): - return method(**(params or {})) - - return handler - raise KeyError() - - -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/jsonrpc/endpoint.py b/pyls/jsonrpc/endpoint.py deleted file mode 100644 index 0b16506d..00000000 --- a/pyls/jsonrpc/endpoint.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -import logging -import uuid -import sys - -from concurrent import futures -from .exceptions import JsonRpcException, JsonRpcRequestCancelled, JsonRpcInternalError, JsonRpcMethodNotFound - -log = logging.getLogger(__name__) -JSONRPC_VERSION = '2.0' -CANCEL_METHOD = '$/cancelRequest' - - -class Endpoint(object): - - def __init__(self, dispatcher, consumer, id_generator=lambda: str(uuid.uuid4()), max_workers=5): - """A JSON RPC endpoint for managing messages sent to/from the client. - - Args: - dispatcher (dict): A dictionary of method name to handler function. - The handler functions should return either the result or a callable that will be used to asynchronously - compute the result. - consumer (fn): A function that consumes JSON RPC message dicts and sends them to the client. - id_generator (fn, optional): A function used to generate request IDs. - Defaults to the string value of :func:`uuid.uuid4`. - max_workers (int, optional): The number of workers in the asynchronous executor pool. - """ - self._dispatcher = dispatcher - self._consumer = consumer - self._id_generator = id_generator - - self._client_request_futures = {} - self._server_request_futures = {} - self._executor_service = futures.ThreadPoolExecutor(max_workers=max_workers) - - def shutdown(self): - self._executor_service.shutdown() - - def notify(self, method, params=None): - """Send a JSON RPC notification to the client. - - Args: - method (str): The method name of the notification to send - params (any): The payload of the notification - """ - log.debug('Sending notification: %s %s', method, params) - - message = { - 'jsonrpc': JSONRPC_VERSION, - 'method': method, - } - if params is not None: - message['params'] = params - - self._consumer(message) - - def request(self, method, params=None): - """Send a JSON RPC request to the client. - - Args: - method (str): The method name of the message to send - params (any): The payload of the message - - Returns: - Future that will resolve once a response has been received - """ - msg_id = self._id_generator() - log.debug('Sending request with id %s: %s %s', msg_id, method, params) - - message = { - 'jsonrpc': JSONRPC_VERSION, - 'id': msg_id, - 'method': method, - } - if params is not None: - message['params'] = params - - request_future = futures.Future() - request_future.add_done_callback(self._cancel_callback(msg_id)) - - self._server_request_futures[msg_id] = request_future - self._consumer(message) - - return request_future - - def _cancel_callback(self, request_id): - """Construct a cancellation callback for the given request ID.""" - def callback(future): - if future.cancelled(): - self.notify(CANCEL_METHOD, {'id': request_id}) - future.set_exception(JsonRpcRequestCancelled()) - return callback - - def consume(self, message): - """Consume a JSON RPC message from the client. - - Args: - message (dict): The JSON RPC message sent by the client - """ - if 'jsonrpc' not in message or message['jsonrpc'] != JSONRPC_VERSION: - log.warn("Unknown message type %s", message) - return - - if 'id' not in message: - log.debug("Handling notification from client %s", message) - self._handle_notification(message['method'], message.get('params')) - elif 'method' not in message: - log.debug("Handling response from client %s", message) - self._handle_response(message['id'], message.get('result'), message.get('error')) - else: - try: - log.debug("Handling request from client %s", message) - self._handle_request(message['id'], message['method'], message.get('params')) - except JsonRpcException as e: - log.exception("Failed to handle request %s", message['id']) - self._consumer({ - 'jsonrpc': JSONRPC_VERSION, - 'id': message['id'], - 'error': e.to_dict() - }) - except Exception: # pylint: disable=broad-except - log.exception("Failed to handle request %s", message['id']) - self._consumer({ - 'jsonrpc': JSONRPC_VERSION, - 'id': message['id'], - 'error': JsonRpcInternalError.of(sys.exc_info()).to_dict() - }) - - def _handle_notification(self, method, params): - """Handle a notification from the client.""" - if method == CANCEL_METHOD: - self._handle_cancel_notification(params['id']) - return - - try: - handler = self._dispatcher[method] - except KeyError: - log.warn("Ignoring notification for unknown method %s", method) - return - - try: - handler_result = handler(params) - except Exception: # pylint: disable=broad-except - log.exception("Failed to handle notification %s: %s", method, params) - return - - if callable(handler_result): - log.debug("Executing async notification handler %s", handler_result) - notification_future = self._executor_service.submit(handler_result) - notification_future.add_done_callback(self._notification_callback(method, params)) - - @staticmethod - def _notification_callback(method, params): - """Construct a notification callback for the given request ID.""" - def callback(future): - try: - future.result() - log.debug("Successfully handled async notification %s %s", method, params) - except Exception: # pylint: disable=broad-except - log.exception("Failed to handle async notification %s %s", method, params) - return callback - - def _handle_cancel_notification(self, msg_id): - """Handle a cancel notification from the client.""" - request_future = self._client_request_futures.pop(msg_id, None) - - if not request_future: - log.warn("Received cancel notification for unknown message id %s", msg_id) - return - - # Will only work if the request hasn't started executing - if request_future.cancel(): - log.debug("Cancelled request with id %s", msg_id) - - def _handle_request(self, msg_id, method, params): - """Handle a request from the client.""" - try: - handler = self._dispatcher[method] - except KeyError: - raise JsonRpcMethodNotFound.of(method) - - handler_result = handler(params) - - if callable(handler_result): - log.debug("Executing async request handler %s", handler_result) - request_future = self._executor_service.submit(handler_result) - self._client_request_futures[msg_id] = request_future - request_future.add_done_callback(self._request_callback(msg_id)) - else: - log.debug("Got result from synchronous request handler: %s", handler_result) - self._consumer({ - 'jsonrpc': JSONRPC_VERSION, - 'id': msg_id, - 'result': handler_result - }) - - def _request_callback(self, request_id): - """Construct a request callback for the given request ID.""" - def callback(future): - # Remove the future from the client requests map - self._client_request_futures.pop(request_id, None) - - if future.cancelled(): - future.set_exception(JsonRpcRequestCancelled()) - - message = { - 'jsonrpc': JSONRPC_VERSION, - 'id': request_id, - } - - try: - message['result'] = future.result() - except JsonRpcException as e: - log.exception("Failed to handle request %s", request_id) - message['error'] = e.to_dict() - except Exception: # pylint: disable=broad-except - log.exception("Failed to handle request %s", request_id) - message['error'] = JsonRpcInternalError.of(sys.exc_info()).to_dict() - - self._consumer(message) - - return callback - - def _handle_response(self, msg_id, result=None, error=None): - """Handle a response from the client.""" - request_future = self._server_request_futures.pop(msg_id, None) - - if not request_future: - log.warn("Received response to unknown message id %s", msg_id) - return - - if error is not None: - log.debug("Received error response to message %s: %s", msg_id, error) - request_future.set_exception(JsonRpcException.from_dict(error)) - - log.debug("Received result for message %s: %s", msg_id, result) - request_future.set_result(result) diff --git a/pyls/jsonrpc/exceptions.py b/pyls/jsonrpc/exceptions.py deleted file mode 100644 index f4f9c2e9..00000000 --- a/pyls/jsonrpc/exceptions.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -import traceback - - -class JsonRpcException(Exception): - - def __init__(self, message=None, code=None, data=None): - super(JsonRpcException, self).__init__() - self.message = message or getattr(self.__class__, 'MESSAGE') - self.code = code or getattr(self.__class__, 'CODE') - self.data = data - - def to_dict(self): - exception_dict = { - 'code': self.code, - 'message': self.message, - } - if self.data is not None: - exception_dict['data'] = self.data - return exception_dict - - def __eq__(self, other): - return ( - isinstance(other, self.__class__) and - self.code == other.code and - self.message == other.message - ) - - def __hash__(self): - return hash((self.code, self.message)) - - @staticmethod - def from_dict(error): - for exc_class in _EXCEPTIONS: - if exc_class.supports_code(error['code']): - return exc_class(**error) - return JsonRpcException(**error) - - @classmethod - def supports_code(cls, code): - # Defaults to UnknownErrorCode - return getattr(cls, 'CODE', -32001) == code - - -class JsonRpcParseError(JsonRpcException): - CODE = -32700 - MESSAGE = 'Parse Error' - - -class JsonRpcInvalidRequest(JsonRpcException): - CODE = -32600 - MESSAGE = 'Invalid Request' - - -class JsonRpcMethodNotFound(JsonRpcException): - CODE = -32601 - MESSAGE = 'Method Not Found' - - @classmethod - def of(cls, method): - return cls(message=cls.MESSAGE + ': ' + method) - - -class JsonRpcInvalidParams(JsonRpcException): - CODE = -32602 - MESSAGE = 'Invalid Params' - - -class JsonRpcInternalError(JsonRpcException): - CODE = -32602 - MESSAGE = 'Internal Error' - - @classmethod - def of(cls, exc_info): - exc_type, exc_value, exc_tb = exc_info - return cls( - message=''.join(traceback.format_exception_only(exc_type, exc_value)).strip(), - data={'traceback': traceback.format_tb(exc_tb)} - ) - - -class JsonRpcRequestCancelled(JsonRpcException): - CODE = -32800 - MESSAGE = 'Request Cancelled' - - -class JsonRpcServerError(JsonRpcException): - - def __init__(self, message, code, data=None): - assert _is_server_error_code(code) - super(JsonRpcServerError, self).__init__(message=message, code=code, data=data) - - @classmethod - def supports_code(cls, code): - return _is_server_error_code(code) - - -def _is_server_error_code(code): - return -32099 <= code <= -32000 - - -_EXCEPTIONS = ( - JsonRpcParseError, - JsonRpcInvalidRequest, - JsonRpcMethodNotFound, - JsonRpcInvalidParams, - JsonRpcInternalError, - JsonRpcRequestCancelled, - JsonRpcServerError, -) diff --git a/pyls/jsonrpc/streams.py b/pyls/jsonrpc/streams.py deleted file mode 100644 index 10f23cbd..00000000 --- a/pyls/jsonrpc/streams.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -import json -import logging -import threading - -log = logging.getLogger(__name__) - - -class JsonRpcStreamReader(object): - - def __init__(self, rfile): - self._rfile = rfile - - def close(self): - self._rfile.close() - - def listen(self, message_consumer): - """Blocking call to listen for messages on the rfile. - - Args: - message_consumer (fn): function that is passed each message as it is read off the socket. - """ - while not self._rfile.closed: - request_str = self._read_message() - - if request_str is None: - break - - try: - message_consumer(json.loads(request_str.decode('utf-8'))) - except ValueError: - log.exception("Failed to parse JSON message %s", request_str) - continue - - def _read_message(self): - """Reads the contents of a message. - - Returns: - body of message if parsable else None - """ - line = self._rfile.readline() - - if not line: - return None - - content_length = self._content_length(line) - - # Blindly consume all header lines - while line and line.strip(): - line = self._rfile.readline() - - if not line: - return None - - # Grab the body - return self._rfile.read(content_length) - - @staticmethod - def _content_length(line): - """Extract the content length from an input line.""" - if line.startswith(b'Content-Length: '): - _, value = line.split(b'Content-Length: ') - value = value.strip() - try: - return int(value) - except ValueError: - raise ValueError("Invalid Content-Length header: {}".format(value)) - - return None - - -class JsonRpcStreamWriter(object): - - def __init__(self, wfile, **json_dumps_args): - self._wfile = wfile - self._wfile_lock = threading.Lock() - self._json_dumps_args = json_dumps_args - - def close(self): - with self._wfile_lock: - self._wfile.close() - - def write(self, message): - with self._wfile_lock: - if self._wfile.closed: - return - try: - body = json.dumps(message, **self._json_dumps_args) - - # Ensure we get the byte length, not the character length - content_length = len(body) if isinstance(body, bytes) else len(body.encode('utf-8')) - - response = ( - "Content-Length: {}\r\n" - "Content-Type: application/vscode-jsonrpc; charset=utf8\r\n\r\n" - "{}".format(content_length, body) - ) - - self._wfile.write(response.encode('utf-8')) - self._wfile.flush() - except Exception: # pylint: disable=broad-except - log.exception("Failed to write message to output file %s", message) diff --git a/setup.py b/setup.py index 4f40cb93..7f1fe40c 100755 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ 'future>=0.14.0', 'futures; python_version<"3.2"', 'jedi>=0.12', + 'python-jsonrpc-server', 'pluggy' ], diff --git a/test/jsonrpc/__init__.py b/test/jsonrpc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/jsonrpc/test_endpoint.py b/test/jsonrpc/test_endpoint.py deleted file mode 100644 index 533e0a0b..00000000 --- a/test/jsonrpc/test_endpoint.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -# pylint: disable=redefined-outer-name -import time -import mock -import pytest - -from pyls.jsonrpc import exceptions -from pyls.jsonrpc.endpoint import Endpoint - -MSG_ID = 'id' - - -@pytest.fixture() -def dispatcher(): - return {} - - -@pytest.fixture() -def consumer(): - return mock.MagicMock() - - -@pytest.fixture() -def endpoint(dispatcher, consumer): - return Endpoint(dispatcher, consumer, id_generator=lambda: MSG_ID) - - -def test_bad_message(endpoint): - # Ensure doesn't raise for a bad message - endpoint.consume({'key': 'value'}) - - -def test_notify(endpoint, consumer): - endpoint.notify('methodName', {'key': 'value'}) - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - -def test_notify_none_params(endpoint, consumer): - endpoint.notify('methodName', None) - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'method': 'methodName', - }) - - -def test_request(endpoint, consumer): - future = endpoint.request('methodName', {'key': 'value'}) - assert not future.done() - - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - # Send the response back to the endpoint - result = 1234 - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'result': result - }) - - assert future.result(timeout=2) == result - - -def test_request_error(endpoint, consumer): - future = endpoint.request('methodName', {'key': 'value'}) - assert not future.done() - - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - # Send an error back from the client - error = exceptions.JsonRpcInvalidRequest(data=1234) - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'error': error.to_dict() - }) - - # Verify the exception raised by the future is the same as the error the client serialized - with pytest.raises(exceptions.JsonRpcException) as exc_info: - assert future.result(timeout=2) - assert exc_info.type == exceptions.JsonRpcInvalidRequest - assert exc_info.value == error - - -def test_request_cancel(endpoint, consumer): - future = endpoint.request('methodName', {'key': 'value'}) - assert not future.done() - - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - # Cancel the request - future.cancel() - consumer.assert_any_call({ - 'jsonrpc': '2.0', - 'method': '$/cancelRequest', - 'params': {'id': MSG_ID} - }) - - with pytest.raises(exceptions.JsonRpcException) as exc_info: - assert future.result(timeout=2) - assert exc_info.type == exceptions.JsonRpcRequestCancelled - - -def test_consume_notification(endpoint, dispatcher): - handler = mock.Mock() - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': 'methodName', - 'params': {'key': 'value'} - }) - handler.assert_called_once_with({'key': 'value'}) - - -def test_consume_notification_error(endpoint, dispatcher): - handler = mock.Mock(side_effect=ValueError) - dispatcher['methodName'] = handler - # Verify the consume doesn't throw - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': 'methodName', - 'params': {'key': 'value'} - }) - handler.assert_called_once_with({'key': 'value'}) - - -def test_consume_notification_method_not_found(endpoint): - # Verify consume doesn't throw for method not found - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - -def test_consume_async_notification_error(endpoint, dispatcher): - def _async_handler(): - raise ValueError() - handler = mock.Mock(return_value=_async_handler) - dispatcher['methodName'] = handler - - # Verify the consume doesn't throw - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': 'methodName', - 'params': {'key': 'value'} - }) - handler.assert_called_once_with({'key': 'value'}) - - -def test_consume_request(endpoint, consumer, dispatcher): - result = 1234 - handler = mock.Mock(return_value=result) - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - handler.assert_called_once_with({'key': 'value'}) - consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'result': result - }) - - -def test_consume_async_request(endpoint, consumer, dispatcher): - def _async_handler(): - return 1234 - handler = mock.Mock(return_value=_async_handler) - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - handler.assert_called_once_with({'key': 'value'}) - await_assertion(lambda: consumer.assert_called_once_with({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'result': 1234 - })) - - -@pytest.mark.parametrize('exc_type, error', [ - (ValueError, exceptions.JsonRpcInternalError(message='ValueError')), - (KeyError, exceptions.JsonRpcInternalError(message='KeyError')), - (exceptions.JsonRpcMethodNotFound, exceptions.JsonRpcMethodNotFound()), -]) -def test_consume_async_request_error(exc_type, error, endpoint, consumer, dispatcher): - def _async_handler(): - raise exc_type() - handler = mock.Mock(return_value=_async_handler) - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - handler.assert_called_once_with({'key': 'value'}) - await_assertion(lambda: assert_consumer_error(consumer, error)) - - -def test_consume_request_method_not_found(endpoint, consumer): - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - assert_consumer_error(consumer, exceptions.JsonRpcMethodNotFound.of('methodName')) - - -@pytest.mark.parametrize('exc_type, error', [ - (ValueError, exceptions.JsonRpcInternalError(message='ValueError')), - (KeyError, exceptions.JsonRpcInternalError(message='KeyError')), - (exceptions.JsonRpcMethodNotFound, exceptions.JsonRpcMethodNotFound()), -]) -def test_consume_request_error(exc_type, error, endpoint, consumer, dispatcher): - handler = mock.Mock(side_effect=exc_type) - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - - handler.assert_called_once_with({'key': 'value'}) - await_assertion(lambda: assert_consumer_error(consumer, error)) - - -def test_consume_request_cancel(endpoint, dispatcher): - def async_handler(): - time.sleep(3) - handler = mock.Mock(return_value=async_handler) - dispatcher['methodName'] = handler - - endpoint.consume({ - 'jsonrpc': '2.0', - 'id': MSG_ID, - 'method': 'methodName', - 'params': {'key': 'value'} - }) - handler.assert_called_once_with({'key': 'value'}) - - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': '$/cancelRequest', - 'params': {'id': MSG_ID} - }) - - # Because Python's Future cannot be cancelled once it's started, the request is never actually cancelled - # consumer.assert_called_once_with({ - # 'jsonrpc': '2.0', - # 'id': MSG_ID, - # 'error': exceptions.JsonRpcRequestCancelled().to_dict() - # }) - - -def test_consume_request_cancel_unknown(endpoint): - # Verify consume doesn't throw - endpoint.consume({ - 'jsonrpc': '2.0', - 'method': '$/cancelRequest', - 'params': {'id': 'unknown identifier'} - }) - - -def assert_consumer_error(consumer_mock, exception): - """Assert that the consumer mock has had once call with the given error message and code. - - The error's data part is not compared since it contains the traceback. - """ - assert len(consumer_mock.mock_calls) == 1 - _name, args, _kwargs = consumer_mock.mock_calls[0] - assert args[0]['error']['message'] == exception.message - assert args[0]['error']['code'] == exception.code - - -def await_assertion(condition, timeout=3.0, interval=0.1, exc=None): - if timeout <= 0: - raise exc if exc else AssertionError("Failed to wait for condition %s" % condition) - try: - condition() - except AssertionError as e: - time.sleep(interval) - await_assertion(condition, timeout=(timeout - interval), interval=interval, exc=e) diff --git a/test/jsonrpc/test_streams.py b/test/jsonrpc/test_streams.py deleted file mode 100644 index 998fdac4..00000000 --- a/test/jsonrpc/test_streams.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2018 Palantir Technologies, Inc. -# pylint: disable=redefined-outer-name -from io import BytesIO -import mock -import pytest - -from pyls.jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter - - -@pytest.fixture() -def rfile(): - return BytesIO() - - -@pytest.fixture() -def wfile(): - return BytesIO() - - -@pytest.fixture() -def reader(rfile): - return JsonRpcStreamReader(rfile) - - -@pytest.fixture() -def writer(wfile): - return JsonRpcStreamWriter(wfile, sort_keys=True) - - -def test_reader(rfile, reader): - rfile.write( - b'Content-Length: 49\r\n' - b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n' - b'\r\n' - b'{"id": "hello", "method": "method", "params": {}}' - ) - rfile.seek(0) - - consumer = mock.Mock() - reader.listen(consumer) - - consumer.assert_called_once_with({ - 'id': 'hello', - 'method': 'method', - 'params': {} - }) - - -def test_reader_bad_message(rfile, reader): - rfile.write(b'Hello world') - rfile.seek(0) - - # Ensure the listener doesn't throw - consumer = mock.Mock() - reader.listen(consumer) - consumer.assert_not_called() - - -def test_reader_bad_json(rfile, reader): - rfile.write( - b'Content-Length: 8\r\n' - b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n' - b'\r\n' - b'{hello}}' - ) - rfile.seek(0) - - # Ensure the listener doesn't throw - consumer = mock.Mock() - reader.listen(consumer) - consumer.assert_not_called() - - -def test_writer(wfile, writer): - writer.write({ - 'id': 'hello', - 'method': 'method', - 'params': {} - }) - assert wfile.getvalue() == ( - b'Content-Length: 49\r\n' - b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n' - b'\r\n' - b'{"id": "hello", "method": "method", "params": {}}' - ) - - -def test_writer_bad_message(wfile, writer): - # A datetime isn't serializable, ensure the write method doesn't throw - import datetime - writer.write(datetime.datetime.now()) - assert wfile.getvalue() == b'' diff --git a/test/test_language_server.py b/test/test_language_server.py index 542e9cd7..7597feb6 100644 --- a/test/test_language_server.py +++ b/test/test_language_server.py @@ -1,9 +1,11 @@ # Copyright 2017 Palantir Technologies, Inc. import os from threading import Thread + +from jsonrpc.exceptions import JsonRpcMethodNotFound import pytest + from pyls.python_ls import start_io_lang_server, PythonLanguageServer -from pyls.jsonrpc.exceptions import JsonRpcMethodNotFound CALL_TIMEOUT = 2 From 7d21c09f9274d8ed31f9ed35011a222ed54e5a63 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Fri, 1 Jun 2018 12:29:38 +0100 Subject: [PATCH 2/2] Remove jsonrpc --- pyls/python_ls.py | 7 ++++--- requirements.txt | 7 ------- 2 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 requirements.txt diff --git a/pyls/python_ls.py b/pyls/python_ls.py index c9ff1e85..eda0e02f 100644 --- a/pyls/python_ls.py +++ b/pyls/python_ls.py @@ -2,11 +2,12 @@ import logging import socketserver +from jsonrpc.dispatchers import MethodDispatcher +from jsonrpc.endpoint import Endpoint +from jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter + from . import lsp, _utils, uris from .config import config -from .jsonrpc.dispatchers import MethodDispatcher -from .jsonrpc.endpoint import Endpoint -from .jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter from .workspace import Workspace log = logging.getLogger(__name__) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0e01002b..00000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -configparser -future -jedi>=0.10 -json-rpc -pycodestyle -pyflakes -yapf