Skip to content

Commit c1c8ed5

Browse files
authored
Updated error message to include server side errors if present. (#183)
Error message to include server side error messages if present in payload. BUG=b/375194356
1 parent b830394 commit c1c8ed5

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

src/kagglehub/exceptions.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import logging
12
from http import HTTPStatus
23
from typing import Any, Optional
34

45
import requests
56

67
from kagglehub.handle import CompetitionHandle, ResourceHandle
8+
from kagglehub.logger import EXTRA_CONSOLE_BLOCK
9+
10+
logger = logging.getLogger(__name__)
711

812

913
class CredentialError(Exception):
@@ -58,6 +62,13 @@ def kaggle_api_raise_for_status(response: requests.Response, resource_handle: Op
5862
response.raise_for_status()
5963
except requests.HTTPError as e:
6064
message = str(e)
65+
server_error_message = ""
66+
try:
67+
server_error_message = response.json().get("message", "")
68+
if server_error_message:
69+
server_error_message = f"The server reported the following issues: {server_error_message}\n"
70+
except requests.exceptions.JSONDecodeError as ex:
71+
logger.info(f"Server payload is not json. See {ex}", extra={**EXTRA_CONSOLE_BLOCK})
6172
resource_url = resource_handle.to_url() if resource_handle else response.url
6273
if response.status_code in {HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN}:
6374
if isinstance(resource_handle, CompetitionHandle):
@@ -72,17 +83,19 @@ def kaggle_api_raise_for_status(response: requests.Response, resource_handle: Op
7283
message = (
7384
f"{response.status_code} Client Error."
7485
"\n\n"
75-
f"You don't have permission to access resource at URL: {resource_url}"
76-
"\nPlease make sure you are authenticated if you are trying to access a"
77-
" private resource or a resource requiring consent."
86+
f"You don't have permission to access resource at URL: {resource_url}. "
87+
f"{server_error_message}"
88+
f"Please make sure you are authenticated if you are trying to access a "
89+
f"private resource or a resource requiring consent."
7890
)
7991

8092
if response.status_code == HTTPStatus.NOT_FOUND:
8193
message = (
8294
f"{response.status_code} Client Error."
8395
"\n\n"
84-
f"Resource not found at URL: {resource_url}"
85-
"\nPlease make sure you specified the correct resource identifiers."
96+
f"Resource not found at URL: {resource_url}\n"
97+
f"{server_error_message}"
98+
"Please make sure you specified the correct resource identifiers."
8699
)
87100

88101
# Default handling

tests/server_stubs/kaggle_api_stub.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import re
44

5-
from flask import Flask, Response, jsonify, request
5+
from flask import Flask, Response, jsonify, make_response, request
66
from flask.typing import ResponseReturnValue
77

88
from kagglehub.integrity import to_b64_digest
@@ -18,8 +18,26 @@ def head() -> ResponseReturnValue:
1818

1919
@app.errorhandler(404)
2020
def error(e: Exception): # noqa: ANN201
21-
data = {"code": "404", "error": str(e)}
22-
return jsonify(data), 200
21+
data = {"code": "404", "error": str(e), "message": "server side error"}
22+
return jsonify(data), 404
23+
24+
25+
@app.route("/api/v1/content_type_mismatch", methods=["GET"])
26+
def content_type_and_payload_mismatch() -> ResponseReturnValue:
27+
html = """
28+
<html>
29+
<head>
30+
<title>Test</title>
31+
</head>
32+
<body>
33+
<h1>This is HTML content</h1>
34+
</body>
35+
</html>
36+
"""
37+
resp = make_response(html)
38+
resp.headers["Content-Type"] = "application/json"
39+
resp.status_code = 404
40+
return resp
2341

2442

2543
@app.route("/api/v1/no-integrity", methods=["GET"])

tests/test_kaggle_api_client.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import kagglehub
66
from kagglehub import clients
77
from kagglehub.clients import KaggleApiV1Client
8-
from kagglehub.exceptions import DataCorruptionError
8+
from kagglehub.exceptions import DataCorruptionError, KaggleApiHTTPError
99
from tests.fixtures import BaseTestCase
1010

1111
from .server_stubs import kaggle_api_stub as stub
@@ -68,6 +68,18 @@ def test_download_corrupted_file_fail_integrity_check(self) -> None:
6868
# Assert the corrupted file has been deleted.
6969
self.assertFalse(os.path.exists(out_file))
7070

71+
def test_error_message(self) -> None:
72+
api_client = KaggleApiV1Client()
73+
with self.assertRaises(KaggleApiHTTPError) as ex:
74+
api_client.get("/error")
75+
self.assertIn("The server reported the following issues:", str(ex.exception))
76+
77+
def test_error_message_with_mismatch(self) -> None:
78+
api_client = KaggleApiV1Client()
79+
with self.assertRaises(KaggleApiHTTPError) as ex:
80+
api_client.get("/content_type_mismatch")
81+
self.assertNotIn("The server reported the following issues:", str(ex.exception))
82+
7183
@patch.dict("os.environ", {})
7284
def test_get_user_agent(self) -> None:
7385
self.assertEqual(clients.get_user_agent(), f"kagglehub/{kagglehub.__version__}")

0 commit comments

Comments
 (0)