Merge pull request #13689 from ncteisen/surfacing-error-details-python

Surfacing Debug Error String Python
pull/15466/head
Noah Eisen 7 years ago committed by GitHub
commit a506d46650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/python/grpcio/grpc/_channel.py
  2. 1
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
  3. 2
      src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi
  4. 10
      src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi
  5. 8
      src/python/grpcio_tests/tests/unit/_rpc_test.py

@ -58,6 +58,17 @@ _STREAM_STREAM_INITIAL_DUE = (
_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
'Exception calling channel subscription callback!') 'Exception calling channel subscription callback!')
_OK_RENDEZVOUS_REPR_FORMAT = ('<_Rendezvous of RPC that terminated with:\n'
'\tstatus = {}\n'
'\tdetails = "{}"\n'
'>')
_NON_OK_RENDEZVOUS_REPR_FORMAT = ('<_Rendezvous of RPC that terminated with:\n'
'\tstatus = {}\n'
'\tdetails = "{}"\n'
'\tdebug_error_string = "{}"\n'
'>')
def _deadline(timeout): def _deadline(timeout):
return None if timeout is None else time.time() + timeout return None if timeout is None else time.time() + timeout
@ -91,6 +102,7 @@ class _RPCState(object):
self.trailing_metadata = trailing_metadata self.trailing_metadata = trailing_metadata
self.code = code self.code = code
self.details = details self.details = details
self.debug_error_string = None
# The semantics of grpc.Future.cancel and grpc.Future.cancelled are # The semantics of grpc.Future.cancel and grpc.Future.cancelled are
# slightly wonky, so they have to be tracked separately from the rest of the # slightly wonky, so they have to be tracked separately from the rest of the
# result of the RPC. This field tracks whether cancellation was requested # result of the RPC. This field tracks whether cancellation was requested
@ -137,6 +149,7 @@ def _handle_event(event, state, response_deserializer):
else: else:
state.code = code state.code = code
state.details = batch_operation.details() state.details = batch_operation.details()
state.debug_error_string = batch_operation.error_string()
callbacks.extend(state.callbacks) callbacks.extend(state.callbacks)
state.callbacks = None state.callbacks = None
return callbacks return callbacks
@ -374,13 +387,23 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
self._state.condition.wait() self._state.condition.wait()
return _common.decode(self._state.details) return _common.decode(self._state.details)
def debug_error_string(self):
with self._state.condition:
while self._state.debug_error_string is None:
self._state.condition.wait()
return _common.decode(self._state.debug_error_string)
def _repr(self): def _repr(self):
with self._state.condition: with self._state.condition:
if self._state.code is None: if self._state.code is None:
return '<_Rendezvous object of in-flight RPC>' return '<_Rendezvous object of in-flight RPC>'
elif self._state.code is grpc.StatusCode.OK:
return _OK_RENDEZVOUS_REPR_FORMAT.format(
self._state.code, self._state.details)
else: else:
return '<_Rendezvous of RPC that terminated with ({}, {})>'.format( return _NON_OK_RENDEZVOUS_REPR_FORMAT.format(
self._state.code, _common.decode(self._state.details)) self._state.code, self._state.details,
self._state.debug_error_string)
def __repr__(self): def __repr__(self):
return self._repr() return self._repr()

@ -291,6 +291,7 @@ cdef extern from "grpc/grpc.h":
grpc_metadata_array *trailing_metadata grpc_metadata_array *trailing_metadata
grpc_status_code *status grpc_status_code *status
grpc_slice *status_details grpc_slice *status_details
char** error_string
ctypedef struct grpc_op_data_recv_close_on_server: ctypedef struct grpc_op_data_recv_close_on_server:
int *cancelled int *cancelled

@ -91,9 +91,11 @@ cdef class ReceiveStatusOnClientOperation(Operation):
cdef grpc_metadata_array _c_trailing_metadata cdef grpc_metadata_array _c_trailing_metadata
cdef grpc_status_code _c_code cdef grpc_status_code _c_code
cdef grpc_slice _c_details cdef grpc_slice _c_details
cdef const char* _c_error_string
cdef tuple _trailing_metadata cdef tuple _trailing_metadata
cdef object _code cdef object _code
cdef str _details cdef str _details
cdef str _error_string
cdef void c(self) cdef void c(self)
cdef void un_c(self) cdef void un_c(self)

@ -199,6 +199,8 @@ cdef class ReceiveStatusOnClientOperation(Operation):
&self._c_code) &self._c_code)
self.c_op.data.receive_status_on_client.status_details = ( self.c_op.data.receive_status_on_client.status_details = (
&self._c_details) &self._c_details)
self.c_op.data.receive_status_on_client.error_string = (
&self._c_error_string)
cdef void un_c(self): cdef void un_c(self):
self._trailing_metadata = _metadata(&self._c_trailing_metadata) self._trailing_metadata = _metadata(&self._c_trailing_metadata)
@ -206,6 +208,11 @@ cdef class ReceiveStatusOnClientOperation(Operation):
self._code = self._c_code self._code = self._c_code
self._details = _decode(_slice_bytes(self._c_details)) self._details = _decode(_slice_bytes(self._c_details))
grpc_slice_unref(self._c_details) grpc_slice_unref(self._c_details)
if self._c_error_string != NULL:
self._error_string = _decode(self._c_error_string)
gpr_free(<void*>self._c_error_string)
else:
self._error_string = ""
def trailing_metadata(self): def trailing_metadata(self):
return self._trailing_metadata return self._trailing_metadata
@ -216,6 +223,9 @@ cdef class ReceiveStatusOnClientOperation(Operation):
def details(self): def details(self):
return self._details return self._details
def error_string(self):
return self._error_string
cdef class ReceiveCloseOnServerOperation(Operation): cdef class ReceiveCloseOnServerOperation(Operation):

@ -225,6 +225,7 @@ class RPCTest(unittest.TestCase):
self.assertEqual(expected_response, response) self.assertEqual(expected_response, response)
self.assertIs(grpc.StatusCode.OK, call.code()) self.assertIs(grpc.StatusCode.OK, call.code())
self.assertEqual("", call.debug_error_string())
def testSuccessfulUnaryRequestFutureUnaryResponse(self): def testSuccessfulUnaryRequestFutureUnaryResponse(self):
request = b'\x07\x08' request = b'\x07\x08'
@ -706,6 +707,13 @@ class RPCTest(unittest.TestCase):
self.assertIs(grpc.StatusCode.UNKNOWN, self.assertIs(grpc.StatusCode.UNKNOWN,
exception_context.exception.code()) exception_context.exception.code())
# sanity checks on to make sure returned string contains default members
# of the error
debug_error_string = exception_context.exception.debug_error_string()
self.assertIn("created", debug_error_string)
self.assertIn("description", debug_error_string)
self.assertIn("file", debug_error_string)
self.assertIn("file_line", debug_error_string)
def testFailedUnaryRequestFutureUnaryResponse(self): def testFailedUnaryRequestFutureUnaryResponse(self):
request = b'\x37\x17' request = b'\x37\x17'

Loading…
Cancel
Save