# region Custom Errors
from inspect import isclass
from typing import Iterable, List, Type, Union
from ..helper import is_iterable
from ..rules import IRule
[docs]class CancelEventError(Exception):
'''Cancel Event Error'''
[docs]class ReservedAttributeError(ValueError):
'''Error when a reserved attribute is attempted to be set'''
[docs]class RuleError(Exception):
'''Rule Error'''
[docs] def __init__(self, **kwargs):
"""
Constructor
Keyword Arguments:
err_rule (Type[IRule], optional): Rule that caused exception.
rules_all (Iterable[Type[IRule]], optional): List of rules that were to all be matched.
One of these rules is usually the reason this exception is being raised.
rules_any (Iterable[Type[IRule]], optional): List of rules that required one or more matches.
One of these rules is usually the reason this exception is being raised.
arg_name (str, optional): Name of the argument for this exception.
errors (Union[Exception, Iterable[Exception]], optional): Exception or Exceptions
that cause this error.
fn_name (str, optional): Name of function/property that raise error.
msg (str, optional): Optional message to append.
"""
self._fn_name = kwargs.get('fn_name', None)
self._err_rule = kwargs.get('err_rule', None)
_rules = kwargs.get('rules_all', False)
self._rules_all = self._get_rules(_rules)
_rules = kwargs.get('rules_any', False)
self._rules_any = self._get_rules(_rules)
_rules = None
self._arg_name = kwargs.get('arg_name', None)
self._errors = kwargs.get('errors', None)
self._msg = kwargs.get('msg', None)
msg = "RuleError:"
if self._fn_name:
msg = msg + f" '{self._fn_name}' error."
if self._arg_name:
msg = msg + f" Argument: '{self._arg_name}' failed validation."
if self.err_rule and self._is_rule(self.err_rule):
msg = msg + f"\nRule '{self.err_rule.__name__}' Failed validation."
if len(self._rules_all) > 0:
if len(self._rules_all) == 1:
msg = msg + "\nExpected the following rule to match: "
else:
msg = msg + "\nExpected all of the following rules to match: "
msg = msg + self._get_rules_str(self._rules_all) + "."
if len(self._rules_any) > 0:
if len(self._rules_any) == 1:
msg = msg + "\nExpected the following rule to match: "
else:
msg = msg + "\nExpected at least one of the following rules to match: "
msg = msg + self._get_rules_str(self._rules_any) + "."
if self._msg:
msg = msg + '\n' + str(self._msg)
if self._is_errors() is True:
msg = msg + "\nInner Error Message: " + self._get_inner_error_msg()
self.message = msg
super().__init__(self.message)
# region private methods
def _is_rule(self, rule) -> bool:
if isclass(rule) and issubclass(rule, IRule):
return True
return False
def _get_rules(self, rules: Iterable[IRule]) -> List[IRule]:
result = []
if rules and is_iterable(rules):
for rule in rules:
if self._is_rule(rule):
result.append(rule)
return result
def _get_rules_str(self, rules: List[IRule]) -> str:
msg = ""
for i, rule in enumerate(rules):
if i > 0:
msg = msg + ', '
msg = f"{msg}{rule.__name__}"
return msg
def _is_errors(self) -> bool:
if self._errors:
if is_iterable(self._errors):
return isinstance(self._errors[0], Exception)
return isinstance(self._errors, Exception)
return False
def _get_first_error(self):
if is_iterable(self._errors):
return self._errors[0]
return self._errors
def _get_inner_error_msg(self) -> str:
err = self._get_first_error()
msg = err.__class__.__name__ + ": "
msg = msg + str(err)
return msg
# endregion private methods
# region Properties
@property
def arg_name(self) -> Union[str, None]:
"""Gets Name of the argument for this exception"""
return self._arg_name
@property
def msg(self) -> Union[str, None]:
"""Gets any messsage that is appended"""
return self._msg
@property
def err_rule(self) -> Union[Type[IRule], None]:
"""Gets rule that caused exception."""
return self._err_rule
@property
def errors(self) -> Union[Exception, Iterable[Exception], None]:
"""Gets Exception or Exceptions that cause this error"""
return self._errors
@property
def rules_all(self) -> List[Type[IRule]]:
"""Gets list of rules that were to all be matched."""
return self._rules_all
@property
def rules_any(self) -> List[Type[IRule]]:
"""Gets of rules that required one or more matches."""
return self._rules_any
@property
def fn_name(self) -> Union[None, str]:
"""Gets the function/property name that raised the error"""
return self._fn_name
# endregion Properties
# region Static Methods
[docs] @staticmethod
def from_rule_error(rule_error: 'RuleError', **kwargs) -> 'RuleError':
"""
Creates a new RuleError from an existing RuleError
Args:
rule_error (RuleError): Current instance of RuleError use to base return value on.
Keyword Arguments:
kwargs: One or more Key, Value properties that will replace property of ``rule_error``.
Returns:
RuleError: New RuleError instance with updated properties included in ``**kwargs``.
"""
rule_dict = {
"err_rule": rule_error.err_rule,
"rules_all": rule_error.rules_all,
"rules_any": rule_error.rules_any,
"arg_name": rule_error.arg_name,
"errors": rule_error.errors,
"fn_name": rule_error.fn_name,
"msg": rule_error.msg
}
rule_dict.update({**kwargs})
return RuleError(**rule_dict)
# endregion Static Methods
# endregion Custom Errors