mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-19 01:24:53 -05:00
#9336 Python: Translate closed-channel errors and stop downgrading RipsError
After the heartbeat closes a dead-server channel, gRPC raises
ValueError("Cannot invoke RPC on closed channel!") rather than a
grpc.RpcError, which slipped past the existing pdmobject wrappers and
surfaced to the user as a bare ValueError nested in a traceback.
- pdmobject add_method decorator and _call_pdm_method_void /
_return_value / _return_optional_value now also catch the
closed-channel ValueError and translate it to RipsError.
- well_path.add_well_log no longer rewraps every exception as
RuntimeError. Cleanup of temporary key-value entries still runs, but
the original typed exception (e.g. RipsError with code/details) is
re-raised so callers can inspect it.
This commit is contained in:
@@ -36,6 +36,14 @@ F = TypeVar("F", bound=Callable[..., Any])
|
||||
C = TypeVar("C")
|
||||
|
||||
|
||||
def _is_closed_channel_error(exc: BaseException) -> bool:
|
||||
# gRPC raises ValueError("Cannot invoke RPC on closed channel!") when an
|
||||
# RPC is attempted after the channel has been closed (e.g. by the
|
||||
# heartbeat after detecting a dead server). Translate to RipsError so
|
||||
# callers see a typed exception instead of a bare ValueError.
|
||||
return isinstance(exc, ValueError) and "closed channel" in str(exc)
|
||||
|
||||
|
||||
def add_method(cls: C) -> Callable[[F], F]:
|
||||
def decorator(func: F) -> F:
|
||||
def wrapper(*args, **kwargs):
|
||||
@@ -45,6 +53,13 @@ def add_method(cls: C) -> Callable[[F], F]:
|
||||
raise RipsError(
|
||||
e.details(), code=e.code(), details=e.details()
|
||||
) from None
|
||||
except ValueError as e:
|
||||
if _is_closed_channel_error(e):
|
||||
raise RipsError(
|
||||
"ResInsight gRPC channel is closed "
|
||||
"(server may have crashed or been shut down)"
|
||||
) from e
|
||||
raise
|
||||
|
||||
# Explicitly preserve signature for Sphinx documentation
|
||||
wrapper.__name__ = func.__name__
|
||||
@@ -563,6 +578,13 @@ class PdmObjectBase:
|
||||
raise RipsError(
|
||||
"%s" % exc.details(), code=exc.code(), details=exc.details()
|
||||
) from None
|
||||
except ValueError as exc:
|
||||
if _is_closed_channel_error(exc):
|
||||
raise RipsError(
|
||||
"ResInsight gRPC channel is closed "
|
||||
"(server may have crashed or been shut down)"
|
||||
) from exc
|
||||
raise
|
||||
|
||||
def _call_pdm_method_return_value(
|
||||
self, method_name: str, class_definition: Type[PdmObjectT], **kwargs: Any
|
||||
@@ -584,6 +606,13 @@ class PdmObjectBase:
|
||||
raise RipsError(
|
||||
"%s" % exc.details(), code=exc.code(), details=exc.details()
|
||||
) from None
|
||||
except ValueError as exc:
|
||||
if _is_closed_channel_error(exc):
|
||||
raise RipsError(
|
||||
"ResInsight gRPC channel is closed "
|
||||
"(server may have crashed or been shut down)"
|
||||
) from exc
|
||||
raise
|
||||
|
||||
def _call_pdm_method_return_optional_value(
|
||||
self, method_name: str, class_definition: Type[PdmObjectT], **kwargs: Any
|
||||
@@ -616,6 +645,13 @@ class PdmObjectBase:
|
||||
raise RipsError(
|
||||
"%s" % exc.details(), code=exc.code(), details=exc.details()
|
||||
) from None
|
||||
except ValueError as exc:
|
||||
if _is_closed_channel_error(exc):
|
||||
raise RipsError(
|
||||
"ResInsight gRPC channel is closed "
|
||||
"(server may have crashed or been shut down)"
|
||||
) from exc
|
||||
raise
|
||||
|
||||
def update(self) -> None:
|
||||
"""Sync all fields from the Python Object to ResInsight
|
||||
|
||||
@@ -224,14 +224,16 @@ def add_well_log(
|
||||
tvd_rkb_key=tvd_rkb_key,
|
||||
)
|
||||
return well_log
|
||||
except Exception as e:
|
||||
# Clean up all temporary keys on failure
|
||||
except Exception:
|
||||
# Clean up all temporary keys on failure, then re-raise the
|
||||
# original exception so callers see the typed RipsError (with
|
||||
# gRPC code/details) instead of a generic RuntimeError.
|
||||
for temp_key in temp_keys:
|
||||
try:
|
||||
project.remove_key_values(temp_key)
|
||||
except Exception:
|
||||
pass # Ignore cleanup errors
|
||||
raise RuntimeError(f"Failed to create well log: {str(e)}") from e
|
||||
raise
|
||||
|
||||
|
||||
@add_method(WellPath)
|
||||
|
||||
Reference in New Issue
Block a user