Files
ortools-clone/ortools/math_opt/python/errors.py

105 lines
3.2 KiB
Python
Raw Normal View History

# Copyright 2010-2025 Google LLC
2024-07-29 15:15:15 +02:00
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Translate C++'s absl::Status errors to Python standard errors.
Here we try to use the standard Python errors we would use if the C++ code was
instead implemented in Python. This will give Python users a more familiar API.
"""
import enum
from typing import Optional, Type
from ortools.math_opt import rpc_pb2
class _StatusCode(enum.Enum):
"""The C++ absl::Status::code() values."""
OK = 0
CANCELLED = 1
UNKNOWN = 2
INVALID_ARGUMENT = 3
DEADLINE_EXCEEDED = 4
NOT_FOUND = 5
ALREADY_EXISTS = 6
PERMISSION_DENIED = 7
UNAUTHENTICATED = 16
RESOURCE_EXHAUSTED = 8
FAILED_PRECONDITION = 9
ABORTED = 10
OUT_OF_RANGE = 11
UNIMPLEMENTED = 12
INTERNAL = 13
UNAVAILABLE = 14
DATA_LOSS = 15
class InternalMathOptError(RuntimeError):
"""Some MathOpt internal error.
This error is usually raised because of a bug in MathOpt or one of the solver
library it wraps.
"""
def status_proto_to_exception(
status_proto: rpc_pb2.StatusProto,
) -> Optional[Exception]:
"""Returns the Python exception that best match the input absl::Status.
There are some Status that we expect the MathOpt code to return, for those the
matching exceptions are:
- InvalidArgument: ValueError
- FailedPrecondition: AssertionError
- Unimplemented: NotImplementedError
- Internal: InternalMathOptError
Other Status's are not used by MathOpt, if they are seen a
InternalMathOptError is raised (as if the Status was Internal) and the error
message contains the unexpected code.
Args:
status_proto: The input proto to convert to an exception.
Returns:
The corresponding exception. None if the input status is OK.
"""
try:
code = _StatusCode(status_proto.code)
except ValueError:
return InternalMathOptError(
f"unknown C++ error (code = {status_proto.code}):"
f" {status_proto.message}"
)
if code == _StatusCode.OK:
return None
# For expected errors we compute the corresponding class.
error_type: Optional[Type[Exception]] = None
if code == _StatusCode.INVALID_ARGUMENT:
error_type = ValueError
if code == _StatusCode.FAILED_PRECONDITION:
error_type = AssertionError
if code == _StatusCode.UNIMPLEMENTED:
error_type = NotImplementedError
if code == _StatusCode.INTERNAL:
error_type = InternalMathOptError
if error_type is not None:
return error_type(f"{status_proto.message} (was C++ {code.name})")
return InternalMathOptError(
f"unexpected C++ error {code.name}: {status_proto.message}"
)