Repository: openlawlibrary/pygls

Issue:
Message: TypeError: 'NoneType' object is not iterable
I'd like to use this to build an LSP for a small language called OpenSCAD, so I started investigating this project (excellent code and docs by the way!). I'm normally a Neovim user, but for sake of better understanding pygls, I installed VS code (in other words, might just be a configuration error) and ran the example JSON language server in debug mode. Unfortunately, I'm quickly met with this error in the debug client running alongside the server:
```
[Error - 12:11:01 AM] Server initialization failed.
  Message: TypeError: 'NoneType' object is not iterable
  Code: -32602 
[object Object]
```

Any ideas on how I could get around this? Is this a new error, or is it a common bump in the road?

Thanks in advance.

Discussion:
danixeee: @Maxattax97 thank you for your contribution! I will review it shortly.

Please take a look at [contributing](https://github.com/openlawlibrary/pygls/blob/master/CONTRIBUTING.md) document, section [Final Checklist](https://github.com/openlawlibrary/pygls/blob/master/CONTRIBUTING.md#final-checklist). 
Maxattax97: I've updated my original comment, added your proposed changes, and including documentation where needed in markdown files.

Let me know if there's anything else to be done!
Maxattax97: Can't say I understand why the CI failed?
Maxattax97: I'm relatively new to Python and still learning some of it's development workflow, thanks for your patience :smile:. I didn't know you could locally install like that (`pip3 install .`) into a venv, good to know from now on.

This is also my first in-depth code review. I've made PR's before, but never had the opportunity to really go through the process, so this is good :+1:.
dgreisen: @Maxattax97 Thanks for your contribution. When your OpenSCAD languange server is up, we'd love if you added it to https://github.com/openlawlibrary/pygls/blob/master/Implementations.md.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1d2aa1..7a894cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning][semver].
 
 ## [Unreleased]
 
+### Changed
+
+- Fix initialization failure when no workspace is open
+
 ## [0.7.3] - 01/30/2019
 
 ### Added
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 18388fd..48307c3 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -2,4 +2,5 @@
 
 - [@augb](https://github.com/augb)
 - [Daniel Elero](https://github.com/danixeee)
+- [Max O'Cull](https://github.com/Maxattax97)
 - [yorodm](https://github.com/yorodm)
diff --git a/pygls/exceptions.py b/pygls/exceptions.py
index de276c1..8d2823e 100644
--- a/pygls/exceptions.py
+++ b/pygls/exceptions.py
@@ -56,7 +56,7 @@ class JsonRpcException(Exception):
             'message': self.message,
         }
         if self.data is not None:
-            exception_dict['data'] = self.data
+            exception_dict['data'] = str(self.data)
         return exception_dict
 
 
diff --git a/pygls/protocol.py b/pygls/protocol.py
index affcb02..c065d7d 100644
--- a/pygls/protocol.py
+++ b/pygls/protocol.py
@@ -526,11 +526,8 @@ class LanguageServerProtocol(JsonRPCProtocol, metaclass=LSPMeta):
         root_uri = params.rootUri or from_fs_path(root_path)
 
         # Initialize the workspace
-        self.workspace = Workspace(root_uri, self)
-
         workspace_folders = getattr(params, 'workspaceFolders', [])
-        for folder in workspace_folders:
-            self.workspace.add_folder(folder)
+        self.workspace = Workspace(root_uri, workspace_folders)
 
         return InitializeResult(server_capabilities)
 
diff --git a/pygls/workspace.py b/pygls/workspace.py
index b13193c..8a7f5a4 100644
--- a/pygls/workspace.py
+++ b/pygls/workspace.py
@@ -128,14 +128,17 @@ class Document(object):
 
 class Workspace(object):
 
-    def __init__(self, root_uri, lsp):
+    def __init__(self, root_uri, workspace_folders=None):
         self._root_uri = root_uri
-        self._lsp = lsp
         self._root_uri_scheme = uri_scheme(self._root_uri)
         self._root_path = to_fs_path(self._root_uri)
         self._folders = {}
         self._docs = {}
 
+        if workspace_folders is not None:
+            for folder in workspace_folders:
+                self.add_folder(folder)
+
     def _create_document(self,
                          doc_uri: str,
                          source: str = None,


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index ebb6698..1ef4cc9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -21,7 +21,6 @@ import os
 from threading import Thread
 
 import pytest
-from mock import Mock
 from pygls import features, uris
 from pygls.feature_manager import FeatureManager
 from pygls.server import LanguageServer
@@ -91,4 +90,4 @@ def feature_manager():
 @pytest.fixture
 def workspace(tmpdir):
     """Return a workspace."""
-    return Workspace(uris.from_fs_path(str(tmpdir)), Mock())
+    return Workspace(uris.from_fs_path(str(tmpdir)))
diff --git a/tests/test_workspace.py b/tests/test_workspace.py
index 549a082..c6c122b 100644
--- a/tests/test_workspace.py
+++ b/tests/test_workspace.py
@@ -19,9 +19,9 @@
 import os
 
 from pygls import uris
+from pygls.workspace import Workspace
 from pygls.types import TextDocumentItem, WorkspaceFolder
 
-
 DOC_URI = uris.from_fs_path(__file__)
 DOC_TEXT = '''test'''
 DOC = TextDocumentItem(DOC_URI, 'plaintext', 0, DOC_TEXT)
@@ -66,3 +66,13 @@ def test_remove_document(workspace):
     assert workspace.get_document(DOC_URI).source == DOC_TEXT
     workspace.remove_document(DOC_URI)
     assert workspace.get_document(DOC_URI)._source is None
+
+
+def test_workspace_folders():
+    wf1 = WorkspaceFolder('/ws/f1', 'ws1')
+    wf2 = WorkspaceFolder('/ws/f2', 'ws2')
+
+    workspace = Workspace('/ws', [wf1, wf2])
+
+    assert workspace.folders['/ws/f1'] is wf1
+    assert workspace.folders['/ws/f2'] is wf2