Skip to content

Commit

Permalink
Remove pluggy racing (#222)
Browse files Browse the repository at this point in the history
* Completion: Remove pluggy racing and disable Rope by default

This allows the user to choose between Rope and Jedi for completions.

It also remove the pluggy racing since Jedi's cache isn't thread safe.

Closes #189

* Only use single completion hook

* Remove pluggy and disable rope completions by default

* Remove pluggy
  • Loading branch information
gatesn authored Jan 14, 2018
1 parent 166967b commit af4dcb3
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 79 deletions.
27 changes: 0 additions & 27 deletions pyls/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,6 @@ def _merge_dicts_(a, b):
return dict(_merge_dicts_(dict_a, dict_b))


def race_hooks(hook_caller, pool, **kwargs):
"""Given a pluggy hook spec, execute impls in parallel returning the first non-None result.
Note this does not support a lot of pluggy functionality, e.g. hook wrappers.
"""
impls = hook_caller._nonwrappers + hook_caller._wrappers
log.debug("Racing hook impls for hook %s: %s", hook_caller, impls)

if not impls:
return None

def _apply(impl):
try:
return impl, impl.function(**kwargs)
except Exception:
log.exception("Failed to run hook %s", impl.plugin_name)
raise

# imap unordered gives us an iterator over the items in the order they finish.
# We have to be careful to set chunksize to 1 to ensure hooks each get their own thread.
# Unfortunately, there's no way to interrupt these threads, so we just have to leave them be.
for impl, result in pool.imap_unordered(_apply, impls, chunksize=1):
if result is not None:
log.debug("Hook from plugin %s returned: %s", impl.plugin_name, result)
return result


def format_docstring(contents):
"""Python doc strings come in a number of formats, but LSP wants markdown.
Expand Down
70 changes: 33 additions & 37 deletions pyls/plugins/jedi_completion.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
# Copyright 2017 Palantir Technologies, Inc.
import logging
from pyls.lsp import CompletionItemKind
from pyls import hookimpl, _utils
from pyls import hookimpl, lsp, _utils

log = logging.getLogger(__name__)


@hookimpl
def pyls_completions(document, position):
log.debug('Launching Jedi')
definitions = document.jedi_script(position).completions()
definitions = [{
return [{
'label': _label(d),
'kind': _kind(d),
'detail': _detail(d),
'documentation': _utils.format_docstring(d.docstring()),
'sortText': _sort_text(d),
'insertText': d.name
} for d in definitions]
log.debug('Jedi finished')
return definitions
} for d in definitions] or None


def _label(definition):
Expand Down Expand Up @@ -53,36 +49,36 @@ def _sort_text(definition):
def _kind(d):
""" Return the VSCode type """
MAP = {
'none': CompletionItemKind.Value,
'type': CompletionItemKind.Class,
'tuple': CompletionItemKind.Class,
'dict': CompletionItemKind.Class,
'dictionary': CompletionItemKind.Class,
'function': CompletionItemKind.Function,
'lambda': CompletionItemKind.Function,
'generator': CompletionItemKind.Function,
'class': CompletionItemKind.Class,
'instance': CompletionItemKind.Reference,
'method': CompletionItemKind.Method,
'builtin': CompletionItemKind.Class,
'builtinfunction': CompletionItemKind.Function,
'module': CompletionItemKind.Module,
'file': CompletionItemKind.File,
'xrange': CompletionItemKind.Class,
'slice': CompletionItemKind.Class,
'traceback': CompletionItemKind.Class,
'frame': CompletionItemKind.Class,
'buffer': CompletionItemKind.Class,
'dictproxy': CompletionItemKind.Class,
'funcdef': CompletionItemKind.Function,
'property': CompletionItemKind.Property,
'import': CompletionItemKind.Module,
'keyword': CompletionItemKind.Keyword,
'constant': CompletionItemKind.Variable,
'variable': CompletionItemKind.Variable,
'value': CompletionItemKind.Value,
'param': CompletionItemKind.Variable,
'statement': CompletionItemKind.Keyword,
'none': lsp.CompletionItemKind.Value,
'type': lsp.CompletionItemKind.Class,
'tuple': lsp.CompletionItemKind.Class,
'dict': lsp.CompletionItemKind.Class,
'dictionary': lsp.CompletionItemKind.Class,
'function': lsp.CompletionItemKind.Function,
'lambda': lsp.CompletionItemKind.Function,
'generator': lsp.CompletionItemKind.Function,
'class': lsp.CompletionItemKind.Class,
'instance': lsp.CompletionItemKind.Reference,
'method': lsp.CompletionItemKind.Method,
'builtin': lsp.CompletionItemKind.Class,
'builtinfunction': lsp.CompletionItemKind.Function,
'module': lsp.CompletionItemKind.Module,
'file': lsp.CompletionItemKind.File,
'xrange': lsp.CompletionItemKind.Class,
'slice': lsp.CompletionItemKind.Class,
'traceback': lsp.CompletionItemKind.Class,
'frame': lsp.CompletionItemKind.Class,
'buffer': lsp.CompletionItemKind.Class,
'dictproxy': lsp.CompletionItemKind.Class,
'funcdef': lsp.CompletionItemKind.Function,
'property': lsp.CompletionItemKind.Property,
'import': lsp.CompletionItemKind.Module,
'keyword': lsp.CompletionItemKind.Keyword,
'constant': lsp.CompletionItemKind.Variable,
'variable': lsp.CompletionItemKind.Variable,
'value': lsp.CompletionItemKind.Value,
'param': lsp.CompletionItemKind.Variable,
'statement': lsp.CompletionItemKind.Keyword,
}

return MAP.get(d.type)
10 changes: 7 additions & 3 deletions pyls/plugins/rope_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@


@hookimpl
def pyls_completions(document, position):
log.debug('Launching Rope')
def pyls_settings():
# Default rope_completion to disabled
return {'plugins': {'rope_completion': {'enabled': False}}}


@hookimpl
def pyls_completions(document, position):
# Rope is a bit rubbish at completing module imports, so we'll return None
word = document.word_at_position({
# The -1 should really be trying to look at the previous word, but that might be quite expensive
Expand Down Expand Up @@ -40,7 +44,7 @@ def pyls_completions(document, position):
'documentation': doc or "",
'sortText': _sort_text(d)})
definitions = new_definitions
log.debug('Rope finished')

return definitions or None


Expand Down
15 changes: 4 additions & 11 deletions pyls/python_ls.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# Copyright 2017 Palantir Technologies, Inc.
import logging
from multiprocessing import dummy as multiprocessing
from . import lsp, _utils
from .config import config
from .language_server import LanguageServer
from .workspace import Workspace

log = logging.getLogger(__name__)

PLUGGY_RACE_POOL_SIZE = 5
LINT_DEBOUNCE_S = 0.5 # 500 ms


Expand All @@ -21,8 +19,6 @@ class PythonLanguageServer(LanguageServer):
# Set of method dispatchers to query
_dispatchers = []

_pool = multiprocessing.Pool(PLUGGY_RACE_POOL_SIZE)

def __getitem__(self, item):
"""Override the method dispatcher to farm out any unknown messages to our plugins."""
try:
Expand All @@ -41,7 +37,8 @@ def _hook_caller(self, hook_name):

def _hook(self, hook_name, doc_uri=None, **kwargs):
doc = self.workspace.get_document(doc_uri) if doc_uri else None
return self._hook_caller(hook_name)(config=self.config, workspace=self.workspace, document=doc, **kwargs)
hook = self.config.plugin_manager.subset_hook_caller(hook_name, self.config.disabled_plugins)
return hook(config=self.config, workspace=self.workspace, document=doc, **kwargs)

def capabilities(self):
server_capabilities = {
Expand Down Expand Up @@ -85,14 +82,10 @@ def code_lens(self, doc_uri):
return flatten(self._hook('pyls_code_lens', doc_uri))

def completions(self, doc_uri, position):
completions = _utils.race_hooks(
self._hook_caller('pyls_completions'), self._pool,
document=self.workspace.get_document(doc_uri) if doc_uri else None,
position=position
)
completions = self._hook('pyls_completions', doc_uri, position=position)
return {
'isIncomplete': False,
'items': completions or []
'items': flatten(completions)
}

def definitions(self, doc_uri, position):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
'pyls = pyls.__main__:main',
],
'pyls': [
'rope_completion = pyls.plugins.rope_completion',
'jedi_completion = pyls.plugins.jedi_completion',
'jedi_definition = pyls.plugins.definition',
'jedi_hover = pyls.plugins.hover',
Expand All @@ -72,6 +71,7 @@
'pycodestyle = pyls.plugins.pycodestyle_lint',
'pydocstyle = pyls.plugins.pydocstyle_lint',
'pyflakes = pyls.plugins.pyflakes_lint',
'rope_completion = pyls.plugins.rope_completion',
'rope_rename = pyls.plugins.rope_rename',
'yapf = pyls.plugins.format',
]
Expand Down

0 comments on commit af4dcb3

Please sign in to comment.