Skip to content

Wrap dec_hook errors in ValidationError - #460

Merged
jcrist merged 1 commit into
mainfrom
chain-exceptions-dec-hook-errors
Jun 30, 2023
Merged

Wrap dec_hook errors in ValidationError#460
jcrist merged 1 commit into
mainfrom
chain-exceptions-dec-hook-errors

Conversation

@jcrist

@jcrist jcrist commented Jun 30, 2023

Copy link
Copy Markdown
Member

This modifies decoding of custom types to wrap errors raised by a dec_hook with ValidationError. Only TypeError or ValueError (and subclasses) exceptions are wrapped, all other exceptions are raised directly.

If an exception is wrapped with a ValidationError, the original exception is set as the __cause__ (and __context__). This means the original exception will still show up in tracebacks, and is available for introspection if needed.

This also changes the recommendation for raising TypeError on unsupported types in dec_hook/enc_hook to raising a NotImplementedError. No previous functionality was based on this recommendation, so this isn't a breaking change in anyway, just a change in recommendation of how best to use msgspec.

Fixes #456.

This modifies decoding of custom types to wrap errors raised by a
`dec_hook` with `ValidationError`. Only `TypeError` or `ValueError` (and
subclasses) exceptions are wrapped, all other exceptions are raised
directly.

If an exception is wrapped with a `ValidationError`, the original
exception is set as the `__cause__` (and `__context__`). This means the
original exception will still show up in tracebacks, and is available
for introspection if needed.

This also changes the recommendation for raising `TypeError` on
unsupported types in `dec_hook`/`enc_hook` to raising a
`NotImplementedError`. No previous functionality was based on this
recommendation, so this isn't a breaking change in anyway, just a change
in recommendation of how best to use `msgspec`.
@jcrist

jcrist commented Jun 30, 2023

Copy link
Copy Markdown
Member Author

Demo of behavior

In [1]: import msgspec, pydantic

In [2]: class Point(pydantic.BaseModel):
   ...:     x: int
   ...:     y: int
   ...: 

In [3]: class Test(msgspec.Struct):
   ...:     point: Point
   ...: 

In [4]: def dec_hook(typ, val):
   ...:     if issubclass(typ, pydantic.BaseModel):
   ...:         return typ.model_validate(val)
   ...:     raise NotImplementedError
   ...: 

In [5]: dec = msgspec.json.Decoder(type=Test, dec_hook=dec_hook)

In [6]: dec.decode(b'{"point": {"x": 1, "y": 2}}')  # works
Out[6]: Test(point=Point(x=1, y=2))

In [7]: try:
   ...:     dec.decode(b'{"point": {"x": "a", "y": "b"}}')
   ...: except msgspec.ValidationError as exc:
   ...:     error = exc  # work around auto-del of exc
   ...: 

In [8]: error
Out[8]: msgspec.ValidationError("2 validation errors for Point\nx\n  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]\n    For further information visit https://errors.pydantic.dev/0.39.0/v/int_parsing\ny\n  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str]\n    For further information visit https://errors.pydantic.dev/0.39.0/v/int_parsing - at `$.point`")

In [9]: error.__cause__
Out[9]: 
2 validation errors for Point
x
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
    For further information visit https://errors.pydantic.dev/0.39.0/v/int_parsing
y
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str]
    For further information visit https://errors.pydantic.dev/0.39.0/v/int_parsing

If the str(exc) for the wrapped error has multiple lines (like pydantic errors) then the path suffix (the "- at $.point" bit) looks a little weird, but for now I think that's fine. For normal one-line exceptions the wrapped errors look identical to the builtin errors, which is the desired effect.

@jcrist
jcrist merged commit 226f475 into main Jun 30, 2023
@jcrist
jcrist deleted the chain-exceptions-dec-hook-errors branch June 30, 2023 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrap errors raised by dec_hook in ValidationError

1 participant