from typing import Tuple import six from boltons.iterutils import is_collection, remap from boltons.typeutils import classproperty from .apierror import APIError jsonable_types = (dict, list, tuple, str, int, float, bool, type(None)) class BaseError(APIError): _default_code = 500 _default_subcode = 0 _default_msg = "" def __init__(self, extra_msg=None, replacement_msg=None, **kwargs): message = replacement_msg or self._default_msg if extra_msg: message += f" ({extra_msg})" if kwargs: kwargs_msg = ", ".join( f"{k}={self._format_kwarg(v)}" for k, v in kwargs.items() ) message += f": {kwargs_msg}" super(BaseError, self).__init__( code=self._default_code, subcode=self._default_subcode, msg=message, error_data=self._to_safe_json_types(kwargs), ) @staticmethod def _to_safe_json_types(data): def visit(_, k, v): if not isinstance(v, jsonable_types): v = str(v) return k, v return remap(data, visit=visit) @staticmethod def _format_kwarg(value): if is_collection(value): return f'({", ".join(str(v) for v in value)})' elif isinstance(value, six.string_types): return value return str(value) @classproperty def codes(self) -> Tuple[int, int]: return self._default_code, self._default_subcode