Quellcode durchsuchen

Catch and log all server exceptions

micbou vor 8 Jahren
Ursprung
Commit
fd41d52dfe

+ 29 - 1
python/ycm/client/base_request.py

@@ -23,6 +23,8 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
+import contextlib
+import logging
 import requests
 import urllib.parse
 import json
@@ -41,6 +43,7 @@ _EXECUTOR = UnsafeThreadPoolExecutor( max_workers = 30 )
 # Setting this to None seems to screw up the Requests/urllib3 libs.
 _DEFAULT_TIMEOUT_SEC = 30
 _HMAC_HEADER = 'x-ycm-hmac'
+_logger = logging.getLogger( __name__ )
 
 
 class BaseRequest( object ):
@@ -193,7 +196,32 @@ def JsonFromFuture( future ):
   return None
 
 
-def HandleServerException( exception, truncate = False ):
+@contextlib.contextmanager
+def HandleServerException( display = True, truncate = False ):
+  try:
+    yield
+  except UnknownExtraConf as e:
+    if vimsupport.Confirm( str( e ) ):
+      _LoadExtraConfFile( e.extra_conf_file )
+    else:
+      _IgnoreExtraConfFile( e.extra_conf_file )
+  except Exception as e:
+    _logger.exception( 'Error while handling server response' )
+    if display:
+      DisplayServerException( e, truncate )
+
+
+def _LoadExtraConfFile( filepath ):
+  BaseRequest.PostDataToHandler( { 'filepath': filepath },
+                                 'load_extra_conf_file' )
+
+
+def _IgnoreExtraConfFile( filepath ):
+  BaseRequest.PostDataToHandler( { 'filepath': filepath },
+                                 'ignore_extra_conf_file' )
+
+
+def DisplayServerException( exception, truncate = False ):
   serialized_exception = str( exception )
 
   # We ignore the exception about the file already being parsed since it comes

+ 1 - 6
python/ycm/client/command_request.py

@@ -23,9 +23,6 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
-from requests.exceptions import ReadTimeout
-
-from ycmd.responses import ServerError
 from ycm.client.base_request import ( BaseRequest, BuildRequestData,
                                       HandleServerException )
 from ycm import vimsupport
@@ -53,11 +50,9 @@ class CommandRequest( BaseRequest ):
       'completer_target': self._completer_target,
       'command_arguments': self._arguments
     } )
-    try:
+    with HandleServerException():
       self._response = self.PostDataToHandler( request_data,
                                                'run_completer_command' )
-    except ( ServerError, ReadTimeout ) as e:
-      HandleServerException( e )
 
 
   def Response( self ):

+ 1 - 6
python/ycm/client/completer_available_request.py

@@ -23,11 +23,8 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
-from requests.exceptions import ReadTimeout
-
 from ycm.client.base_request import ( BaseRequest, BuildRequestData,
                                       HandleServerException )
-from ycmd.responses import ServerError
 
 
 class CompleterAvailableRequest( BaseRequest ):
@@ -40,11 +37,9 @@ class CompleterAvailableRequest( BaseRequest ):
   def Start( self ):
     request_data = BuildRequestData()
     request_data.update( { 'filetypes': self.filetypes } )
-    try:
+    with HandleServerException():
       self._response = self.PostDataToHandler( request_data,
                                                'semantic_completion_available' )
-    except ( ServerError, ReadTimeout ) as e:
-      HandleServerException( e )
 
 
   def Response( self ):

+ 4 - 8
python/ycm/client/completion_request.py

@@ -23,13 +23,10 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
-from requests.exceptions import ReadTimeout
-
 from ycmd.utils import ToUnicode
 from ycm.client.base_request import ( BaseRequest, JsonFromFuture,
                                       HandleServerException,
                                       MakeServerException )
-from ycmd.responses import ServerError
 
 TIMEOUT_SECONDS = 0.5
 
@@ -53,16 +50,15 @@ class CompletionRequest( BaseRequest ):
   def RawResponse( self ):
     if not self._response_future:
       return []
-    try:
+    with HandleServerException( truncate = True ):
       response = JsonFromFuture( self._response_future )
 
       errors = response[ 'errors' ] if 'errors' in response else []
       for e in errors:
-        HandleServerException( MakeServerException( e ) )
+        with HandleServerException( truncate = True ):
+          raise MakeServerException( e )
 
-      return JsonFromFuture( self._response_future )[ 'completions' ]
-    except ( ServerError, ReadTimeout ) as e:
-      HandleServerException( e, truncate = True )
+      return response[ 'completions' ]
     return []
 
 

+ 52 - 0
python/ycm/client/debug_info_request.py

@@ -0,0 +1,52 @@
+# Copyright (C) 2016 YouCompleteMe contributors
+#
+# This file is part of YouCompleteMe.
+#
+# YouCompleteMe is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# YouCompleteMe is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from future import standard_library
+standard_library.install_aliases()
+from builtins import *  # noqa
+
+from ycm.client.base_request import ( BaseRequest, BuildRequestData,
+                                      HandleServerException )
+
+
+class DebugInfoRequest( BaseRequest ):
+  def __init__( self ):
+    super( DebugInfoRequest, self ).__init__()
+    self._response = None
+
+
+  def Start( self ):
+    request_data = BuildRequestData()
+    with HandleServerException( display = False ):
+      self._response = self.PostDataToHandler( request_data, 'debug_info' )
+
+
+  def Response( self ):
+    if not self._response:
+      return 'Server errored, no debug info from server'
+    return self._response
+
+
+def SendDebugInfoRequest():
+  request = DebugInfoRequest()
+  # This is a blocking call.
+  request.Start()
+  return request.Response()

+ 2 - 24
python/ycm/client/event_notification.py

@@ -23,10 +23,6 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
-from requests.exceptions import ReadTimeout
-
-from ycm import vimsupport
-from ycmd.responses import UnknownExtraConf, ServerError
 from ycm.client.base_request import ( BaseRequest, BuildRequestData,
                                       JsonFromFuture, HandleServerException )
 
@@ -61,16 +57,8 @@ class EventNotification( BaseRequest ):
     if not self._response_future or self._event_name != 'FileReadyToParse':
       return []
 
-    try:
-      try:
-        self._cached_response = JsonFromFuture( self._response_future )
-      except UnknownExtraConf as e:
-          if vimsupport.Confirm( str( e ) ):
-            _LoadExtraConfFile( e.extra_conf_file )
-          else:
-            _IgnoreExtraConfFile( e.extra_conf_file )
-    except ( ServerError, ReadTimeout ) as e:
-      HandleServerException( e )
+    with HandleServerException( truncate = True ):
+      self._cached_response = JsonFromFuture( self._response_future )
 
     return self._cached_response if self._cached_response else []
 
@@ -80,13 +68,3 @@ def SendEventNotificationAsync( event_name,
                                 extra_data = None ):
   event = EventNotification( event_name, filepath, extra_data )
   event.Start()
-
-
-def _LoadExtraConfFile( filepath ):
-  BaseRequest.PostDataToHandler( { 'filepath': filepath },
-                                 'load_extra_conf_file' )
-
-
-def _IgnoreExtraConfFile( filepath ):
-  BaseRequest.PostDataToHandler( { 'filepath': filepath },
-                                 'ignore_extra_conf_file' )

+ 3 - 9
python/ycm/client/shutdown_request.py

@@ -23,9 +23,7 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import *  # noqa
 
-from requests.exceptions import ReadTimeout
-
-from ycm.client.base_request import BaseRequest
+from ycm.client.base_request import BaseRequest, HandleServerException
 
 TIMEOUT_SECONDS = 0.1
 
@@ -36,12 +34,8 @@ class ShutdownRequest( BaseRequest ):
 
 
   def Start( self ):
-    try:
-      self.PostDataToHandler( {},
-                              'shutdown',
-                              TIMEOUT_SECONDS )
-    except ReadTimeout:
-      pass
+    with HandleServerException( display = False ):
+      self.PostDataToHandler( {}, 'shutdown', TIMEOUT_SECONDS )
 
 
 def SendShutdownRequest():

+ 2 - 5
python/ycm/omni_completer.py

@@ -26,7 +26,6 @@ from builtins import *  # noqa
 import vim
 from ycm import vimsupport
 from ycmd import utils
-from ycmd.responses import ServerError
 from ycmd.completers.completer import Completer
 from ycm.client.base_request import BaseRequest, HandleServerException
 
@@ -115,9 +114,7 @@ class OmniCompleter( Completer ):
       'query': query
     }
 
-    try:
+    with HandleServerException():
       return BaseRequest.PostDataToHandler( request_data,
                                             'filter_and_sort_candidates' )
-    except ServerError as e:
-      HandleServerException( e )
-      return candidates
+    return candidates

+ 6 - 6
python/ycm/tests/event_notification_test.py

@@ -128,13 +128,13 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
 
       # The first call raises a warning
       post_vim_message.assert_has_exact_calls( [
-        call( ERROR_TEXT, truncate = False )
+        call( ERROR_TEXT, truncate = True )
       ] )
 
       # Subsequent calls don't re-raise the warning
       ycm.HandleFileParseRequest()
       post_vim_message.assert_has_exact_calls( [
-        call( ERROR_TEXT, truncate = False )
+        call( ERROR_TEXT, truncate = True )
       ] )
 
       # But it does if a subsequent event raises again
@@ -142,8 +142,8 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
       ok_( ycm.FileParseRequestReady() )
       ycm.HandleFileParseRequest()
       post_vim_message.assert_has_exact_calls( [
-        call( ERROR_TEXT, truncate = False ),
-        call( ERROR_TEXT, truncate = False )
+        call( ERROR_TEXT, truncate = True ),
+        call( ERROR_TEXT, truncate = True )
       ] )
 
 
@@ -159,9 +159,9 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test(
       vim_command.assert_not_called()
 
 
-@patch( 'ycm.client.event_notification._LoadExtraConfFile',
+@patch( 'ycm.client.base_request._LoadExtraConfFile',
         new_callable = ExtendedMock )
-@patch( 'ycm.client.event_notification._IgnoreExtraConfFile',
+@patch( 'ycm.client.base_request._IgnoreExtraConfFile',
         new_callable = ExtendedMock )
 @YouCompleteMeInstance()
 def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(

+ 13 - 17
python/ycm/youcompleteme.py

@@ -38,16 +38,17 @@ from ycm import base, paths, vimsupport
 from ycmd import utils
 from ycmd import server_utils
 from ycmd.request_wrap import RequestWrap
-from ycmd.responses import ServerError
 from ycm.diagnostic_interface import DiagnosticInterface
 from ycm.omni_completer import OmniCompleter
 from ycm import syntax_parse
 from ycm.client.ycmd_keepalive import YcmdKeepalive
-from ycm.client.base_request import BaseRequest, BuildRequestData
+from ycm.client.base_request import ( BaseRequest, BuildRequestData,
+                                      HandleServerException )
 from ycm.client.completer_available_request import SendCompleterAvailableRequest
 from ycm.client.command_request import SendCommandRequest
 from ycm.client.completion_request import ( CompletionRequest,
                                             ConvertCompletionDataToVimData )
+from ycm.client.debug_info_request import SendDebugInfoRequest
 from ycm.client.omni_completion_request import OmniCompletionRequest
 from ycm.client.event_notification import ( SendEventNotificationAsync,
                                             EventNotification )
@@ -302,13 +303,10 @@ class YouCompleteMe( object ):
 
   def GetDefinedSubcommands( self ):
     if self.IsServerAlive():
-      try:
+      with HandleServerException():
         return BaseRequest.PostDataToHandler( BuildRequestData(),
-                                             'defined_subcommands' )
-      except ServerError:
-        return []
-    else:
-      return []
+                                              'defined_subcommands' )
+    return []
 
 
   def GetCurrentCompletionRequest( self ):
@@ -636,14 +634,13 @@ class YouCompleteMe( object ):
   def ShowDetailedDiagnostic( self ):
     if not self.IsServerAlive():
       return
-    try:
-      debug_info = BaseRequest.PostDataToHandler( BuildRequestData(),
-                                                  'detailed_diagnostic' )
-      if 'message' in debug_info:
-        vimsupport.PostVimMessage( debug_info[ 'message' ],
+    with HandleServerException():
+      detailed_diagnostic = BaseRequest.PostDataToHandler(
+          BuildRequestData(), 'detailed_diagnostic' )
+
+      if 'message' in detailed_diagnostic:
+        vimsupport.PostVimMessage( detailed_diagnostic[ 'message' ],
                                    warning = False )
-    except ServerError as e:
-      vimsupport.PostVimMessage( str( e ) )
 
 
   def DebugInfo( self ):
@@ -651,8 +648,7 @@ class YouCompleteMe( object ):
     if self._client_logfile:
       debug_info += 'Client logfile: {0}\n'.format( self._client_logfile )
     if self.IsServerAlive():
-      debug_info += BaseRequest.PostDataToHandler( BuildRequestData(),
-                                                   'debug_info' )
+      debug_info += SendDebugInfoRequest()
     else:
       debug_info += 'Server crashed, no debug info from server'
     debug_info += '\nServer running at: {0}\n'.format(