diff --git a/src/citrine/exceptions.py b/src/citrine/exceptions.py index a528cce80..7f60ab826 100644 --- a/src/citrine/exceptions.py +++ b/src/citrine/exceptions.py @@ -8,9 +8,26 @@ class CitrineException(Exception): - """The base exception class for Citrine-Python exceptions.""" - - pass + """The base exception class for Citrine-Python exceptions. + + Parameters + ---------- + *args + Positional arguments passed to the base Exception class. + hint : str, optional + An actionable suggestion for how the user can resolve + the error. Displayed after the main message. + """ + + def __init__(self, *args, hint=None): + super().__init__(*args) + self.hint = hint + + def __str__(self): + base = super().__str__() + if self.hint: + return "{}\n\nHint: {}".format(base, self.hint) + return base class NonRetryableException(CitrineException): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 000000000..bc51071e0 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,49 @@ +"""Tests for the Citrine exception hierarchy.""" +import pytest + +from citrine.exceptions import CitrineException + + +def test_citrine_exception_without_hint(): + exc = CitrineException("something went wrong") + assert str(exc) == "something went wrong" + assert exc.hint is None + + +def test_citrine_exception_with_hint(): + exc = CitrineException("bad request", hint="Check your input.") + assert "bad request" in str(exc) + assert "Hint: Check your input." in str(exc) + assert exc.hint == "Check your input." + + +def test_citrine_exception_hint_format(): + exc = CitrineException("error", hint="Try again.") + expected = "error\n\nHint: Try again." + assert str(exc) == expected + + +def test_citrine_exception_no_args(): + exc = CitrineException() + assert str(exc) == "" + assert exc.hint is None + + +def test_citrine_exception_no_args_with_hint(): + exc = CitrineException(hint="Do something.") + assert "Hint: Do something." in str(exc) + + +def test_citrine_exception_is_catchable_as_exception(): + with pytest.raises(Exception): + raise CitrineException("test") + + +def test_hint_preserved_through_subclass(): + """Subclasses that call super().__init__ with hint should work.""" + class MyError(CitrineException): + pass + + exc = MyError("oops", hint="Fix it.") + assert exc.hint == "Fix it." + assert "Hint: Fix it." in str(exc)