aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Noah Eisen <ncteisen@google.com>2017-12-14 16:01:25 -0800
committerGravatar ncteisen <ncteisen@gmail.com>2018-05-17 07:42:02 -0700
commit0b8b9a08e28f57231075b8d9804daaf966f86ba8 (patch)
tree13c138447cd205910db51ccb3bf9c7250d3cc504 /src
parent6e5ca7bad72ebf50f9245e1e1f2eb642f285067a (diff)
Surfaces debug_error_string to Python API
In case of error, the user can access call.debug_error_string() which contains a string representation of error from the c core.
Diffstat (limited to 'src')
-rw-r--r--src/python/grpcio/grpc/_channel.py27
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi1
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi2
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi10
-rw-r--r--src/python/grpcio_tests/tests/unit/_rpc_test.py8
5 files changed, 46 insertions, 2 deletions
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 8cc0e981ef..2017d47130 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -58,6 +58,17 @@ _STREAM_STREAM_INITIAL_DUE = (
_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
'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):
return None if timeout is None else time.time() + timeout
@@ -91,6 +102,7 @@ class _RPCState(object):
self.trailing_metadata = trailing_metadata
self.code = code
self.details = details
+ self.debug_error_string = None
# 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
# result of the RPC. This field tracks whether cancellation was requested
@@ -137,6 +149,7 @@ def _handle_event(event, state, response_deserializer):
else:
state.code = code
state.details = batch_operation.details()
+ state.debug_error_string = batch_operation.error_string()
callbacks.extend(state.callbacks)
state.callbacks = None
return callbacks
@@ -374,13 +387,23 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
self._state.condition.wait()
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):
with self._state.condition:
if self._state.code is None:
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:
- return '<_Rendezvous of RPC that terminated with ({}, {})>'.format(
- self._state.code, _common.decode(self._state.details))
+ return _NON_OK_RENDEZVOUS_REPR_FORMAT.format(
+ self._state.code, self._state.details,
+ self._state.debug_error_string)
def __repr__(self):
return self._repr()
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index a4c0319553..2d6c900c54 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -291,6 +291,7 @@ cdef extern from "grpc/grpc.h":
grpc_metadata_array *trailing_metadata
grpc_status_code *status
grpc_slice *status_details
+ char** error_string
ctypedef struct grpc_op_data_recv_close_on_server:
int *cancelled
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi
index bfbe27785b..69a2a4989e 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/operation.pxd.pxi
@@ -91,9 +91,11 @@ cdef class ReceiveStatusOnClientOperation(Operation):
cdef grpc_metadata_array _c_trailing_metadata
cdef grpc_status_code _c_code
cdef grpc_slice _c_details
+ cdef const char* _c_error_string
cdef tuple _trailing_metadata
cdef object _code
cdef str _details
+ cdef str _error_string
cdef void c(self)
cdef void un_c(self)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi
index 239d0f3f95..454627f570 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/operation.pyx.pxi
@@ -199,6 +199,8 @@ cdef class ReceiveStatusOnClientOperation(Operation):
&self._c_code)
self.c_op.data.receive_status_on_client.status_details = (
&self._c_details)
+ self.c_op.data.receive_status_on_client.error_string = (
+ &self._c_error_string)
cdef void un_c(self):
self._trailing_metadata = _metadata(&self._c_trailing_metadata)
@@ -206,6 +208,11 @@ cdef class ReceiveStatusOnClientOperation(Operation):
self._code = self._c_code
self._details = _decode(_slice_bytes(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):
return self._trailing_metadata
@@ -216,6 +223,9 @@ cdef class ReceiveStatusOnClientOperation(Operation):
def details(self):
return self._details
+ def error_string(self):
+ return self._error_string
+
cdef class ReceiveCloseOnServerOperation(Operation):
diff --git a/src/python/grpcio_tests/tests/unit/_rpc_test.py b/src/python/grpcio_tests/tests/unit/_rpc_test.py
index 54f01d9f8d..34e7831a98 100644
--- a/src/python/grpcio_tests/tests/unit/_rpc_test.py
+++ b/src/python/grpcio_tests/tests/unit/_rpc_test.py
@@ -225,6 +225,7 @@ class RPCTest(unittest.TestCase):
self.assertEqual(expected_response, response)
self.assertIs(grpc.StatusCode.OK, call.code())
+ self.assertEqual("", call.debug_error_string())
def testSuccessfulUnaryRequestFutureUnaryResponse(self):
request = b'\x07\x08'
@@ -706,6 +707,13 @@ class RPCTest(unittest.TestCase):
self.assertIs(grpc.StatusCode.UNKNOWN,
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):
request = b'\x37\x17'