Repository: openlawlibrary/pygls

Issue:
Type of the id field in response message
The type of the `id` field in server response message is always `str` [now](https://github.com/openlawlibrary/pygls/blob/4d8d8e25fbedd3d41b75e8bfe041c99fe3611b1d/pygls/lsp/types/basic_structures.py#L72).

The LSP client can't get response for `{"id": 1, "method": "initialize}` request because pygls will respond with `{"id": "1", "result": {...}}` and `1 != "1"`. 

Can we please have the `id` in the response exactly the same as in the request?

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dcba8a6..348262e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,8 +13,10 @@ and this project adheres to [Semantic Versioning][semver].
 
 ### Fixed
 
+- Fix `pydantic` Unions type conversion ([#160])
 - Fix change_notifications type (pydantic bug) ([#158])
 
+[#160]: https://github.com/openlawlibrary/pygls/pull/160
 [#158]: https://github.com/openlawlibrary/pygls/pull/158
 
 ## [0.10.1] - 03/17/2021
diff --git a/pygls/lsp/types/basic_structures.py b/pygls/lsp/types/basic_structures.py
index 6b1478a..5ed2092 100644
--- a/pygls/lsp/types/basic_structures.py
+++ b/pygls/lsp/types/basic_structures.py
@@ -28,6 +28,7 @@ import enum
 from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
 
 from pydantic import BaseModel, root_validator
+from typeguard import check_type
 
 NumType = Union[int, float]
 T = TypeVar('T')
@@ -62,19 +63,33 @@ class JsonRPCNotification(JsonRpcMessage):
 
 class JsonRPCRequestMessage(JsonRpcMessage):
     """A class that represents json rpc request message."""
-    id: str
+    id: Any
     method: str
     params: Any
 
+    @root_validator
+    def check_result_or_error(cls, values):
+        # Workaround until pydantic supports StrictUnion
+        # https://github.com/samuelcolvin/pydantic/pull/2092
+        id_val = values.get('id')
+        check_type('', id_val, Union[int, str])
+
+        return values
+
 
 class JsonRPCResponseMessage(JsonRpcMessage):
     """A class that represents json rpc response message."""
-    id: str
+    id: Any
     result: Any
     error: Any
 
     @root_validator
     def check_result_or_error(cls, values):
+        # Workaround until pydantic supports StrictUnion
+        # https://github.com/samuelcolvin/pydantic/pull/2092
+        id_val = values.get('id')
+        check_type('', id_val, Union[int, str])
+
         result_val, error_val = values.get('result'), values.get('error')
 
         if result_val is not None and error_val is not None:
@@ -335,15 +350,26 @@ class MarkupContent(Model):
     value: str
 
 
-DocumentChangesType = Union[
-    List[TextDocumentEdit],
-    List[Union[TextDocumentEdit, CreateFile, RenameFile, DeleteFile]],
-]
-
-
 class WorkspaceEdit(Model):
     changes: Optional[Dict[str, List[TextEdit]]] = None
-    document_changes: Optional[DocumentChangesType] = None
+    document_changes: Any = None
+
+    @root_validator
+    def check_result_or_error(cls, values):
+        # Workaround until pydantic supports StrictUnion
+        # https://github.com/samuelcolvin/pydantic/pull/2092
+
+        document_changes_val = values.get('document_changes')
+        check_type(
+            '',
+            document_changes_val,
+            Optional[Union[
+                List[TextDocumentEdit],
+                List[Union[TextDocumentEdit, CreateFile, RenameFile, DeleteFile]],
+            ]]
+        )
+
+        return values
 
 
 class WorkDoneProgressBegin(Model):


Test patch:
diff --git a/tests/lsp/test_rename.py b/tests/lsp/test_rename.py
index b701334..609988f 100644
--- a/tests/lsp/test_rename.py
+++ b/tests/lsp/test_rename.py
@@ -157,11 +157,7 @@ class TestRename(unittest.TestCase):
 
         assert response['documentChanges'][3]['uri'] == 'delete file'
         assert response['documentChanges'][3]['options']['ignoreIfExists'] == True
-
-        # NOTE: pydantic BUG! `response['documentChanges'][3]` is of type `CreateFile` !?!?
-        # https://github.com/samuelcolvin/pydantic/pull/2092
-        #
-        # assert response['documentChanges'][3]['options']['overwrite'] == True
+        assert response['documentChanges'][3]['options']['recursive'] == True
 
     def test_rename_return_none(self):
         response = self.client.lsp.send_request(