Catch and log all server exceptions
This commit is contained in:
parent
48b7ccef76
commit
fd41d52dfe
@ -23,6 +23,8 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import json
|
import json
|
||||||
@ -41,6 +43,7 @@ _EXECUTOR = UnsafeThreadPoolExecutor( max_workers = 30 )
|
|||||||
# Setting this to None seems to screw up the Requests/urllib3 libs.
|
# Setting this to None seems to screw up the Requests/urllib3 libs.
|
||||||
_DEFAULT_TIMEOUT_SEC = 30
|
_DEFAULT_TIMEOUT_SEC = 30
|
||||||
_HMAC_HEADER = 'x-ycm-hmac'
|
_HMAC_HEADER = 'x-ycm-hmac'
|
||||||
|
_logger = logging.getLogger( __name__ )
|
||||||
|
|
||||||
|
|
||||||
class BaseRequest( object ):
|
class BaseRequest( object ):
|
||||||
@ -193,7 +196,32 @@ def JsonFromFuture( future ):
|
|||||||
return None
|
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 )
|
serialized_exception = str( exception )
|
||||||
|
|
||||||
# We ignore the exception about the file already being parsed since it comes
|
# We ignore the exception about the file already being parsed since it comes
|
||||||
|
@ -23,9 +23,6 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
from requests.exceptions import ReadTimeout
|
|
||||||
|
|
||||||
from ycmd.responses import ServerError
|
|
||||||
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
||||||
HandleServerException )
|
HandleServerException )
|
||||||
from ycm import vimsupport
|
from ycm import vimsupport
|
||||||
@ -53,11 +50,9 @@ class CommandRequest( BaseRequest ):
|
|||||||
'completer_target': self._completer_target,
|
'completer_target': self._completer_target,
|
||||||
'command_arguments': self._arguments
|
'command_arguments': self._arguments
|
||||||
} )
|
} )
|
||||||
try:
|
with HandleServerException():
|
||||||
self._response = self.PostDataToHandler( request_data,
|
self._response = self.PostDataToHandler( request_data,
|
||||||
'run_completer_command' )
|
'run_completer_command' )
|
||||||
except ( ServerError, ReadTimeout ) as e:
|
|
||||||
HandleServerException( e )
|
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
def Response( self ):
|
||||||
|
@ -23,11 +23,8 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
from requests.exceptions import ReadTimeout
|
|
||||||
|
|
||||||
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
||||||
HandleServerException )
|
HandleServerException )
|
||||||
from ycmd.responses import ServerError
|
|
||||||
|
|
||||||
|
|
||||||
class CompleterAvailableRequest( BaseRequest ):
|
class CompleterAvailableRequest( BaseRequest ):
|
||||||
@ -40,11 +37,9 @@ class CompleterAvailableRequest( BaseRequest ):
|
|||||||
def Start( self ):
|
def Start( self ):
|
||||||
request_data = BuildRequestData()
|
request_data = BuildRequestData()
|
||||||
request_data.update( { 'filetypes': self.filetypes } )
|
request_data.update( { 'filetypes': self.filetypes } )
|
||||||
try:
|
with HandleServerException():
|
||||||
self._response = self.PostDataToHandler( request_data,
|
self._response = self.PostDataToHandler( request_data,
|
||||||
'semantic_completion_available' )
|
'semantic_completion_available' )
|
||||||
except ( ServerError, ReadTimeout ) as e:
|
|
||||||
HandleServerException( e )
|
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
def Response( self ):
|
||||||
|
@ -23,13 +23,10 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
from requests.exceptions import ReadTimeout
|
|
||||||
|
|
||||||
from ycmd.utils import ToUnicode
|
from ycmd.utils import ToUnicode
|
||||||
from ycm.client.base_request import ( BaseRequest, JsonFromFuture,
|
from ycm.client.base_request import ( BaseRequest, JsonFromFuture,
|
||||||
HandleServerException,
|
HandleServerException,
|
||||||
MakeServerException )
|
MakeServerException )
|
||||||
from ycmd.responses import ServerError
|
|
||||||
|
|
||||||
TIMEOUT_SECONDS = 0.5
|
TIMEOUT_SECONDS = 0.5
|
||||||
|
|
||||||
@ -53,16 +50,15 @@ class CompletionRequest( BaseRequest ):
|
|||||||
def RawResponse( self ):
|
def RawResponse( self ):
|
||||||
if not self._response_future:
|
if not self._response_future:
|
||||||
return []
|
return []
|
||||||
try:
|
with HandleServerException( truncate = True ):
|
||||||
response = JsonFromFuture( self._response_future )
|
response = JsonFromFuture( self._response_future )
|
||||||
|
|
||||||
errors = response[ 'errors' ] if 'errors' in response else []
|
errors = response[ 'errors' ] if 'errors' in response else []
|
||||||
for e in errors:
|
for e in errors:
|
||||||
HandleServerException( MakeServerException( e ) )
|
with HandleServerException( truncate = True ):
|
||||||
|
raise MakeServerException( e )
|
||||||
|
|
||||||
return JsonFromFuture( self._response_future )[ 'completions' ]
|
return response[ 'completions' ]
|
||||||
except ( ServerError, ReadTimeout ) as e:
|
|
||||||
HandleServerException( e, truncate = True )
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
52
python/ycm/client/debug_info_request.py
Executable file
52
python/ycm/client/debug_info_request.py
Executable file
@ -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()
|
@ -23,10 +23,6 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
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,
|
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
||||||
JsonFromFuture, HandleServerException )
|
JsonFromFuture, HandleServerException )
|
||||||
|
|
||||||
@ -61,16 +57,8 @@ class EventNotification( BaseRequest ):
|
|||||||
if not self._response_future or self._event_name != 'FileReadyToParse':
|
if not self._response_future or self._event_name != 'FileReadyToParse':
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
with HandleServerException( truncate = True ):
|
||||||
try:
|
self._cached_response = JsonFromFuture( self._response_future )
|
||||||
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 )
|
|
||||||
|
|
||||||
return self._cached_response if self._cached_response else []
|
return self._cached_response if self._cached_response else []
|
||||||
|
|
||||||
@ -80,13 +68,3 @@ def SendEventNotificationAsync( event_name,
|
|||||||
extra_data = None ):
|
extra_data = None ):
|
||||||
event = EventNotification( event_name, filepath, extra_data )
|
event = EventNotification( event_name, filepath, extra_data )
|
||||||
event.Start()
|
event.Start()
|
||||||
|
|
||||||
|
|
||||||
def _LoadExtraConfFile( filepath ):
|
|
||||||
BaseRequest.PostDataToHandler( { 'filepath': filepath },
|
|
||||||
'load_extra_conf_file' )
|
|
||||||
|
|
||||||
|
|
||||||
def _IgnoreExtraConfFile( filepath ):
|
|
||||||
BaseRequest.PostDataToHandler( { 'filepath': filepath },
|
|
||||||
'ignore_extra_conf_file' )
|
|
||||||
|
@ -23,9 +23,7 @@ from future import standard_library
|
|||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
from builtins import * # noqa
|
from builtins import * # noqa
|
||||||
|
|
||||||
from requests.exceptions import ReadTimeout
|
from ycm.client.base_request import BaseRequest, HandleServerException
|
||||||
|
|
||||||
from ycm.client.base_request import BaseRequest
|
|
||||||
|
|
||||||
TIMEOUT_SECONDS = 0.1
|
TIMEOUT_SECONDS = 0.1
|
||||||
|
|
||||||
@ -36,12 +34,8 @@ class ShutdownRequest( BaseRequest ):
|
|||||||
|
|
||||||
|
|
||||||
def Start( self ):
|
def Start( self ):
|
||||||
try:
|
with HandleServerException( display = False ):
|
||||||
self.PostDataToHandler( {},
|
self.PostDataToHandler( {}, 'shutdown', TIMEOUT_SECONDS )
|
||||||
'shutdown',
|
|
||||||
TIMEOUT_SECONDS )
|
|
||||||
except ReadTimeout:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def SendShutdownRequest():
|
def SendShutdownRequest():
|
||||||
|
@ -26,7 +26,6 @@ from builtins import * # noqa
|
|||||||
import vim
|
import vim
|
||||||
from ycm import vimsupport
|
from ycm import vimsupport
|
||||||
from ycmd import utils
|
from ycmd import utils
|
||||||
from ycmd.responses import ServerError
|
|
||||||
from ycmd.completers.completer import Completer
|
from ycmd.completers.completer import Completer
|
||||||
from ycm.client.base_request import BaseRequest, HandleServerException
|
from ycm.client.base_request import BaseRequest, HandleServerException
|
||||||
|
|
||||||
@ -115,9 +114,7 @@ class OmniCompleter( Completer ):
|
|||||||
'query': query
|
'query': query
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
with HandleServerException():
|
||||||
return BaseRequest.PostDataToHandler( request_data,
|
return BaseRequest.PostDataToHandler( request_data,
|
||||||
'filter_and_sort_candidates' )
|
'filter_and_sort_candidates' )
|
||||||
except ServerError as e:
|
return candidates
|
||||||
HandleServerException( e )
|
|
||||||
return candidates
|
|
||||||
|
@ -128,13 +128,13 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
|
|||||||
|
|
||||||
# The first call raises a warning
|
# The first call raises a warning
|
||||||
post_vim_message.assert_has_exact_calls( [
|
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
|
# Subsequent calls don't re-raise the warning
|
||||||
ycm.HandleFileParseRequest()
|
ycm.HandleFileParseRequest()
|
||||||
post_vim_message.assert_has_exact_calls( [
|
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
|
# But it does if a subsequent event raises again
|
||||||
@ -142,8 +142,8 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
|
|||||||
ok_( ycm.FileParseRequestReady() )
|
ok_( ycm.FileParseRequestReady() )
|
||||||
ycm.HandleFileParseRequest()
|
ycm.HandleFileParseRequest()
|
||||||
post_vim_message.assert_has_exact_calls( [
|
post_vim_message.assert_has_exact_calls( [
|
||||||
call( ERROR_TEXT, truncate = False ),
|
call( ERROR_TEXT, truncate = True ),
|
||||||
call( ERROR_TEXT, truncate = False )
|
call( ERROR_TEXT, truncate = True )
|
||||||
] )
|
] )
|
||||||
|
|
||||||
|
|
||||||
@ -159,9 +159,9 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test(
|
|||||||
vim_command.assert_not_called()
|
vim_command.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
@patch( 'ycm.client.event_notification._LoadExtraConfFile',
|
@patch( 'ycm.client.base_request._LoadExtraConfFile',
|
||||||
new_callable = ExtendedMock )
|
new_callable = ExtendedMock )
|
||||||
@patch( 'ycm.client.event_notification._IgnoreExtraConfFile',
|
@patch( 'ycm.client.base_request._IgnoreExtraConfFile',
|
||||||
new_callable = ExtendedMock )
|
new_callable = ExtendedMock )
|
||||||
@YouCompleteMeInstance()
|
@YouCompleteMeInstance()
|
||||||
def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
|
def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
|
||||||
|
@ -38,16 +38,17 @@ from ycm import base, paths, vimsupport
|
|||||||
from ycmd import utils
|
from ycmd import utils
|
||||||
from ycmd import server_utils
|
from ycmd import server_utils
|
||||||
from ycmd.request_wrap import RequestWrap
|
from ycmd.request_wrap import RequestWrap
|
||||||
from ycmd.responses import ServerError
|
|
||||||
from ycm.diagnostic_interface import DiagnosticInterface
|
from ycm.diagnostic_interface import DiagnosticInterface
|
||||||
from ycm.omni_completer import OmniCompleter
|
from ycm.omni_completer import OmniCompleter
|
||||||
from ycm import syntax_parse
|
from ycm import syntax_parse
|
||||||
from ycm.client.ycmd_keepalive import YcmdKeepalive
|
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.completer_available_request import SendCompleterAvailableRequest
|
||||||
from ycm.client.command_request import SendCommandRequest
|
from ycm.client.command_request import SendCommandRequest
|
||||||
from ycm.client.completion_request import ( CompletionRequest,
|
from ycm.client.completion_request import ( CompletionRequest,
|
||||||
ConvertCompletionDataToVimData )
|
ConvertCompletionDataToVimData )
|
||||||
|
from ycm.client.debug_info_request import SendDebugInfoRequest
|
||||||
from ycm.client.omni_completion_request import OmniCompletionRequest
|
from ycm.client.omni_completion_request import OmniCompletionRequest
|
||||||
from ycm.client.event_notification import ( SendEventNotificationAsync,
|
from ycm.client.event_notification import ( SendEventNotificationAsync,
|
||||||
EventNotification )
|
EventNotification )
|
||||||
@ -302,13 +303,10 @@ class YouCompleteMe( object ):
|
|||||||
|
|
||||||
def GetDefinedSubcommands( self ):
|
def GetDefinedSubcommands( self ):
|
||||||
if self.IsServerAlive():
|
if self.IsServerAlive():
|
||||||
try:
|
with HandleServerException():
|
||||||
return BaseRequest.PostDataToHandler( BuildRequestData(),
|
return BaseRequest.PostDataToHandler( BuildRequestData(),
|
||||||
'defined_subcommands' )
|
'defined_subcommands' )
|
||||||
except ServerError:
|
return []
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def GetCurrentCompletionRequest( self ):
|
def GetCurrentCompletionRequest( self ):
|
||||||
@ -636,14 +634,13 @@ class YouCompleteMe( object ):
|
|||||||
def ShowDetailedDiagnostic( self ):
|
def ShowDetailedDiagnostic( self ):
|
||||||
if not self.IsServerAlive():
|
if not self.IsServerAlive():
|
||||||
return
|
return
|
||||||
try:
|
with HandleServerException():
|
||||||
debug_info = BaseRequest.PostDataToHandler( BuildRequestData(),
|
detailed_diagnostic = BaseRequest.PostDataToHandler(
|
||||||
'detailed_diagnostic' )
|
BuildRequestData(), 'detailed_diagnostic' )
|
||||||
if 'message' in debug_info:
|
|
||||||
vimsupport.PostVimMessage( debug_info[ 'message' ],
|
if 'message' in detailed_diagnostic:
|
||||||
|
vimsupport.PostVimMessage( detailed_diagnostic[ 'message' ],
|
||||||
warning = False )
|
warning = False )
|
||||||
except ServerError as e:
|
|
||||||
vimsupport.PostVimMessage( str( e ) )
|
|
||||||
|
|
||||||
|
|
||||||
def DebugInfo( self ):
|
def DebugInfo( self ):
|
||||||
@ -651,8 +648,7 @@ class YouCompleteMe( object ):
|
|||||||
if self._client_logfile:
|
if self._client_logfile:
|
||||||
debug_info += 'Client logfile: {0}\n'.format( self._client_logfile )
|
debug_info += 'Client logfile: {0}\n'.format( self._client_logfile )
|
||||||
if self.IsServerAlive():
|
if self.IsServerAlive():
|
||||||
debug_info += BaseRequest.PostDataToHandler( BuildRequestData(),
|
debug_info += SendDebugInfoRequest()
|
||||||
'debug_info' )
|
|
||||||
else:
|
else:
|
||||||
debug_info += 'Server crashed, no debug info from server'
|
debug_info += 'Server crashed, no debug info from server'
|
||||||
debug_info += '\nServer running at: {0}\n'.format(
|
debug_info += '\nServer running at: {0}\n'.format(
|
||||||
|
Loading…
Reference in New Issue
Block a user