diff --git a/src/frequenz/client/microgrid/__init__.py b/src/frequenz/client/microgrid/__init__.py index 596a9e9..27e7656 100644 --- a/src/frequenz/client/microgrid/__init__.py +++ b/src/frequenz/client/microgrid/__init__.py @@ -32,7 +32,7 @@ DataLoss, EntityAlreadyExists, EntityNotFound, - GrpcStatusError, + GrpcError, InternalError, InvalidArgument, OperationAborted, @@ -69,7 +69,7 @@ "EntityNotFound", "Fuse", "GridMetadata", - "GrpcStatusError", + "GrpcError", "InternalError", "InvalidArgument", "InverterData", diff --git a/src/frequenz/client/microgrid/_client.py b/src/frequenz/client/microgrid/_client.py index c1397e4..c2b9796 100644 --- a/src/frequenz/client/microgrid/_client.py +++ b/src/frequenz/client/microgrid/_client.py @@ -92,7 +92,7 @@ async def components(self) -> Iterable[Component]: Raises: ClientError: If the are any errors communicating with the Microgrid API, most likely a subclass of - [GrpcStatusError][frequenz.client.microgrid.GrpcStatusError]. + [GrpcError][frequenz.client.microgrid.GrpcError]. """ try: component_list = await self.api.list_components( @@ -172,7 +172,7 @@ async def connections( Raises: ClientError: If the are any errors communicating with the Microgrid API, most likely a subclass of - [GrpcStatusError][frequenz.client.microgrid.GrpcStatusError]. + [GrpcError][frequenz.client.microgrid.GrpcError]. """ connection_filter = pb_microgrid.ConnectionFilter( starts=list(starts), ends=list(ends) @@ -391,7 +391,7 @@ async def set_power(self, component_id: int, power_w: float) -> None: Raises: ClientError: If the are any errors communicating with the Microgrid API, most likely a subclass of - [GrpcStatusError][frequenz.client.microgrid.GrpcStatusError]. + [GrpcError][frequenz.client.microgrid.GrpcError]. """ try: await self.api.set_power_active( @@ -425,7 +425,7 @@ async def set_bounds( greater than 0. ClientError: If the are any errors communicating with the Microgrid API, most likely a subclass of - [GrpcStatusError][frequenz.client.microgrid.GrpcStatusError]. + [GrpcError][frequenz.client.microgrid.GrpcError]. """ if upper < 0: raise ValueError(f"Upper bound {upper} must be greater than or equal to 0.") diff --git a/src/frequenz/client/microgrid/_exception.py b/src/frequenz/client/microgrid/_exception.py index 13f0fdb..c116266 100644 --- a/src/frequenz/client/microgrid/_exception.py +++ b/src/frequenz/client/microgrid/_exception.py @@ -58,7 +58,7 @@ def from_grpc_error( server_url: str, operation: str, grpc_error: grpclib.GRPCError, - ) -> GrpcStatusError: + ) -> GrpcError: """Create an instance of the appropriate subclass from a gRPC error. Args: @@ -68,16 +68,16 @@ def from_grpc_error( Returns: An instance of - [GrpcStatusError][frequenz.client.microgrid.GrpcStatusError] if + [GrpcError][frequenz.client.microgrid.GrpcError] if the gRPC status is not recognized, or an appropriate subclass if it is. """ class Ctor(Protocol): - """A protocol for the constructor of a subclass of `GrpcStatusError`.""" + """A protocol for the constructor of a subclass of `GrpcError`.""" def __call__( self, *, server_url: str, operation: str, grpc_error: grpclib.GRPCError - ) -> GrpcStatusError: ... + ) -> GrpcError: ... status_map: dict[grpclib.Status, Ctor] = { grpclib.Status.CANCELLED: OperationCancelled, @@ -109,11 +109,12 @@ def __call__( ) -class GrpcStatusError(ClientError): - """The gRPC server returned an error status code. +class GrpcError(ClientError): + """The gRPC server returned an error with a status code. These errors are specific to gRPC. If you want to use the client in - a protocol-independent way, you should avoid catching this exception. + a protocol-independent way, you should avoid catching this exception. Catching + subclasses that don't have *grpc* in their name should be protocol-independent. References: * [gRPC status @@ -152,7 +153,7 @@ def __init__( # pylint: disable=too-many-arguments """The original gRPC error.""" -class UnrecognizedGrpcStatus(GrpcStatusError): +class UnrecognizedGrpcStatus(GrpcError): """The gRPC server returned an unrecognized status code.""" def __init__( @@ -174,7 +175,7 @@ def __init__( ) -class OperationCancelled(GrpcStatusError): +class OperationCancelled(GrpcError): """The operation was cancelled.""" def __init__( @@ -196,7 +197,7 @@ def __init__( ) -class UnknownError(GrpcStatusError): +class UnknownError(GrpcError): """There was an error that can't be described using other statuses.""" def __init__( @@ -218,7 +219,7 @@ def __init__( ) -class InvalidArgument(GrpcStatusError, ValueError): +class InvalidArgument(GrpcError, ValueError): """The client specified an invalid argument. Note that this error differs from @@ -246,7 +247,7 @@ def __init__( ) -class OperationTimedOut(GrpcStatusError): +class OperationTimedOut(GrpcError): """The time limit was exceeded while waiting for the operationt o complete. For operations that change the state of the system, this error may be returned even @@ -274,7 +275,7 @@ def __init__( ) -class EntityNotFound(GrpcStatusError): +class EntityNotFound(GrpcError): """The requested entity was not found. Note that this error differs from @@ -301,7 +302,7 @@ def __init__( ) -class EntityAlreadyExists(GrpcStatusError): +class EntityAlreadyExists(GrpcError): """The entity that we attempted to create already exists.""" def __init__( @@ -323,7 +324,7 @@ def __init__( ) -class PermissionDenied(GrpcStatusError): +class PermissionDenied(GrpcError): """The caller does not have permission to execute the specified operation. Note that when the operation is rejected due to other reasons, such as the resources @@ -354,7 +355,7 @@ def __init__( ) -class ResourceExhausted(GrpcStatusError): +class ResourceExhausted(GrpcError): """Some resource has been exhausted (for example per-user quota, disk space, etc.).""" def __init__( @@ -377,7 +378,7 @@ def __init__( ) -class OperationPreconditionFailed(GrpcStatusError): +class OperationPreconditionFailed(GrpcError): """The operation was rejected because the system is not in a required state. For example, the directory to be deleted is non-empty, an rmdir operation is applied @@ -405,7 +406,7 @@ def __init__( ) -class OperationAborted(GrpcStatusError): +class OperationAborted(GrpcError): """The operation was aborted. Typically due to a concurrency issue or transaction abort. @@ -430,7 +431,7 @@ def __init__( ) -class OperationOutOfRange(GrpcStatusError): +class OperationOutOfRange(GrpcError): """The operation was attempted past the valid range. Unlike [InvalidArgument][frequenz.client.microgrid.InvalidArgument], this @@ -461,7 +462,7 @@ def __init__( ) -class OperationNotImplemented(GrpcStatusError): +class OperationNotImplemented(GrpcError): """The operation is not implemented or not supported/enabled in this service.""" def __init__( @@ -484,7 +485,7 @@ def __init__( ) -class InternalError(GrpcStatusError): +class InternalError(GrpcError): """Some invariants expected by the underlying system have been broken. This error code is reserved for serious errors. @@ -510,7 +511,7 @@ def __init__( ) -class ServiceUnavailable(GrpcStatusError): +class ServiceUnavailable(GrpcError): """The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with @@ -536,7 +537,7 @@ def __init__( ) -class DataLoss(GrpcStatusError): +class DataLoss(GrpcError): """Unrecoverable data loss or corruption.""" def __init__( @@ -558,7 +559,7 @@ def __init__( ) -class OperationUnauthenticated(GrpcStatusError): +class OperationUnauthenticated(GrpcError): """The request does not have valid authentication credentials for the operation.""" def __init__( diff --git a/tests/test_exception.py b/tests/test_exception.py index eaf960a..fbf9460 100644 --- a/tests/test_exception.py +++ b/tests/test_exception.py @@ -15,7 +15,7 @@ DataLoss, EntityAlreadyExists, EntityNotFound, - GrpcStatusError, + GrpcError, InternalError, InvalidArgument, OperationAborted, @@ -33,15 +33,15 @@ ) -class _GrpcStatusErrorCtor(Protocol): - """A protocol for the constructor of a subclass of `GrpcStatusError`.""" +class _GrpcErrorCtor(Protocol): + """A protocol for the constructor of a subclass of `GrpcErrorCtor`.""" def __call__( self, *, server_url: str, operation: str, grpc_error: grpclib.GRPCError - ) -> GrpcStatusError: ... + ) -> GrpcError: ... -ERROR_TUPLES: list[tuple[type[GrpcStatusError], grpclib.Status, str, bool]] = [ +ERROR_TUPLES: list[tuple[type[GrpcError], grpclib.Status, str, bool]] = [ ( UnrecognizedGrpcStatus, mock.MagicMock(name="unknown_status"), @@ -146,7 +146,7 @@ def __call__( "exception_class, grpc_status, expected_description, retryable", ERROR_TUPLES ) def test_grpc_status_error( - exception_class: _GrpcStatusErrorCtor, + exception_class: _GrpcErrorCtor, grpc_status: grpclib.Status, expected_description: str, retryable: bool, @@ -176,7 +176,7 @@ def test_grpc_unknown_status_error() -> None: "grpc error message", "grpc error details", ) - exception = GrpcStatusError( + exception = GrpcError( server_url="http://testserver", operation="test_operation", description=expected_description, @@ -210,7 +210,7 @@ def test_client_error() -> None: "exception_class, grpc_status, expected_description, retryable", ERROR_TUPLES ) def test_from_grpc_error( - exception_class: type[GrpcStatusError], + exception_class: type[GrpcError], grpc_status: grpclib.Status, expected_description: str, retryable: bool,