From 66eb893b4d9b77a46520d0d7de78f7f7c23d9357 Mon Sep 17 00:00:00 2001 From: jo Date: Fri, 25 Apr 2025 10:41:45 +0200 Subject: [PATCH] feat: improve exception messages --- hcloud/_exceptions.py | 8 +++-- hcloud/actions/domain.py | 15 ++++++++- tests/unit/test_exceptions.py | 62 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 tests/unit/test_exceptions.py diff --git a/hcloud/_exceptions.py b/hcloud/_exceptions.py index c884a9a9..e33105a3 100644 --- a/hcloud/_exceptions.py +++ b/hcloud/_exceptions.py @@ -4,11 +4,15 @@ class HCloudException(Exception): - """There was an error while using the hcloud library""" + """There was an error while using the hcloud library. + + All exceptions in the hcloud library inherit from this exception. It may be used as + catch-all exception. + """ class APIException(HCloudException): - """There was an error while performing an API Request""" + """There was an error while performing an API Request.""" def __init__( self, diff --git a/hcloud/actions/domain.py b/hcloud/actions/domain.py index 71f3f3a3..aa7f2fd9 100644 --- a/hcloud/actions/domain.py +++ b/hcloud/actions/domain.py @@ -71,9 +71,22 @@ class ActionException(HCloudException): def __init__(self, action: Action | BoundAction): assert self.__doc__ is not None message = self.__doc__ - if action.error is not None and "message" in action.error: + + extras = [] + if ( + action.error is not None + and "code" in action.error + and "message" in action.error + ): message += f": {action.error['message']}" + extras.append(action.error["code"]) + else: + extras.append(action.command) + + extras.append(str(action.id)) + message += f" ({', '.join(extras)})" + super().__init__(message) self.message = message self.action = action diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py new file mode 100644 index 00000000..c4546ec8 --- /dev/null +++ b/tests/unit/test_exceptions.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import pytest + +from hcloud import ( + APIException, + HCloudException, +) +from hcloud.actions import Action, ActionFailedException, ActionTimeoutException + +running_action = Action( + id=12345, + command="action_command", + status=Action.STATUS_RUNNING, +) + +failed_action = Action( + id=12345, + command="action_command", + status=Action.STATUS_ERROR, + error={"code": "action_failed", "message": "Action failed"}, +) + + +@pytest.mark.parametrize( + ("exception", "expected"), + [ + ( + # Should never be raised by itself + HCloudException(), + "", + ), + ( + # Should never be raised by itself + HCloudException("A test error"), + "A test error", + ), + ( + APIException(code="conflict", message="API error message", details=None), + "API error message (conflict)", + ), + ( + APIException( + code="conflict", + message="API error message", + details=None, + correlation_id="fddea8fabd02fb21", + ), + "API error message (conflict, fddea8fabd02fb21)", + ), + ( + ActionFailedException(failed_action), + "The pending action failed: Action failed (action_failed, 12345)", + ), + ( + ActionTimeoutException(running_action), + "The pending action timed out (action_command, 12345)", + ), + ], +) +def test_exceptions(exception, expected): + assert str(exception) == expected