Skip to content

Commit 4646633

Browse files
andfoyccordoba12
andauthored
Read pycodestyle and flake8 configurations per workspace (#827)
Co-authored-by: Carlos Cordoba <[email protected]>
1 parent a72704f commit 4646633

13 files changed

+141
-102
lines changed

pyls/config/config.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,29 +106,31 @@ def settings(self, document_path=None):
106106
settings = {}
107107
sources = self._settings.get('configurationSources', DEFAULT_CONFIG_SOURCES)
108108

109+
# Plugin configuration
110+
settings = _utils.merge_dicts(settings, self._plugin_settings)
111+
112+
# LSP configuration
113+
settings = _utils.merge_dicts(settings, self._settings)
114+
115+
# User configuration
109116
for source_name in reversed(sources):
110117
source = self._config_sources.get(source_name)
111118
if not source:
112119
continue
113120
source_conf = source.user_config()
114121
log.debug("Got user config from %s: %s", source.__class__.__name__, source_conf)
115122
settings = _utils.merge_dicts(settings, source_conf)
116-
log.debug("With user configuration: %s", settings)
117-
118-
settings = _utils.merge_dicts(settings, self._plugin_settings)
119-
log.debug("With plugin configuration: %s", settings)
120-
121-
settings = _utils.merge_dicts(settings, self._settings)
122-
log.debug("With lsp configuration: %s", settings)
123123

124+
# Project configuration
124125
for source_name in reversed(sources):
125126
source = self._config_sources.get(source_name)
126127
if not source:
127128
continue
128129
source_conf = source.project_config(document_path or self._root_path)
129130
log.debug("Got project config from %s: %s", source.__class__.__name__, source_conf)
130131
settings = _utils.merge_dicts(settings, source_conf)
131-
log.debug("With project configuration: %s", settings)
132+
133+
log.debug("With configuration: %s", settings)
132134

133135
return settings
134136

pyls/plugins/flake8_lint.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ def pyls_settings():
1717

1818

1919
@hookimpl
20-
def pyls_lint(config, document):
20+
def pyls_lint(workspace, document):
21+
config = workspace._config
2122
settings = config.plugin_settings('flake8')
2223
log.debug("Got flake8 settings: %s", settings)
2324

pyls/plugins/pycodestyle_lint.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020

2121
@hookimpl
22-
def pyls_lint(config, document):
22+
def pyls_lint(workspace, document):
23+
config = workspace._config
2324
settings = config.plugin_settings('pycodestyle')
2425
log.debug("Got pycodestyle settings: %s", settings)
2526

pyls/python_ls.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def m_workspace__did_change_configuration(self, settings=None):
358358
self.config.update((settings or {}).get('pyls', {}))
359359
for workspace_uri in self.workspaces:
360360
workspace = self.workspaces[workspace_uri]
361-
workspace.update_config(self.config)
361+
workspace.update_config(settings)
362362
for doc_uri in workspace.documents:
363363
self.lint(doc_uri, is_saved=False)
364364

@@ -376,23 +376,31 @@ def m_workspace__did_change_workspace_folders(self, event=None, **_kwargs): # p
376376
for added_info in added:
377377
if 'uri' in added_info:
378378
added_uri = added_info['uri']
379-
self.workspaces[added_uri] = Workspace(added_uri, self._endpoint, self.config)
379+
workspace_config = config.Config(
380+
added_uri, self.config._init_opts,
381+
self.config._process_id, self.config._capabilities)
382+
self.workspaces[added_uri] = Workspace(
383+
added_uri, self._endpoint, workspace_config)
380384

381385
root_workspace_removed = any(removed_info['uri'] == self.root_uri for removed_info in removed)
382386
workspace_added = len(added) > 0 and 'uri' in added[0]
383387
if root_workspace_removed and workspace_added:
384388
added_uri = added[0]['uri']
385389
self.root_uri = added_uri
386-
self.workspace = self.workspaces[added_uri]
390+
new_root_workspace = self.workspaces[added_uri]
391+
self.config = new_root_workspace._config
392+
self.workspace = new_root_workspace
387393
elif root_workspace_removed:
388394
# NOTE: Removing the root workspace can only happen when the server
389395
# is closed, thus the else condition of this if can never happen.
390396
if self.workspaces:
391397
log.debug('Root workspace deleted!')
392398
available_workspaces = sorted(self.workspaces)
393399
first_workspace = available_workspaces[0]
400+
new_root_workspace = self.workspaces[first_workspace]
394401
self.root_uri = first_workspace
395-
self.workspace = self.workspaces[first_workspace]
402+
self.config = new_root_workspace._config
403+
self.workspace = new_root_workspace
396404

397405
# Migrate documents that are on the root workspace and have a better
398406
# match now

pyls/workspace.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ def update_document(self, doc_uri, change, version=None):
8484
self._docs[doc_uri].apply_change(change)
8585
self._docs[doc_uri].version = version
8686

87-
def update_config(self, config):
88-
self._config = config
87+
def update_config(self, settings):
88+
self._config.update((settings or {}).get('pyls', {}))
8989
for doc_uri in self.documents:
90-
self.get_document(doc_uri).update_config(config)
90+
self.get_document(doc_uri).update_config(settings)
9191

9292
def apply_edit(self, edit):
9393
return self._endpoint.request(self.M_APPLY_EDIT, {'edit': edit})
@@ -106,23 +106,25 @@ def source_roots(self, document_path):
106106
def _create_document(self, doc_uri, source=None, version=None):
107107
path = uris.to_fs_path(doc_uri)
108108
return Document(
109-
doc_uri, self, source=source, version=version,
109+
doc_uri,
110+
self,
111+
source=source,
112+
version=version,
110113
extra_sys_path=self.source_roots(path),
111114
rope_project_builder=self._rope_project_builder,
112-
config=self._config,
113115
)
114116

115117

116118
class Document(object):
117119

118120
def __init__(self, uri, workspace, source=None, version=None, local=True, extra_sys_path=None,
119-
rope_project_builder=None, config=None):
121+
rope_project_builder=None):
120122
self.uri = uri
121123
self.version = version
122124
self.path = uris.to_fs_path(uri)
123125
self.filename = os.path.basename(self.path)
124126

125-
self._config = config
127+
self._config = workspace._config
126128
self._workspace = workspace
127129
self._local = local
128130
self._source = source
@@ -147,8 +149,8 @@ def source(self):
147149
return f.read()
148150
return self._source
149151

150-
def update_config(self, config):
151-
self._config = config
152+
def update_config(self, settings):
153+
self._config.update((settings or {}).get('pyls', {}))
152154

153155
def apply_change(self, change):
154156
"""Apply a change to the document."""

test/fixtures.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def pyls(tmpdir):
3939
@pytest.fixture
4040
def workspace(tmpdir):
4141
"""Return a workspace."""
42-
return Workspace(uris.from_fs_path(str(tmpdir)), Mock())
42+
ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock())
43+
ws._config = Config(ws.root_uri, {}, 0, {})
44+
return ws
4345

4446

4547
@pytest.fixture

test/plugins/test_completion.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import os
33
import sys
44

5-
from test.test_utils import MockWorkspace
65
import pytest
76

87
from pyls import uris, lsp
@@ -278,7 +277,7 @@ def test_multistatement_snippet(config, workspace):
278277
assert completions[0]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0'
279278

280279

281-
def test_jedi_completion_extra_paths(config, tmpdir, workspace):
280+
def test_jedi_completion_extra_paths(tmpdir, workspace):
282281
# Create a tempfile with some content and pass to extra_paths
283282
temp_doc_content = '''
284283
def spam():
@@ -296,42 +295,42 @@ def spam():
296295

297296
# After 'foo.s' without extra paths
298297
com_position = {'line': 1, 'character': 5}
299-
completions = pyls_jedi_completions(config, doc, com_position)
298+
completions = pyls_jedi_completions(doc._config, doc, com_position)
300299
assert completions is None
301300

302301
# Update config extra paths
303-
config.update({'plugins': {'jedi': {'extra_paths': extra_paths}}})
304-
doc.update_config(config)
302+
settings = {'pyls': {'plugins': {'jedi': {'extra_paths': extra_paths}}}}
303+
doc.update_config(settings)
305304

306305
# After 'foo.s' with extra paths
307306
com_position = {'line': 1, 'character': 5}
308-
completions = pyls_jedi_completions(config, doc, com_position)
307+
completions = pyls_jedi_completions(doc._config, doc, com_position)
309308
assert completions[0]['label'] == 'spam()'
310309

311310

312311
@pytest.mark.skipif(PY2 or not LINUX or not CI, reason="tested on linux and python 3 only")
313-
def test_jedi_completion_environment(config):
312+
def test_jedi_completion_environment(workspace):
314313
# Content of doc to test completion
315314
doc_content = '''import logh
316315
'''
317-
doc = Document(DOC_URI, MockWorkspace(), doc_content)
316+
doc = Document(DOC_URI, workspace, doc_content)
318317

319318
# After 'import logh' with default environment
320319
com_position = {'line': 0, 'character': 11}
321320

322321
assert os.path.isdir('/tmp/pyenv/')
323322

324-
config.update({'plugins': {'jedi': {'environment': None}}})
325-
doc.update_config(config)
326-
completions = pyls_jedi_completions(config, doc, com_position)
323+
settings = {'pyls': {'plugins': {'jedi': {'environment': None}}}}
324+
doc.update_config(settings)
325+
completions = pyls_jedi_completions(doc._config, doc, com_position)
327326
assert completions is None
328327

329328
# Update config extra environment
330329
env_path = '/tmp/pyenv/bin/python'
331-
config.update({'plugins': {'jedi': {'environment': env_path}}})
332-
doc.update_config(config)
330+
settings = {'pyls': {'plugins': {'jedi': {'environment': env_path}}}}
331+
doc.update_config(settings)
333332

334333
# After 'import logh' with new environment
335-
completions = pyls_jedi_completions(config, doc, com_position)
334+
completions = pyls_jedi_completions(doc._config, doc, com_position)
336335
assert completions[0]['label'] == 'loghub'
337336
assert 'changelog generator' in completions[0]['documentation'].lower()

test/plugins/test_flake8_lint.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright 2019 Palantir Technologies, Inc.
22
import tempfile
33
import os
4-
from test.test_utils import MockWorkspace
54
from mock import patch
65
from pyls import lsp, uris
76
from pyls.plugins import flake8_lint
@@ -19,29 +18,29 @@ def using_const():
1918
"""
2019

2120

22-
def temp_document(doc_text):
21+
def temp_document(doc_text, workspace):
2322
temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
2423
name = temp_file.name
2524
temp_file.write(doc_text)
2625
temp_file.close()
27-
doc = Document(uris.from_fs_path(name), MockWorkspace())
26+
doc = Document(uris.from_fs_path(name), workspace)
2827

2928
return name, doc
3029

3130

32-
def test_flake8_no_checked_file(config, workspace):
31+
def test_flake8_no_checked_file(workspace):
3332
# A bad uri or a non-saved file may cause the flake8 linter to do nothing.
3433
# In this situtation, the linter will return an empty list.
3534

3635
doc = Document('', workspace, DOC)
37-
diags = flake8_lint.pyls_lint(config, doc)
36+
diags = flake8_lint.pyls_lint(workspace, doc)
3837
assert 'Error' in diags[0]['message']
3938

4039

41-
def test_flake8_lint(config):
40+
def test_flake8_lint(workspace):
4241
try:
43-
name, doc = temp_document(DOC)
44-
diags = flake8_lint.pyls_lint(config, doc)
42+
name, doc = temp_document(DOC, workspace)
43+
diags = flake8_lint.pyls_lint(workspace, doc)
4544
msg = 'local variable \'a\' is assigned to but never used'
4645
unused_var = [d for d in diags if d['message'] == msg][0]
4746

@@ -55,14 +54,14 @@ def test_flake8_lint(config):
5554
os.remove(name)
5655

5756

58-
def test_flake8_config_param(config):
57+
def test_flake8_config_param(workspace):
5958
with patch('pyls.plugins.flake8_lint.Popen') as popen_mock:
6059
mock_instance = popen_mock.return_value
6160
mock_instance.communicate.return_value = [bytes(), bytes()]
6261
flake8_conf = '/tmp/some.cfg'
63-
config.update({'plugins': {'flake8': {'config': flake8_conf}}})
64-
_name, doc = temp_document(DOC)
65-
flake8_lint.pyls_lint(config, doc)
62+
workspace._config.update({'plugins': {'flake8': {'config': flake8_conf}}})
63+
_name, doc = temp_document(DOC, workspace)
64+
flake8_lint.pyls_lint(workspace, doc)
6665
call_args = popen_mock.call_args.args[0]
6766
assert 'flake8' in call_args
6867
assert '--config={}'.format(flake8_conf) in call_args

test/plugins/test_pycodestyle_lint.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import os
33
from pyls import lsp, uris
4-
from pyls.config.config import Config
54
from pyls.workspace import Document
65
from pyls.plugins import pycodestyle_lint
76

@@ -20,9 +19,9 @@ def hello( ):
2019
"""
2120

2221

23-
def test_pycodestyle(config, workspace):
22+
def test_pycodestyle(workspace):
2423
doc = Document(DOC_URI, workspace, DOC)
25-
diags = pycodestyle_lint.pyls_lint(config, doc)
24+
diags = pycodestyle_lint.pyls_lint(workspace, doc)
2625

2726
assert all([d['source'] == 'pycodestyle' for d in diags])
2827

@@ -78,10 +77,9 @@ def test_pycodestyle_config(workspace):
7877
doc_uri = uris.from_fs_path(os.path.join(workspace.root_path, 'test.py'))
7978
workspace.put_document(doc_uri, DOC)
8079
doc = workspace.get_document(doc_uri)
81-
config = Config(workspace.root_uri, {}, 1234, {})
8280

8381
# Make sure we get a warning for 'indentation contains tabs'
84-
diags = pycodestyle_lint.pyls_lint(config, doc)
82+
diags = pycodestyle_lint.pyls_lint(workspace, doc)
8583
assert [d for d in diags if d['code'] == 'W191']
8684

8785
content = {
@@ -93,20 +91,20 @@ def test_pycodestyle_config(workspace):
9391
# Now we'll add config file to ignore it
9492
with open(os.path.join(workspace.root_path, conf_file), 'w+') as f:
9593
f.write(content)
96-
config.settings.cache_clear()
94+
workspace._config.settings.cache_clear()
9795

9896
# And make sure we don't get any warnings
99-
diags = pycodestyle_lint.pyls_lint(config, doc)
97+
diags = pycodestyle_lint.pyls_lint(workspace, doc)
10098
assert len([d for d in diags if d['code'] == 'W191']) == (0 if working else 1)
10199
assert len([d for d in diags if d['code'] == 'E201']) == (0 if working else 1)
102100
assert [d for d in diags if d['code'] == 'W391']
103101

104102
os.unlink(os.path.join(workspace.root_path, conf_file))
105103

106104
# Make sure we can ignore via the PYLS config as well
107-
config.update({'plugins': {'pycodestyle': {'ignore': ['W191', 'E201']}}})
105+
workspace._config.update({'plugins': {'pycodestyle': {'ignore': ['W191', 'E201']}}})
108106
# And make sure we only get one warning
109-
diags = pycodestyle_lint.pyls_lint(config, doc)
107+
diags = pycodestyle_lint.pyls_lint(workspace, doc)
110108
assert not [d for d in diags if d['code'] == 'W191']
111109
assert not [d for d in diags if d['code'] == 'E201']
112110
assert [d for d in diags if d['code'] == 'W391']

0 commit comments

Comments
 (0)