def _error_details_unary_unary(request, servicer_context): details = any_pb2.Any() details.Pack( error_details_pb2.DebugInfo(stack_entries=traceback.format_stack(), detail='Intentionally invoked')) rich_status = status_pb2.Status( code=code_pb2.INTERNAL, message=_STATUS_DETAILS, details=[details], ) servicer_context.abort_with_status(rpc_status.to_status(rich_status))
def _abort(context, err_msg: str, code: int, detail_msg: str = ""): detail = any_pb2.Any() detail.Pack( error_details_pb2.DebugInfo( stack_entries=traceback.format_stack(), detail=detail_msg, )) rich_status = status_pb2.Status(code=code, message=err_msg, details=[detail]) context.abort_with_status(rpc_status.to_status(rich_status))
def __init__(self, message, exc, stack_entries): super().__init__(message) if stack_entries is not None: detail = any_pb2.Any() detail.Pack( error_details_pb2.DebugInfo(stack_entries=stack_entries, detail=repr(exc))) status = status_pb2.Status(code=code_pb2.INTERNAL, message=str(exc), details=[detail]) else: status = status_pb2.Status(code=code_pb2.INTERNAL, message=str(exc)) self.impl = rpc_status.to_status(status)
def test_error_details(self): with self.assertRaises(grpc.RpcError) as exception_context: self._channel.unary_unary(_ERROR_DETAILS).with_call(_REQUEST) rpc_error = exception_context.exception status = rpc_status.from_call(rpc_error) self.assertEqual(rpc_error.code(), grpc.StatusCode.INTERNAL) self.assertEqual(status.code, code_pb2.Code.Value('INTERNAL')) # Check if the underlying proto message is intact self.assertEqual( status.details[0].Is(error_details_pb2.DebugInfo.DESCRIPTOR), True) info = error_details_pb2.DebugInfo() status.details[0].Unpack(info) self.assertIn('_error_details_unary_unary', info.stack_entries[-1])
def exception_handler(self, exc, context): log.exception(exc) detail = any_pb2.Any() if self.debug: detail.Pack( error_details_pb2.DebugInfo( stack_entries=traceback.format_stack(), detail=repr(exc) ) ) status = status_pb2.Status( code=code_pb2.INTERNAL, message=str(exc), details=[detail] ) context.abort_with_status(rpc_status.to_status(status))
def test_method_exceptions_with_stacktrace(server_method, server_request): exc = SomeException('Bmi method always fails') model = FailingModel(exc) server = BmiServer(model, True) context = Mock(grpc.ServicerContext) getattr(server, server_method)(server_request, context) context.abort_with_status.assert_called_once() status = context.abort_with_status.call_args[0][0] assert status.code == grpc.StatusCode.INTERNAL assert status.details == 'Bmi method always fails' metadata = status_pb2.Status.FromString(status.trailing_metadata[0][1]) debuginfo = error_details_pb2.DebugInfo() metadata.details[0].Unpack(debuginfo) assert debuginfo.detail == "SomeException('Bmi method always fails',)" assert len(debuginfo.stack_entries) > 0
def handle_error(exc): """Parsers DebugInfo (https://github.com/googleapis/googleapis/blob/07244bb797ddd6e0c1c15b02b4467a9a5729299f/google/rpc/error_details.proto#L46-L52) from the trailing metadata of a grpc.RpcError Args: exc (grpc.RpcError): Exception to handle Raises: original exception or RemoteException """ status = rpc_status.from_call(exc) if status is None: raise for detail in status.details: if detail.Is(error_details_pb2.DebugInfo.DESCRIPTOR): info = error_details_pb2.DebugInfo() detail.Unpack(info) remote_traceback = info.stack_entries remote_detail = info.detail raise RemoteException(remote_detail, remote_traceback) from exc raise