-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom error handling for API errors (#205)
* Crete base DCResponse DCResponse just wraps around the api response * NodeResponse * ObservationResponse * ResolveResponse * SparqlResponse * fix return * consistent naming * Update response.py * tests for response utilities * Move response scripts and tests * Improve endpoint docstrings * Implement custom errors * Add tests for custom errors Move file to match history * isort isort imports with google profile * move tests to right folder * correct module structure (imports) * isort tests * Fix name typo
- Loading branch information
Showing
12 changed files
with
189 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from requests import Request | ||
from requests import Response | ||
|
||
from datacommons_client.utils.error_handling import APIError | ||
from datacommons_client.utils.error_handling import DataCommonsError | ||
from datacommons_client.utils.error_handling import DCAuthenticationError | ||
from datacommons_client.utils.error_handling import DCConnectionError | ||
from datacommons_client.utils.error_handling import DCStatusError | ||
from datacommons_client.utils.error_handling import InvalidDCInstanceError | ||
|
||
|
||
def test_data_commons_error_default_message(): | ||
"""Tests that DataCommonsError uses the default message.""" | ||
error = DataCommonsError() | ||
assert str(error) == DataCommonsError.default_message | ||
|
||
|
||
def test_data_commons_error_custom_message(): | ||
"""Tests that DataCommonsError uses a custom message when provided.""" | ||
error = DataCommonsError("Custom message") | ||
assert str(error) == "Custom message" | ||
|
||
|
||
def test_api_error_without_response(): | ||
"""Tests APIError initialization without a Response object.""" | ||
error = APIError() | ||
assert str(error) == f"\n{APIError.default_message}" | ||
|
||
|
||
def test_api_error_with_response(): | ||
"""Tests APIError initialization with a mocked Response object. | ||
Verifies that the string representation includes status code, | ||
request URL, and response text. | ||
""" | ||
mock_request = Request("GET", "http://example.com").prepare() | ||
mock_response = Response() | ||
mock_response.request = mock_request | ||
mock_response.status_code = 404 | ||
mock_response._content = b"Not Found" | ||
|
||
error = APIError(response=mock_response) | ||
assert "Status Code: 404" in str(error) | ||
assert "Request URL: http://example.com" in str(error) | ||
assert "Not Found" in str(error) | ||
|
||
|
||
def test_subclass_default_messages(): | ||
"""Tests that subclasses use their default messages.""" | ||
connection_error = DCConnectionError() | ||
assert DCConnectionError.default_message in str(connection_error) | ||
|
||
status_error = DCStatusError() | ||
assert DCStatusError.default_message in str(status_error) | ||
|
||
auth_error = DCAuthenticationError() | ||
assert DCAuthenticationError.default_message in str(auth_error) | ||
|
||
instance_error = InvalidDCInstanceError() | ||
assert InvalidDCInstanceError.default_message in str(instance_error) | ||
|
||
|
||
def test_subclass_custom_message(): | ||
"""Tests that subclasses use custom messages when provided.""" | ||
error = DCAuthenticationError( | ||
response=Response(), message="Custom auth error" | ||
) | ||
assert str(error) == "\nCustom auth error" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from typing import Optional | ||
|
||
from requests import Response | ||
|
||
|
||
class DataCommonsError(Exception): | ||
"""Base exception for all Data Commons-related errors.""" | ||
|
||
default_message = "An error occurred getting data from Data Commons API." | ||
|
||
def __init__(self, message: Optional[str] = None): | ||
"""Initializes a DataCommonsError with a default or custom message.""" | ||
super().__init__(message or self.default_message) | ||
|
||
|
||
class APIError(DataCommonsError): | ||
"""Represents an error interacting with Data Commons API.""" | ||
|
||
default_message = "An API error occurred." | ||
|
||
def __init__( | ||
self, | ||
response: Optional[Response] = None, | ||
message: Optional[str] = None, | ||
): | ||
"""Initializes an APIError. | ||
Args: | ||
response (Optional[Response]): The response, if available. | ||
message (Optional[str]): A descriptive error message. | ||
""" | ||
super().__init__(message or self.default_message) | ||
self.response = response | ||
self.request = getattr(response, "request", None) | ||
self.status_code = getattr(response, "status_code", None) | ||
|
||
def __str__(self) -> str: | ||
"""Returns a detailed string representation of the error. | ||
Returns: | ||
str: A string describing the error, including the request URL if available. | ||
""" | ||
|
||
details = f"\n{self.args[0]}" | ||
if self.status_code: | ||
details += f"\nStatus Code: {self.status_code}" | ||
if getattr(self.request, "url", None): | ||
details += f"\nRequest URL: {self.request.url}" | ||
if getattr(self.response, "text", None): | ||
details += f"\nResponse: {self.response.text}" | ||
|
||
return details | ||
|
||
|
||
class DCConnectionError(APIError): | ||
"""Raised for network-related errors in the Data Commons API.""" | ||
|
||
default_message = ( | ||
"A network error occurred while connecting to the Data Commons API." | ||
) | ||
|
||
|
||
class DCStatusError(APIError): | ||
"""Raised for non-2xx HTTP status code errors in the Data Commons API.""" | ||
|
||
default_message = "The Data Commons API returned a non-2xx status code." | ||
|
||
|
||
class DCAuthenticationError(APIError): | ||
"""Raised for 401 Unauthorized errors in the Data Commons API.""" | ||
|
||
default_message = "Authentication failed. Please check your API key." | ||
|
||
|
||
class InvalidDCInstanceError(DataCommonsError): | ||
"""Raised when an invalid Data Commons instance is provided.""" | ||
|
||
default_message = "The specified Data Commons instance is invalid." |