Files
ResInsight/GrpcInterface/Python/rips/exception.py
T
Kristian Bendiksen f8d180f48d #9336 Python: Improve error reporting from gRPC to client
Surface gRPC failure context to Python script authors instead of
silently returning sentinel values or raising bare gRPC errors.

- RipsError now carries code/details/location and a from_rpc_error()
  helper. Existing single-arg construction stays valid.
- Stop swallowing grpc.RpcError in case.__grid_count for non-NOT_FOUND
  failures and in instance._check_connection_and_version, so the
  underlying status is propagated.
- pdmobject add_method decorator and _call_pdm_method_* attach
  code/details to the raised RipsError; existing message text is
  preserved so pytest match=... assertions keep working.
- Instance.start_heartbeat / stop_heartbeat / check_alive provide an
  opt-in background ping that flips a sticky lost-connection flag and
  raises a readable RipsError from check_alive() if the server dies.
- Server side: GetPdmObject in RiaGrpcAppService, RiaGrpcCaseService
  and RiaGrpcProjectService no longer return Status::OK with an empty
  reply when the underlying object is missing; they return NOT_FOUND
  / INTERNAL with a descriptive message. Other RiaGrpc*Service files
  audited and already return non-OK with text on error paths.
2026-05-04 20:03:39 +02:00

48 lines
1.4 KiB
Python

from typing import Optional
class RipsError(Exception):
"""Exception raised by the rips Python client.
Carries optional gRPC context so callers can branch on code/details
when a remote ResInsight call fails. Backwards compatible with the
legacy ``RipsError("message")`` form.
"""
def __init__(
self,
message: object,
*,
code: Optional[object] = None,
details: Optional[str] = None,
location: Optional[str] = None,
) -> None:
super().__init__(message)
self.code = code
self.details = details
self.location = location
@classmethod
def from_rpc_error(
cls, rpc_error: Exception, location: Optional[str] = None
) -> "RipsError":
"""Build a RipsError from a grpc.RpcError-like object."""
code_fn = getattr(rpc_error, "code", None)
details_fn = getattr(rpc_error, "details", None)
code = code_fn() if callable(code_fn) else None
details = details_fn() if callable(details_fn) else ""
parts = ["ResInsight gRPC call failed"]
if code is not None:
parts.append(f"({code})")
if details:
parts.append(f"- {details}")
if location:
parts.append(f"[server={location}]")
return cls(
" ".join(parts),
code=code,
details=details or None,
location=location,
)