def logStatus(e: grpc.RpcError) -> None: logging.info("----------------------------------") # logging.info("gRPC returned error: {}".format(e)) # logging.info(" trailing_metadata: {}".format(e.trailing_metadata())) print("----") code = e.code() print(" code: {} ({})".format(code.name, code.value[0])) details = e.details() print(" details: {}".format(details)) err_string = e.debug_error_string() print(" err str: {}".format(err_string)) status = rpc_status.from_call(e) if status is not None: for detail in status.details: if detail.Is(error_details_pb2.ErrorInfo.DESCRIPTOR): errinfo = error_details_pb2.ErrorInfo() detail.Unpack(errinfo) print(" ErrorInfo:") print(" Reason: {}".format(errinfo.reason)) print(" Domain: {}".format(errinfo.domain)) print(" Metadata:") for k, v in errinfo.metadata.items(): print(" {}: {}".format(k, v)) # logging.info(" ErrorInfo: {}".format(errinfo)) else: print(" -- unknown details type.")
def is_reset_signal(error: GoogleAPICallError) -> bool: """ Determines whether the given error contains the stream RESET signal, sent by the server to instruct clients to reset stream state. Returns: True if the error contains the RESET signal. """ if not is_retryable(error) or not error.response: return False try: status = rpc_status.from_call(error.response) if not status: return False for detail in status.details: if detail.Is(ErrorInfo.DESCRIPTOR): info = ErrorInfo() if ( detail.Unpack(info) and info.reason == "RESET" and info.domain == "pubsublite.googleapis.com" ): return True except ValueError: pass return False
def _missing_owner_rich_error(stub): logger.info("missing account owner, rich error") # open account request missing the owner cmd = OpenAccountRequest(balance=200) request = HandleCommandRequest( command=pack_any(cmd), prior_state=pack_any(Empty()), prior_event_meta=MetaData(), ) did_fail = False try: stub.HandleCommand(request) except RpcError as e: error_status = rpc_status.from_call(e) assert error_status.details, 'missing details' bad_request = unpack_any(error_status.details[0], error_details_pb2.BadRequest) assert bad_request.field_violations, 'missing violations' violation = bad_request.field_violations[0] assert violation.field == "account_owner" assert violation.description == "missing account owner" did_fail = True assert did_fail, "did not fail"
def _get_extra_data_from_error(error: RpcError): """ Extra error info is stored in trailing_metadata as custom protos """ status = rpc_status.from_call(error) if not status: return error_code = None field_errors = None for detail in status.details: # detail is an Any proto. Right now these are usually ErrorInfo protos. if detail.Is(ErrorInfo.DESCRIPTOR): error_info = ErrorInfo() detail.Unpack(error_info) error_code = error_info.code if detail.Is(BadRequest.DESCRIPTOR): bad_request = BadRequest() detail.Unpack(bad_request) field_errors = [ FieldError( field=field_error.field, message=field_error.message, code=field_error.code, ) for field_error in bad_request.field_errors ] return dict( error_code=error_code, field_errors=field_errors, )
def test_status_not_ok(self): with self.assertRaises(grpc.RpcError) as exception_context: self._channel.unary_unary(_STATUS_NOT_OK).with_call(_REQUEST) rpc_error = exception_context.exception self.assertEqual(rpc_error.code(), grpc.StatusCode.INTERNAL) # Failed RPC doesn't automatically generate status status = rpc_status.from_call(rpc_error) self.assertIs(status, None)
def grpc_server_error(rpc_error): """ A gRPC errors handler """ app.rpc_token_metadata = None # clear meta_data logger.error('gRPC error: %s' % str(rpc_error)) err_name = rpc_error._state.code.name err_code = rpc_error._state.code.value[0] extended_status = rpc_status.from_call(rpc_error) return jsonify({'grpc_error': f'{err_name} {err_code} {extended_status}'}), HTTPStatus.CONFLICT
def process(stub): try: response = stub.SayHello(helloworld_pb2.HelloRequest(name='Alice')) _LOGGER.info('Call success: %s', response.message) except grpc.RpcError as rpc_error: _LOGGER.error('Call failure: %s', rpc_error) status = rpc_status.from_call(rpc_error) for detail in status.details: if detail.Is(error_details_pb2.QuotaFailure.DESCRIPTOR): info = error_details_pb2.QuotaFailure() detail.Unpack(info) _LOGGER.error('Quota failure: %s', info) else: raise RuntimeError('Unexpected failure: %s' % detail)
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 _missing_owner_rich_error(stub: BankAccountServiceStub): logger.info("missing account owner, rich error") # open account request missing the owner cmd = OpenAccountRequest(balance=200) did_fail = False try: stub.OpenAccount(cmd) except RpcError as e: error_status = rpc_status.from_call(e) assert error_status.details, 'missing details' bad_request = unpack_any(error_status.details[0], error_details_pb2.BadRequest) assert bad_request.field_violations, 'missing violations' violation = bad_request.field_violations[0] assert violation.field == "account_owner" assert violation.description == "missing account owner" did_fail = True assert did_fail, "did not fail"
def dependency_error_details(exc, as_json=False): if not is_dependency(exc): return None # Should have dependency metadata. meta = exc.trailing_metadata() if not meta: return None status = rpc_status.from_call(exc) details = [] for d in status.details: if not d.type_url.endswith('/proto.ModifyDep'): return None dep = entroq_pb2.ModifyDep() dep.ParseFromString(d.value) if dep.type == entroq_pb2.DETAIL and not dep.msg: continue if as_json: details.append(json_format.MessageToDict(dep)) else: details.append(dep) return details
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
def _cos_process_command(cls, id, command, context): '''helper method to run process command''' logger.debug("begin process_command") client = cls._get_cos_client() command_any = pack_any(command) metadata = [('x-custom-request-uuid', str(uuid4()))] request = ProcessCommandRequest(entity_id=id, command=command_any) try: response = client.ProcessCommand(request=request, metadata=metadata) return cls._cos_unpack_state(response.state) except grpc.RpcError as e: output_status = rpc_status.from_call(e) if not output_status: context.abort(code=e.code(), details=e.details()) context.abort_with_status(rpc_status.to_status(output_status)) except Exception as e: context.abort(code=StatusCode.INTERNAL, details=str(e))
def leaderboard(): last_30_days = 'last_30_days' in request.args name = request.args.get('name') try: page = int(request.args.get('page', 0)) except ValueError: return jsonify({'error': 'page value must be int'}), HTTPStatus.BAD_REQUEST with grpc.insecure_channel(config.GRPC_SERVER_HOST) as channel: stub = LeaderBoardStub(channel) rpc_request = GetLeaderBoard() if last_30_days: rpc_request.option = GetLeaderBoard.LAST_30_DAYS if page: rpc_request.page = page if name: rpc_request.name = name try: leaderboard_response = stub.GetLeaderBoardPages(rpc_request, metadata=[app.rpc_token_metadata]) results = [(r.name, r.score, r.rank) for r in leaderboard_response.results] around_me = [(a.name, a.score, a.rank) for a in leaderboard_response.around_me] except grpc.RpcError as rpc_error: status = rpc_status.from_call(rpc_error) if status.code == code_pb2.INVALID_ARGUMENT and status.message == 'page': error, status = 'page value exceeds max possible value', HTTPStatus.BAD_REQUEST elif status.code == code_pb2.INVALID_ARGUMENT and status.message == 'name': if last_30_days: error, status = 'player has no new results during last 30 days', HTTPStatus.BAD_REQUEST else: error, status = 'unknown player name', HTTPStatus.BAD_REQUEST else: raise rpc_error return jsonify({'error': error}), status else: return jsonify({ 'next_page': leaderboard_response.next_page, 'results': results, 'around_me': around_me }), HTTPStatus.OK
def _parse_grpc_error_details(rpc_exc): try: status = rpc_status.from_call(rpc_exc) except NotImplementedError: # workaround return [], None if not status: return [], None possible_errors = [ error_details_pb2.BadRequest, error_details_pb2.PreconditionFailure, error_details_pb2.QuotaFailure, error_details_pb2.ErrorInfo, error_details_pb2.RetryInfo, error_details_pb2.ResourceInfo, error_details_pb2.RequestInfo, error_details_pb2.DebugInfo, error_details_pb2.Help, error_details_pb2.LocalizedMessage, ] error_info = None error_details = [] for detail in status.details: matched_detail_cls = list( filter(lambda x: detail.Is(x.DESCRIPTOR), possible_errors)) # If nothing matched, use detail directly. if len(matched_detail_cls) == 0: info = detail else: info = matched_detail_cls[0]() detail.Unpack(info) error_details.append(info) if isinstance(info, error_details_pb2.ErrorInfo): error_info = info return error_details, error_info
def test_status_ok(self): _, call = self._channel.unary_unary(_STATUS_OK).with_call(_REQUEST) # Succeed RPC doesn't have status status = rpc_status.from_call(call) self.assertIs(status, None)
datapoints = [] for x in range(0, 20): numeric_msg = math_pb2.Numeric() numeric_msg.value = random.uniform(0, 1) data_point = datapoint_pb2.Datapoint( stream="test.numeric", numeric=numeric_msg, timestamp=int(time.time() * 1000) ) datapoints.append(data_point) # force some throttling errors to see error handling time.sleep(0.001) request = agent_pb2.PostDataMultiRequest(datapoints=datapoints) try: agent.PostDataMulti(request) # catch rpcError except grpc.RpcError as e: status = rpc_status.from_call(e) for post_data_multi_error in status.details: if post_data_multi_error.Is(agent_pb2.PostDataMultiError.DESCRIPTOR): # unpack error details into proto agent_pb2.PostDataMultiError post_data_multi_error_pb = agent_pb2.PostDataMultiError() post_data_multi_error.Unpack(post_data_multi_error_pb) for error in post_data_multi_error_pb.errors: # iterate through errors and check for retryable if error.retryable: print("datapoint at index %s is retryable" % error.index) else: print("datapoint at index %s is not retryable" % error.index)