Skip to content

Commit

Permalink
Better HTTP Error Handling (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludomitch authored Dec 31, 2024
1 parent 91b895a commit 22529f5
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 2 deletions.
6 changes: 4 additions & 2 deletions ldp/alg/rollout.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ldp.agent import Agent
from ldp.data_structures import Trajectory, Transition
from ldp.utils import format_error_details

from .callbacks import Callback

Expand Down Expand Up @@ -42,8 +43,9 @@ def reraise_exc_as(reraise: type[CaughtError], enabled: bool) -> Iterator[None]:
yield
except Exception as e:
if enabled:
logger.exception(f"Caught {reraise.exc_type} exception.")
raise reraise(e) from e
error_details = format_error_details(e)
logger.exception(f"Caught {reraise.exc_type} exception:\n{error_details}")
raise reraise(e) from None
raise


Expand Down
30 changes: 30 additions & 0 deletions ldp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import logging.config
from typing import Any

logger = logging.getLogger(__name__)


def configure_stdout_logs(
name: str = "root",
Expand Down Expand Up @@ -80,3 +82,31 @@ def discounted_returns(
returns.append(r)
returns.reverse()
return returns


def format_error_details(error: Exception) -> str:
"""Format detailed error information from an exception.
Specially handles HTTP errors that have response attributes with status codes
and JSON details, but works with any exception type.
Args:
error: The exception to format
Returns:
A formatted error string with available details
"""
error_details = f"{error!s}"

if hasattr(error, "response"):
error_details += f"\nStatus code: {error.response.status_code}"
try:
response_data = error.response.json()
if "detail" in response_data:
error_details += "\nServer Traceback:\n"
for line in response_data["detail"].split("\n"):
error_details += f" {line}\n"
except Exception:
error_details += f"\nResponse body: {error.response.text}"

return error_details
54 changes: 54 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from dataclasses import dataclass
from typing import Any

from ldp.utils import format_error_details


@dataclass
class MockResponse:
status_code: int
_json: dict | None = None
_text: str = ""

def json(self) -> dict[str, Any]:
if self._json is None:
raise ValueError("No JSON")
return self._json

@property
def text(self) -> str:
return self._text


class MockHTTPError(Exception):
def __init__(self, status_code: int, detail: str | None = None, text: str = ""):
self.response = MockResponse(
status_code=status_code,
_json={"detail": detail} if detail else None,
_text=text,
)
super().__init__(f"HTTP {status_code}")


def test_format_basic_error():
error = ValueError("something went wrong")
details = format_error_details(error)
assert details == "something went wrong"


def test_format_http_error_with_json():
error = MockHTTPError(
status_code=500,
detail="Traceback:\n File 'app.py', line 123\n raise ValueError('oops')",
)
details = format_error_details(error)
assert "Status code: 500" in details
assert "Server Traceback:" in details
assert "File 'app.py'" in details


def test_format_http_error_with_text():
error = MockHTTPError(status_code=404, text="Not found")
details = format_error_details(error)
assert "Status code: 404" in details
assert "Response body: Not found" in details

0 comments on commit 22529f5

Please sign in to comment.