106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright 2010-2025 Google LLC
|
|
# 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}"
|
|
)
|