Skip to content

Commit fe32bcb

Browse files
authored
Make plugins optional (#301)
1 parent 9254d5f commit fe32bcb

File tree

5 files changed

+55
-17
lines changed

5 files changed

+55
-17
lines changed

README.rst

+21-8
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,41 @@ Python Language Server
1212

1313
A Python 2.7 and 3.4+ implementation of the `Language Server Protocol`_.
1414

15-
Feature Providers
16-
-----------------
17-
* Jedi_ for Completions, Definitions, Hover, References, Signature Help, and Symbols
15+
Installation
16+
------------
17+
18+
The base language server requires Jedi_ to provide Completions, Definitions, Hover, References, Signature Help, and
19+
Symbols:
20+
21+
``pip install python-language-server``
22+
23+
If the respective dependencies are found, the following optional providers will be enabled:
24+
1825
* Rope_ for Completions and renaming
1926
* Pyflakes_ linter to detect various errors
2027
* McCabe_ linter for complexity checking
2128
* pycodestyle_ linter for style checking
2229
* pydocstyle_ linter for docstring style checking
2330
* YAPF_ for code formatting
2431

32+
Optional providers can be installed using the `extras` syntax. To install YAPF_ formatting for example:
33+
34+
``pip install 'python-language-server[yapf]'``
35+
36+
All optional providers can be installed using:
37+
38+
``pip install 'python-language-server[all]'``
39+
2540
3rd Party Plugins
2641
~~~~~~~~~~~~~~~~~
2742
Installing these plugins will add extra functionality to the language server:
2843

2944
* pyls-mypy_ Mypy type checking for Python 3
3045
* pyls-isort_ Isort import sort code formatting
3146

47+
Please see the above repositories for examples on how to write plugins for the Python Language Server. Please file an
48+
issue if you require assistance writing a plugin.
49+
3250
Configuration
3351
-------------
3452

@@ -78,11 +96,6 @@ Document Formatting:
7896

7997
.. image:: https://raw.githubusercontent.com/palantir/python-language-server/develop/resources/document-format.gif
8098

81-
Installation
82-
------------
83-
84-
``pip install python-language-server``
85-
8699
Development
87100
-----------
88101

pyls/config/config.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import logging
3+
import pkg_resources
4+
35
import pluggy
46

57
from pyls import _utils, hookspecs, uris, PYLS
@@ -32,10 +34,23 @@ def __init__(self, root_uri, init_opts):
3234
self._pm.trace.root.setwriter(log.debug)
3335
self._pm.enable_tracing()
3436
self._pm.add_hookspecs(hookspecs)
37+
38+
# Pluggy will skip loading a plugin if it throws a DistributionNotFound exception.
39+
# However I don't want all plugins to have to catch ImportError and re-throw. So here we'll filter
40+
# out any entry points that throw ImportError assuming one or more of their dependencies isn't present.
41+
for entry_point in pkg_resources.iter_entry_points(PYLS):
42+
try:
43+
entry_point.load()
44+
except ImportError as e:
45+
log.warn("Failed to load %s entry point '%s': %s", PYLS, entry_point.name, e)
46+
self._pm.set_blocked(entry_point.name)
47+
48+
# Load the entry points into pluggy, having blocked any failing ones
3549
self._pm.load_setuptools_entrypoints(PYLS)
3650

3751
for name, plugin in self._pm.list_name_plugin():
38-
log.info("Loaded pyls plugin %s from %s", name, plugin)
52+
if plugin is not None:
53+
log.info("Loaded pyls plugin %s from %s", name, plugin)
3954

4055
for plugin_conf in self._pm.hook.pyls_settings(config=self):
4156
self._plugin_settings = _utils.merge_dicts(self._plugin_settings, plugin_conf)

pyls/workspace.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import pkgutil
99

1010
import jedi
11-
from rope.base import libutils
12-
from rope.base.project import Project
1311

1412
from . import lsp, uris, _utils
1513

@@ -86,6 +84,8 @@ def __init__(self, root_uri, rpc_manager):
8684
self.__rope_config = None
8785

8886
def _rope_project_builder(self, rope_config):
87+
from rope.base.project import Project
88+
8989
# TODO: we could keep track of dirty files and validate only those
9090
if self.__rope is None or self.__rope_config != rope_config:
9191
rope_folder = rope_config.get('ropeFolder')
@@ -169,6 +169,7 @@ def __str__(self):
169169
return str(self.uri)
170170

171171
def _rope_resource(self, rope_config):
172+
from rope.base import libutils
172173
return libutils.path_to_resource(self._rope_project_builder(rope_config), self.path)
173174

174175
@property

setup.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@
3737
'futures; python_version<"3.2"',
3838
'jedi>=0.10',
3939
'json-rpc==1.10.8',
40-
'mccabe',
41-
'pycodestyle',
42-
'pydocstyle>=2.0.0',
43-
'pyflakes>=1.6.0',
44-
'rope>=0.10.5',
45-
'yapf',
4640
'pluggy'
4741
],
4842

@@ -51,6 +45,20 @@
5145
# for example:
5246
# $ pip install -e .[test]
5347
extras_require={
48+
'all': [
49+
'mccabe',
50+
'pycodestyle',
51+
'pydocstyle>=2.0.0',
52+
'pyflakes>=1.6.0',
53+
'rope>-0.10.5',
54+
'yapf',
55+
],
56+
'mccabe': ['mccabe'],
57+
'pycodestyle': ['pycodestyle'],
58+
'pydocstyle': ['pydocstyle>=2.0.0'],
59+
'pyflakes': ['pyflakes>=1.6.0'],
60+
'rope': ['rope>0.10.5'],
61+
'yapf': ['yapf'],
5462
'test': ['tox', 'versioneer', 'pytest', 'mock', 'pytest-cov', 'coverage'],
5563
},
5664

tox.ini

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ deps =
2626
mock
2727
pytest-cov
2828
pylint
29+
extras = all
2930

3031
[testenv:lint]
3132
commands =

0 commit comments

Comments
 (0)