Skip to content

Commit

Permalink
Change exclude_none back to exclude_unset
Browse files Browse the repository at this point in the history
This undoes the change in openlawlibrary#236 as well as bring back the explicit
setting of non `None` default fields as suggested in [1]

This commit also introduces serialization test cases that hopefully
cover all the scenarios raised in openlawlibrary#245, openlawlibrary#231 and openlawlibrary#198

[1]: openlawlibrary#245 (comment)
  • Loading branch information
alcarney committed Jul 4, 2022
1 parent 96bc69d commit 934ba94
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
7 changes: 7 additions & 0 deletions pygls/lsp/types/basic_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ class Config:
'from_': 'from'
}

def __init__(self, **data: Any) -> None:
super().__init__(**data)

# Serialize (.json()) fields that has default value which is not None
for name, field in self.__fields__.items():
if getattr(field, 'default', None) is not None:
self.__fields_set__.add(name)

class JsonRpcMessage(Model):
"""A base json rpc message defined by LSP."""
Expand Down
2 changes: 1 addition & 1 deletion pygls/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def _send_data(self, data):
return

try:
body = data.json(by_alias=True, exclude_none=True, encoder=default_serializer)
body = data.json(by_alias=True, exclude_unset=True, encoder=default_serializer)
logger.info('Sending data: %s', body)

body = body.encode(self.CHARSET)
Expand Down
101 changes: 97 additions & 4 deletions tests/test_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,28 @@
# See the License for the specific language governing permissions and #
# limitations under the License. #
############################################################################
import io
import json
from concurrent.futures import Future
from functools import partial
from pathlib import Path
from typing import Optional
from unittest.mock import Mock
from pygls.lsp.types.language_features.completion import CompletionItemKind

import pytest

from pygls.exceptions import JsonRpcException, JsonRpcInvalidParams
from pygls.lsp import Model, get_method_params_type
from pygls.lsp.types import ClientCapabilities, InitializeParams, InitializeResult
from pygls.protocol import JsonRPCNotification, JsonRPCRequestMessage, JsonRPCResponseMessage
from pygls.lsp.types import (
ClientCapabilities,
CompletionItem,
InitializeParams,
InitializeResult,
ProgressParams,
WorkDoneProgressBegin,
)
from pygls.protocol import JsonRPCNotification, JsonRPCProtocol, JsonRPCRequestMessage, JsonRPCResponseMessage
from pygls.protocol import deserialize_message as _deserialize_message

TEST_METHOD = 'test_method'
Expand Down Expand Up @@ -94,6 +103,47 @@ def test_deserialize_notification_message_bad_params_should_raise_error():
json.loads(params, object_hook=deserialize_message)


@pytest.mark.parametrize(
"params, expected",
[
(
ProgressParams(
token="id1",
value=WorkDoneProgressBegin(
title="Begin progress",
percentage=0,
)
),
{
"jsonrpc": "2.0",
"method": "test/notification",
"params": {
"token": "id1",
"value": {
"kind": "begin",
"percentage": 0,
"title": "Begin progress"
}
}
}
),
]
)
def test_serialize_notification_message(params, expected):
"""Ensure that we can serialize notification messages, retaining all expected fields."""

buffer = io.StringIO()

protocol = JsonRPCProtocol(None)
protocol._send_only_body = True
protocol.connection_made(buffer)

protocol.notify("test/notification", params=params)
actual = json.loads(buffer.getvalue())

assert actual == expected


def test_deserialize_response_message():
params = '''
{
Expand All @@ -110,7 +160,6 @@ def test_deserialize_response_message():
assert result.result == "1"
assert result.error is None


def test_deserialize_request_message_with_registered_type__should_return_instance():
params = '''
{
Expand Down Expand Up @@ -163,6 +212,51 @@ def test_deserialize_request_message_without_registered_type__should_return_name
assert result.params.field_b.inner_field == "test_inner"


@pytest.mark.parametrize(
"result, expected",
[
(None, {"jsonrpc": "2.0", "id": "1", "result": None}),
(
[
CompletionItem(label='example-one'),
CompletionItem(
label='example-two',
kind=CompletionItemKind.Class,
preselect=False,
deprecated=True
),
],
{
"jsonrpc": "2.0",
"id": "1",
"result": [
{"label": "example-one"},
{
"label": "example-two",
"kind": 7, # CompletionItemKind.Class
"preselect": False,
"deprecated": True
}
]
}
),
]
)
def test_serialize_response_message(result, expected):
"""Ensure that we can serialize response messages, retaining all expected fields."""

buffer = io.StringIO()

protocol = JsonRPCProtocol(None)
protocol._send_only_body = True
protocol.connection_made(buffer)

protocol._send_response("1", result=result)
actual = json.loads(buffer.getvalue())

assert actual == expected


def test_data_received_without_content_type_should_handle_message(client_server):
_, server = client_server
body = json.dumps({
Expand Down Expand Up @@ -278,4 +372,3 @@ def test_ignore_unknown_notification(client_server):

# Remove mock
server.lsp._execute_notification = fn

0 comments on commit 934ba94

Please sign in to comment.