diff --git a/newsfragments/3282.feature.rst b/newsfragments/3282.feature.rst new file mode 100644 index 0000000000..00b905b374 --- /dev/null +++ b/newsfragments/3282.feature.rst @@ -0,0 +1 @@ +Add ``user_message`` kwarg for human readable ``Web3Exception`` messages. \ No newline at end of file diff --git a/tests/core/core/block-utils/test_select_method_for_block_identifier.py b/tests/core/block-utils/test_select_method_for_block_identifier.py similarity index 100% rename from tests/core/core/block-utils/test_select_method_for_block_identifier.py rename to tests/core/block-utils/test_select_method_for_block_identifier.py diff --git a/tests/core/exceptions/test_exceptions.py b/tests/core/exceptions/test_exceptions.py new file mode 100644 index 0000000000..e613cb517d --- /dev/null +++ b/tests/core/exceptions/test_exceptions.py @@ -0,0 +1,32 @@ +import pytest + +from web3.exceptions import ( + Web3Exception, +) + + +def test_web3exception_with_user_message(): + with pytest.raises(Web3Exception) as exception: + raise Web3Exception(user_message="This failed!") + assert exception.type is Web3Exception + assert exception.value.user_message == "This failed!" + + +def test_web3exception_with_kwargs(): + with pytest.raises(TypeError) as exception: + raise Web3Exception(data={"message": "Unable to fulfill your request."}) + + # For Python > 3.9, str exception includes 'Web3Exception.' + expected = "__init__() got an unexpected keyword argument 'data'" + actual = str(exception.value) + assert exception.type is TypeError + assert hasattr(exception.value, "data") is False + assert expected in actual + + +def test_web3exception_with_args(): + with pytest.raises(Web3Exception) as exception: + raise Web3Exception("failed") + assert exception.type is Web3Exception + assert exception.value.user_message is None + assert exception.value.args[0] == "failed" diff --git a/web3/exceptions.py b/web3/exceptions.py index 217c4744c1..154290533a 100644 --- a/web3/exceptions.py +++ b/web3/exceptions.py @@ -30,6 +30,16 @@ class Web3Exception(Exception): # deal with other exceptions """ + def __init__( + self, + *args: Any, + user_message: Optional[str] = None, + ): + super().__init__(*args) + + # Assign properties of Web3Exception + self.user_message = user_message + class BadFunctionCallOutput(Web3Exception): """ @@ -274,6 +284,7 @@ def __init__( message: Optional[str] = None, data: Optional[Union[str, Dict[str, str]]] = None, ): + super().__init__(message, data) self.message = message self.data = data diff --git a/web3/manager.py b/web3/manager.py index 67a69f94e0..a70fce3c10 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -276,7 +276,11 @@ def formatted_response( if not isinstance(code, int): _raise_bad_response_format(response, "error['code'] must be an integer") elif code == METHOD_NOT_FOUND: - raise MethodUnavailable(error) + raise MethodUnavailable( + error, + user_message="Check your node provider's API docs to see what " + "methods are supported", + ) # Errors must include a message if not isinstance(error.get("message"), str):