Skip to content

Commit 5a0d969

Browse files
authored
Improves error deserialization to handle bad format returned from the JSON (#263)
1 parent c62cdc5 commit 5a0d969

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# CHANGELOG
22

3+
## Next release
4+
5+
- Improves error deserialization to handle bad format returned from the JSON
6+
37
## v7.11.0 (2023-04-04)
48

5-
- Add `get_next_page` function that retrieves the next page of results for a paginated collection
9+
- Adds `get_next_page` function that retrieves the next page of results for a paginated collection
610

711
## v7.10.0 (2023-02-17)
812

easypost/error.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import json
22
from typing import (
3+
Any,
4+
Dict,
5+
List,
36
Optional,
47
Union,
58
)
@@ -9,14 +12,16 @@ class Error(Exception):
912
def __init__(
1013
self,
1114
message: Optional[
12-
Union[str, list]
15+
Union[Dict[str, Any], list, str]
1316
] = None, # message should be a string but can sometimes incorrectly come back as a list
1417
http_status: Optional[int] = None,
1518
http_body: Optional[Union[str, bytes]] = None,
1619
original_exception: Optional[Exception] = None,
1720
):
1821
super(Error, self).__init__(message)
19-
self.message = ", ".join(message) if type(message) == list else message
22+
message_list: List[str] = []
23+
Error.traverse_json_element(message, message_list)
24+
self.message = ", ".join(message_list)
2025
self.http_status = http_status
2126
self.http_body = http_body
2227
self.original_exception = original_exception
@@ -32,3 +37,19 @@ def __init__(
3237
self.param = self.json_body["error"].get("param", None)
3338
except Exception:
3439
self.param = None
40+
41+
@staticmethod
42+
def traverse_json_element(
43+
error_message: Optional[Union[Dict[str, Any], list, str]], messages_list: List[str]
44+
) -> None:
45+
"""Recursively traverses a JSON object or array and extracts error messages
46+
as strings. Adds the extracted messages to the specified messages_list array.
47+
"""
48+
if isinstance(error_message, dict):
49+
for value in error_message.values():
50+
Error.traverse_json_element(value, messages_list)
51+
elif isinstance(error_message, list):
52+
for value in error_message:
53+
Error.traverse_json_element(value, messages_list)
54+
else:
55+
messages_list.append(str(error_message))

tests/test_error.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,30 @@ def test_error_list_message():
3030
error = easypost.Error(message=["Error1", "Error2"])
3131

3232
assert error.message == "Error1, Error2"
33+
34+
35+
def test_error_dict_message():
36+
"""Tests that we concatenate error messages that are a dict (they should be a string from the
37+
API but aren't always so we protect against that here).
38+
"""
39+
message_data = {"errors": ["Bad format 1", "Bad format 2"]}
40+
41+
error = easypost.Error(message=message_data)
42+
43+
assert error.message == "Bad format 1, Bad format 2"
44+
45+
46+
def test_error_bad_format_message():
47+
"""Tests that we concatenate error messages that has really bad format
48+
(they should be a string from the API but aren't always so we protect against that here).
49+
"""
50+
message_data = {
51+
"errors": ["Bad format 1", "Bad format 2"],
52+
"bad_data": [
53+
{"first_message": "Bad format 3", "second_message": "Bad format 4", "thrid_message": "Bad format 5"}
54+
],
55+
}
56+
57+
error = easypost.Error(message=message_data)
58+
59+
assert error.message == "Bad format 1, Bad format 2, Bad format 3, Bad format 4, Bad format 5"

0 commit comments

Comments
 (0)