Skip to content

Commit

Permalink
Human Readable Pydantic Errors (basetenlabs#1176)
Browse files Browse the repository at this point in the history
* Beautify unreadable pydantic errors

* Delete obsolete repo files

* Review comments
  • Loading branch information
marius-baseten authored Oct 22, 2024
1 parent ea9aa46 commit 9889e04
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 8 deletions.
2 changes: 0 additions & 2 deletions .gitattributes

This file was deleted.

1 change: 0 additions & 1 deletion .python-version

This file was deleted.

43 changes: 43 additions & 0 deletions truss/templates/server/common/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import contextlib
import logging
import sys
import textwrap
from http import HTTPStatus
from types import TracebackType
from typing import (
Expand All @@ -13,6 +14,7 @@
)

import fastapi
import pydantic
import starlette.responses
from fastapi import HTTPException
from fastapi.responses import JSONResponse
Expand Down Expand Up @@ -171,3 +173,44 @@ def intercept_exceptions(
"Internal Server Error", exc_info=filter_traceback(model_file_name)
)
raise UserCodeError(str(exc))


def _loc_to_dot_sep(loc: Tuple[Union[str, int], ...]) -> str:
# From https://docs.pydantic.dev/latest/errors/errors/#customize-error-messages.
# Chained field access is stylized with `.`-notation (corresponding to str parts)
# and array indexing is stylized with `[i]`-notation (corresponding to int parts).
# E.g. ('items', 1, 'value') -> items[1].value.
parts = []
for i, x in enumerate(loc):
if isinstance(x, str):
if i > 0:
parts.append(".")
parts.append(x)
elif isinstance(x, int):
parts.append(f"[{x}]")
else:
raise TypeError(f"Unexpected type: {x}.")
return "".join(parts)


def format_pydantic_validation_error(exc: pydantic.ValidationError) -> str:
if exc.error_count() == 1:
parts = ["Input Parsing Error:"]
else:
parts = ["Input Parsing Errors:"]

try:
for error in exc.errors(include_url=False, include_input=False):
loc = _loc_to_dot_sep(error["loc"])
if error["msg"]:
msg = error["msg"]
else:
error_no_loc = dict(error)
error_no_loc.pop("loc")
msg = str(error_no_loc)
parts.append(textwrap.indent(f"`{loc}`: {msg}.", " "))
except: # noqa: E722
# Fallback in case any of the fields cannot be processed as expected.
return f"Input Parsing Error, {str(exc)}"

return "\n".join(parts)
4 changes: 2 additions & 2 deletions truss/templates/server/truss_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async def _parse_body(
inputs = truss_schema.input_type.parse_obj(inputs)
except pydantic.ValidationError as e:
raise errors.InputParsingError(
f"Request Validation Error, {str(e)}"
errors.format_pydantic_validation_error(e)
) from e
else:
if truss_schema:
Expand All @@ -151,7 +151,7 @@ async def _parse_body(
inputs = truss_schema.input_type.parse_raw(body_raw)
except pydantic.ValidationError as e:
raise errors.InputParsingError(
f"Request Validation Error, {str(e)}"
errors.format_pydantic_validation_error(e)
) from e
else:
try:
Expand Down
3 changes: 1 addition & 2 deletions truss/tests/test_model_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ def test_truss_with_annotated_inputs_outputs():
assert response.status_code == 400
assert "error" in response.json()
assert (
"Request Validation Error, 1 validation error for ModelInput"
"\nprompt\n Field required [type=missing, input_value={'bad_key': 'value'}, input_type=dict]\n"
"Input Parsing Error:\n `prompt`: Field required."
in response.json()["error"]
)
assert response.headers["x-baseten-error-source"] == "04"
Expand Down
1 change: 0 additions & 1 deletion truss/tests/test_trussless_docker_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def test_docker_server_truss():
assert response.status_code == 200
assert response.json() == {
"message": "Hello World",
"is_torch_cuda_available": False,
"is_env_var_passed": True,
"is_secret_mounted": True,
}

0 comments on commit 9889e04

Please sign in to comment.