diff --git a/pyls/plugins/preload_imports.py b/pyls/plugins/preload_imports.py new file mode 100644 index 00000000..b00552cf --- /dev/null +++ b/pyls/plugins/preload_imports.py @@ -0,0 +1,34 @@ +# Copyright 2017 Palantir Technologies, Inc. +import logging +from pyls import hookimpl + +log = logging.getLogger(__name__) + +MODULES = [ + "OpenGL", "PIL", + "array", "audioop", "binascii", "cPickle", "cStringIO", "cmath", "collections", + "datetime", "errno", "exceptions", "gc", "imageop", "imp", "itertools", + "marshal", "math", "matplotlib", "mmap", "mpmath", "msvcrt", "networkx", "nose", "nt", + "numpy", "operator", "os", "os.path", "pandas", "parser", "rgbimg", "scipy", "signal", + "skimage", "sklearn", "statsmodels", "strop", "sympy", "sys", "thread", "time", + "wx", "xxsubtype", "zipimport", "zlib" +] + + +@hookimpl +def pyls_settings(): + # Setup default modules to preload, and rope extension modules + return { + 'plugins': {'preload': {'modules': MODULES}}, + 'rope': {'extensionModules': MODULES} + } + + +@hookimpl +def pyls_initialize(config): + for mod_name in config.plugin_settings('preload').get('modules', []): + try: + __import__(mod_name) + log.debug("Preloaded module %s", mod_name) + except ImportError: + pass diff --git a/pyls/workspace.py b/pyls/workspace.py index edb9528f..c731f670 100644 --- a/pyls/workspace.py +++ b/pyls/workspace.py @@ -3,8 +3,6 @@ import logging import os import re -import imp -import pkgutil import jedi @@ -17,59 +15,11 @@ RE_END_WORD = re.compile('^[A-Za-z_0-9]*') -def get_submodules(mod): - """Get all submodules of a given module""" - def catch_exceptions(_module): - pass - - try: - m = __import__(mod) - submodules = [mod] - submods = pkgutil.walk_packages(m.__path__, m.__name__ + '.', catch_exceptions) - for sm in submods: - sm_name = sm[1] - submodules.append(sm_name) - except ImportError: - return [] - except: # pylint: disable=bare-except - return [mod] - return submodules - - -def get_preferred_submodules(): - mods = ['numpy', 'scipy', 'sympy', 'pandas', - 'networkx', 'statsmodels', 'matplotlib', 'sklearn', - 'skimage', 'mpmath', 'os', 'PIL', - 'OpenGL', 'array', 'audioop', 'binascii', 'cPickle', - 'cStringIO', 'cmath', 'collections', 'datetime', - 'errno', 'exceptions', 'gc', 'imageop', 'imp', - 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', - 'nt', 'operator', 'parser', 'rgbimg', 'signal', - 'strop', 'sys', 'thread', 'time', 'wx', 'xxsubtype', - 'zipimport', 'zlib', 'nose', 'os.path'] - - submodules = [] - for mod in mods: - submods = get_submodules(mod) - submodules += submods - - actual = [] - for submod in submodules: - try: - imp.find_module(submod) - actual.append(submod) - except ImportError: - pass - - return actual - - class Workspace(object): M_PUBLISH_DIAGNOSTICS = 'textDocument/publishDiagnostics' M_APPLY_EDIT = 'workspace/applyEdit' M_SHOW_MESSAGE = 'window/showMessage' - PRELOADED_MODULES = get_preferred_submodules() def __init__(self, root_uri, endpoint): self._root_uri = root_uri @@ -89,7 +39,7 @@ def _rope_project_builder(self, rope_config): if self.__rope is None or self.__rope_config != rope_config: rope_folder = rope_config.get('ropeFolder') self.__rope = Project(self._root_path, ropefolder=rope_folder) - self.__rope.prefs.set('extension_modules', self.PRELOADED_MODULES) + self.__rope.prefs.set('extension_modules', rope_config.get('extensionModules', [])) self.__rope.prefs.set('ignore_syntax_errors', True) self.__rope.prefs.set('ignore_bad_imports', True) self.__rope.validate() diff --git a/setup.py b/setup.py index 4f40cb93..a8657bb4 100755 --- a/setup.py +++ b/setup.py @@ -80,6 +80,7 @@ 'jedi_signature_help = pyls.plugins.signature', 'jedi_symbols = pyls.plugins.symbols', 'mccabe = pyls.plugins.mccabe_lint', + 'preload = pyls.plugins.preload_imports', 'pycodestyle = pyls.plugins.pycodestyle_lint', 'pydocstyle = pyls.plugins.pydocstyle_lint', 'pyflakes = pyls.plugins.pyflakes_lint', diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index ced3d1d6..132b720a 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -1,9 +1,8 @@ # Copyright 2017 Palantir Technologies, Inc. import os -from rope.base.project import Project from pyls import uris -from pyls.workspace import Document, get_preferred_submodules +from pyls.workspace import Document from pyls.plugins.jedi_completion import pyls_completions as pyls_jedi_completions from pyls.plugins.rope_completion import pyls_completions as pyls_rope_completions @@ -47,8 +46,6 @@ def test_jedi_completion(): def test_rope_completion(config, workspace): # Over 'i' in os.path.isabs(...) com_position = {'line': 1, 'character': 15} - rope = Project(LOCATION) - rope.prefs.set('extension_modules', get_preferred_submodules()) workspace.put_document(DOC_URI, source=DOC) doc = workspace.get_document(DOC_URI) items = pyls_rope_completions(config, workspace, doc, com_position) diff --git a/vscode-client/package.json b/vscode-client/package.json index c9073e82..dc00fecf 100644 --- a/vscode-client/package.json +++ b/vscode-client/package.json @@ -75,6 +75,20 @@ "default": 15, "description": "The minimum threshold that triggers warnings about cyclomatic complexity." }, + "pyls.plugins.preload.enabled": { + "type": "boolean", + "default": true, + "description": "Enable or disable the plugin." + }, + "pyls.plugins.preload.modules": { + "type": "array", + "default": null, + "items": { + "type": "string" + }, + "uniqueItems": true, + "description": "List of modules to import on startup" + }, "pyls.plugins.pycodestyle.enabled": { "type": "boolean", "default": true, @@ -201,10 +215,19 @@ "default": true, "description": "Enable or disable the plugin." }, - "pyls.rope.ropeFolder": { + "pyls.rope.extensionModules": { "type": "string", - "default": ".ropeproject", + "default": null, "description": "The name of the folder in which rope stores project configurations and data. Pass `null` for not using such a folder at all." + }, + "pyls.rope.ropeFolder": { + "type": "array", + "default": null, + "items": { + "type": "string" + }, + "uniqueItems": true, + "description": "Builtin and c-extension modules that are allowed to be imported and inspected by rope." } } }