From ef161ff02a465c0e6a8cfcbc8da7f474f27c6a91 Mon Sep 17 00:00:00 2001 From: Ches Martin Date: Fri, 6 Jan 2017 20:55:29 +0700 Subject: [PATCH] Fail fast if our python dependencies aren't installed We were jumping through some weird hoops to avoid early imports that might have prevented the plugin from loading entirely, with bad error messaging. But that resulted in the plugin getting a lot further into execution than it should (including starting the server), since it can't do anything useful without these deps installed. I came up with a way to validate the deps before our Python modules load, so now we can import normally in our code. Closes #236 --- ensime_shared/client.py | 28 ++++++---------------- ensime_shared/config.py | 1 - ensime_shared/util.py | 8 ------- plugin/ensime.vim | 52 ++++++++++++++++++++++++++++++----------- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/ensime_shared/client.py b/ensime_shared/client.py index 837d52f..89bd823 100644 --- a/ensime_shared/client.py +++ b/ensime_shared/client.py @@ -11,12 +11,14 @@ from subprocess import PIPE, Popen from threading import Thread +import websocket + from .config import feedback, gconfig, LOG_FORMAT from .debugger import DebuggerClient from .errors import InvalidJavaPathError from .protocol import ProtocolHandler, ProtocolHandlerV1, ProtocolHandlerV2 from .typecheck import TypecheckHandler -from .util import catch, module_exists, Pretty, Util +from .util import catch, Pretty, Util # Queue depends on python version if sys.version_info > (3, 0): @@ -121,12 +123,6 @@ def setup_logger(): thread.daemon = True thread.start() - self.websocket_exists = module_exists("websocket") - if not self.websocket_exists: - self.tell_module_missing("websocket-client") - if not module_exists("sexpdata"): - self.tell_module_missing("sexpdata") - def queue_poll(self, sleep_t=0.5): """Put new messages on the queue as they arrive. Blocking in a thread. @@ -148,9 +144,7 @@ def logger_and_close(msg): self.teardown() self._display_ws_warning() - # WebSocket exception may happen - # FIXME: What Exception class? Don't catch Exception - with catch(Exception, logger_and_close): + with catch(websocket.WebSocketException, logger_and_close): result = self.ws.recv() self.queue.put(result) @@ -182,8 +176,6 @@ def lazy_initialize_ensime(): return bool(self.ensime) def ready_to_connect(): - if not self.websocket_exists: - return False if not self.ws and self.ensime.is_ready(): self.connect_ensime_server() return True @@ -191,11 +183,6 @@ def ready_to_connect(): # True if ensime is up and connection is ok, otherwise False return self.running and lazy_initialize_ensime() and ready_to_connect() - def tell_module_missing(self, name): - """Warn users that a module is not available in their machines.""" - msg = feedback["module_missing"] - self.editor.raw_message(msg.format(name, name)) - def _display_ws_warning(self): warning = "A WS exception happened, 'ensime-vim' has been disabled. " +\ "For more information, have a look at the logs in `.ensime_cache`" @@ -211,7 +198,7 @@ def reconnect(e): self.log.debug('send: in') if self.running and self.ws: - with catch(Exception, reconnect): # FIXME: what Exception?? + with catch(websocket.WebSocketException, reconnect): self.log.debug('send: sending JSON on WebSocket') self.ws.send(msg + "\n") @@ -232,14 +219,13 @@ def disable_completely(e): port = self.ensime.http_port() uri = "websocket" if server_v2 else "jerky" self.ensime_server = gconfig["ensime_server"].format(port, uri) - with catch(Exception, disable_completely): - from websocket import create_connection + with catch(websocket.WebSocketException, disable_completely): # Use the default timeout (no timeout). options = {"subprotocols": ["jerky"]} if server_v2 else {} options['enable_multithread'] = True self.log.debug("About to connect to %s with options %s", self.ensime_server, options) - self.ws = create_connection(self.ensime_server, **options) + self.ws = websocket.create_connection(self.ensime_server, **options) if self.ws: self.send_request({"typehint": "ConnectionInfoReq"}) else: diff --git a/ensime_shared/config.py b/ensime_shared/config.py index ca7a41e..ea33031 100644 --- a/ensime_shared/config.py +++ b/ensime_shared/config.py @@ -29,7 +29,6 @@ "invalid_java": "Java not found or not executable, verify :java-home in your .ensime config", "manual_doc": "Go to {}", "missing_debug_class": "You must specify a class to debug", - "module_missing": "{} missing: do a `pip install {}` and restart vim", "notify_break": "Execution paused at breakpoint line {} in {}", "package_inspect_current": "Using currently focused package...", "prompt_server_install": diff --git a/ensime_shared/util.py b/ensime_shared/util.py index 866df7c..a2c4324 100644 --- a/ensime_shared/util.py +++ b/ensime_shared/util.py @@ -51,14 +51,6 @@ def catch(exception, handler=lambda e: None): handler(str(e)) -def module_exists(module_name): - res = False - with catch(ImportError): - __import__(module_name) - res = True - return res - - class Pretty(object): """Wrapper to pretty-format object's string representation. diff --git a/plugin/ensime.vim b/plugin/ensime.vim index af3cf55..6e55e16 100644 --- a/plugin/ensime.vim +++ b/plugin/ensime.vim @@ -1,22 +1,48 @@ if exists('g:loaded_ensime') || &cp finish -else - if !has('python') - echohl WarningMsg - echomsg '[ensime] Your Vim build is missing +python support, ensime-vim will not be loaded.' - if has('nvim') - echomsg '[ensime] Did you remember to `pip2 install neovim`?' - else - echomsg '[ensime] Please review the installation guide.' - endif - echohl None - finish +endif + +function! s:Warn(msg) + echohl WarningMsg | echomsg '[ensime] ' . a:msg | echohl None +endf + +if !has('python') + call s:Warn('Your Vim build is missing +python support, ensime-vim will not be loaded.') + if has('nvim') + call s:Warn('Did you remember to `pip2 install neovim`?') + else + call s:Warn('Please review the installation guide.') endif + finish +endif + +" Fail fast if dependencies are missing, we can't do much useful if so. +" We need to wrap this in a function, see :help script-here +function! s:DependenciesValid() abort + python <"))