Skip to content

Commit

Permalink
feat: add pydantic's error dict to ValidationException
Browse files Browse the repository at this point in the history
  • Loading branch information
Anu-cool-007 committed May 4, 2024
1 parent d5196ed commit 8993645
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 9 deletions.
11 changes: 9 additions & 2 deletions litestar/_signature/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ErrorMessage(TypedDict):
key: NotRequired[str]
message: str
source: NotRequired[Literal["body"] | ParamType]
exc: NotRequired[Dict[str, Any]]


MSGSPEC_CONSTRAINT_FIELDS = (
Expand Down Expand Up @@ -128,12 +129,15 @@ def _create_exception(cls, connection: ASGIConnection, messages: list[ErrorMessa
return InternalServerException()

@classmethod
def _build_error_message(cls, keys: Sequence[str], exc_msg: str, connection: ASGIConnection) -> ErrorMessage:
def _build_error_message(
cls, keys: Sequence[str], exc_msg: str, connection: ASGIConnection, exc: Optional[Dict[str, Any]] = None
) -> ErrorMessage:
"""Build an error message.
Args:
keys: A list of keys.
exc_msg: A message.
exc: An optional error dict.
connection: An ASGI connection instance.
Returns:
Expand All @@ -142,6 +146,9 @@ def _build_error_message(cls, keys: Sequence[str], exc_msg: str, connection: ASG

message: ErrorMessage = {"message": exc_msg.split(" - ")[0]}

if exc:
message["exc"] = exc

if keys:
message["key"] = key = ".".join(keys)
if keys[0].startswith("data"):
Expand Down Expand Up @@ -195,7 +202,7 @@ def parse_values_from_connection_kwargs(cls, connection: ASGIConnection, **kwarg
except ExtendedMsgSpecValidationError as e:
for exc in e.errors:
keys = [str(loc) for loc in exc["loc"]]
message = cls._build_error_message(keys=keys, exc_msg=exc["msg"], connection=connection)
message = cls._build_error_message(keys=keys, exc_msg=exc["msg"], connection=connection, exc=exc)
messages.append(message)
raise cls._create_exception(messages=messages, connection=connection) from e
except ValidationError as e:
Expand Down
69 changes: 62 additions & 7 deletions tests/unit/test_contrib/test_pydantic/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,80 @@ def test(
assert data
if pydantic_version == "v1":
assert data["extra"] == [
{"key": "child.val", "message": "value is not a valid integer"},
{"key": "child.other_val", "message": "value is not a valid integer"},
{"key": "other_child.val.1", "message": "value is not a valid integer"},
{
"key": "child.val",
"message": "value is not a valid integer",
"exc": {
"loc": ["child", "val"],
"msg": "value is not a valid integer",
"type": "type_error.integer",
},
},
{
"key": "child.other_val",
"message": "value is not a valid integer",
"exc": {
"loc": ["child", "other_val"],
"msg": "value is not a valid integer",
"type": "type_error.integer",
},
},
{
"key": "other_child.val.1",
"message": "value is not a valid integer",
"exc": {
"loc": ["other_child", "val", 1],
"msg": "value is not a valid integer",
"type": "type_error.integer",
},
},
]
else:
assert data["extra"] == [
# removed url from exc as that contains pydantic version specific url
expected_extra_dict = [
{
"message": "Input should be a valid integer, unable to parse string as an integer",
"key": "child.val",
"message": "Input should be a valid integer, unable to parse string as an integer",
"exc": {
"type": "int_parsing",
"loc": ["child", "val"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "a",
},
},
{
"message": "Input should be a valid integer, unable to parse string as an integer",
"key": "child.other_val",
"message": "Input should be a valid integer, unable to parse string as an integer",
"exc": {
"type": "int_parsing",
"loc": ["child", "other_val"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "b",
},
},
{
"message": "Input should be a valid integer, unable to parse string as an integer",
"key": "other_child.val.1",
"message": "Input should be a valid integer, unable to parse string as an integer",
"exc": {
"type": "int_parsing",
"loc": ["other_child", "val", 1],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "c",
},
},
]
for data_extra_item, expected_extra_item in zip(data["extra"], expected_extra_dict):
# partial assert for key and message
assert all(
data_extra_item[key] == expected_extra_item[key]
for key in list(set(data_extra_item.keys()) - {"exc"})
)

# partial assert for exc without url
assert all(
data_extra_item["exc"][key] == expected_extra_item["exc"][key]
for key in list(set(data_extra_item["exc"].keys()) - {"url"})
)


class V1ModelWithPrivateFields(pydantic_v1.BaseModel):
Expand Down

0 comments on commit 8993645

Please sign in to comment.