diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py index 6264c1fac22..49ad0d2f5e5 100644 --- a/src/python/grpcio/grpc/_server.py +++ b/src/python/grpcio/grpc/_server.py @@ -499,8 +499,12 @@ def _call_behavior( _abort(state, rpc_event.call, cygrpc.StatusCode.unknown, b'RPC Aborted') elif exception not in state.rpc_errors: - details = 'Exception calling application: {}'.format( - exception) + try: + details = 'Exception calling application: {}'.format( + exception) + except Exception: # pylint: disable=broad-except + details = 'Calling application raised unprintable Exception!' + traceback.print_exc() _LOGGER.exception(details) _abort(state, rpc_event.call, cygrpc.StatusCode.unknown, _common.encode(details)) diff --git a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py index cacb028c3b4..904d8b914f2 100644 --- a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py +++ b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py @@ -27,6 +27,7 @@ _SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3 _DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3] _UNARY_UNARY = '/test/UnaryUnary' +_UNARY_UNARY_NESTED_EXCEPTION = '/test/UnaryUnaryNestedException' _UNARY_STREAM = '/test/UnaryStream' _STREAM_UNARY = '/test/StreamUnary' _STREAM_STREAM = '/test/StreamStream' @@ -47,6 +48,10 @@ class _Handler(object): ),)) return request + def handle_unary_unary_with_nested_exception(self, request, + servicer_context): + raise test_control.NestedDefect() + def handle_unary_stream(self, request, servicer_context): for _ in range(test_constants.STREAM_LENGTH): self._control.control() @@ -128,6 +133,11 @@ class _GenericHandler(grpc.GenericRpcHandler): self._handler.handle_stream_stream) elif handler_call_details.method == _DEFECTIVE_GENERIC_RPC_HANDLER: return self._handler.defective_generic_rpc_handler() + elif handler_call_details.method == _UNARY_UNARY_NESTED_EXCEPTION: + return _MethodHandler( + False, False, None, None, + self._handler.handle_unary_unary_with_nested_exception, None, + None, None) else: return None @@ -176,6 +186,10 @@ def _defective_handler_multi_callable(channel): return channel.unary_unary(_DEFECTIVE_GENERIC_RPC_HANDLER) +def _defective_nested_exception_handler_multi_callable(channel): + return channel.unary_unary(_UNARY_UNARY_NESTED_EXCEPTION) + + class InvocationDefectsTest(unittest.TestCase): """Tests the handling of exception-raising user code on the client-side.""" @@ -260,6 +274,19 @@ class InvocationDefectsTest(unittest.TestCase): self.assertIs(grpc.StatusCode.UNKNOWN, exception_context.exception.code()) + def testNestedExceptionGenericRpcHandlerUnaryResponse(self): + request = b'\x07\x08' + multi_callable = _defective_nested_exception_handler_multi_callable( + self._channel) + + with self.assertRaises(grpc.RpcError) as exception_context: + multi_callable(request, + metadata=(('test', + 'DefectiveGenericRpcHandlerUnary'),)) + + self.assertIs(grpc.StatusCode.UNKNOWN, + exception_context.exception.code()) + if __name__ == '__main__': logging.basicConfig() diff --git a/src/python/grpcio_tests/tests/unit/framework/common/test_control.py b/src/python/grpcio_tests/tests/unit/framework/common/test_control.py index ed97bdf4a62..8ef040751eb 100644 --- a/src/python/grpcio_tests/tests/unit/framework/common/test_control.py +++ b/src/python/grpcio_tests/tests/unit/framework/common/test_control.py @@ -26,6 +26,13 @@ class Defect(Exception): """ +class NestedDefect(Exception): + """Simulates a nested programming defect raised into in a system under test.""" + + def __str__(self): + raise Exception('Nested Exception') + + class Control(abc.ABC): """An object that accepts program control from a system under test.