diff --git a/pyls/workspace.py b/pyls/workspace.py
index eaebc12..0ffab25 100644
--- a/pyls/workspace.py
+++ b/pyls/workspace.py
@@ -88,8 +88,8 @@ class Workspace(object):
 
     def source_roots(self, document_path):
         """Return the source roots for the given document."""
-        files = _utils.find_parents(self._root_path, document_path, ['setup.py']) or []
-        return [os.path.dirname(setup_py) for setup_py in files]
+        files = _utils.find_parents(self._root_path, document_path, ['setup.py', 'pyproject.toml']) or []
+        return list(set((os.path.dirname(project_file) for project_file in files))) or [self._root_path]
 
     def _create_document(self, doc_uri, source=None, version=None):
         path = uris.to_fs_path(doc_uri)

diff --git a/test/test_workspace.py b/test/test_workspace.py
index f510eaa..9b5b7b0 100644
--- a/test/test_workspace.py
+++ b/test/test_workspace.py
@@ -1,7 +1,9 @@
 # Copyright 2017 Palantir Technologies, Inc.
 import os
-import os.path as osp
 import sys
+
+import pytest
+
 from pyls import uris
 
 PY2 = sys.version_info.major == 2
@@ -16,7 +18,7 @@ DOC_URI = uris.from_fs_path(__file__)
 
 
 def path_as_uri(path):
-    return pathlib.Path(osp.abspath(path)).as_uri()
+    return pathlib.Path(os.path.abspath(path)).as_uri()
 
 
 def test_local(pyls):
@@ -49,14 +51,16 @@ def test_rm_document(pyls):
     assert pyls.workspace.get_document(DOC_URI)._source is None
 
 
-def test_non_root_project(pyls):
+@pytest.mark.parametrize('metafiles', [('setup.py',), ('pyproject.toml',), ('setup.py', 'pyproject.toml')])
+def test_non_root_project(pyls, metafiles):
     repo_root = os.path.join(pyls.workspace.root_path, 'repo-root')
     os.mkdir(repo_root)
     project_root = os.path.join(repo_root, 'project-root')
     os.mkdir(project_root)
 
-    with open(os.path.join(project_root, 'setup.py'), 'w+') as f:
-        f.write('# setup.py')
+    for metafile in metafiles:
+        with open(os.path.join(project_root, metafile), 'w+') as f:
+            f.write('# ' + metafile)
 
     test_uri = uris.from_fs_path(os.path.join(project_root, 'hello/test.py'))
     pyls.workspace.put_document(test_uri, 'assert True')
@@ -64,6 +68,15 @@ def test_non_root_project(pyls):
     assert project_root in test_doc.sys_path()
 
 
+def test_root_project_with_no_setup_py(pyls):
+    """Default to workspace root."""
+    workspace_root = pyls.workspace.root_path
+    test_uri = uris.from_fs_path(os.path.join(workspace_root, 'hello/test.py'))
+    pyls.workspace.put_document(test_uri, 'assert True')
+    test_doc = pyls.workspace.get_document(test_uri)
+    assert workspace_root in test_doc.sys_path()
+
+
 def test_multiple_workspaces(tmpdir, pyls):
     workspace1_dir = tmpdir.mkdir('workspace1')
     workspace2_dir = tmpdir.mkdir('workspace2')

No default source root
If no `setup.py` is found, `Workspace.source_roots()` currently returns `[]`.

I think a preferable alternative would be to return the Workspace root (as the language client may have other means for detecting a source root).
test/test_workspace.py::test_non_root_project[metafiles1] test/test_workspace.py::test_root_project_with_no_setup_py
test/test_workspace.py::test_local test/test_workspace.py::test_put_document test/test_workspace.py::test_get_document test/test_workspace.py::test_get_missing_document test/test_workspace.py::test_rm_document test/test_workspace.py::test_non_root_project[metafiles0] test/test_workspace.py::test_non_root_project[metafiles2] test/test_workspace.py::test_multiple_workspaces