jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML
https://jcristharif.com/msgspec/
BSD 3-Clause "New" or "Revised" License
2.01k stars 59 forks source link

Constraint check for typing.Optional field #622

Closed zulqasar closed 6 months ago

zulqasar commented 6 months ago

Description

Description

Please consider allowing currently supported constraints to typing.Optional fields. If the given value is None, skip constraint check else apply constraint check.

This will greatly help sanitizing api request data when the value is not None.

Thanks.

jcrist commented 6 months ago

Constraints are already supported on optional fields, you just need to put the annotation inside the Optional[...] wrapper.

from typing import Annotated, Optional

import msgspec

class Example(msgspec.Struct):
    # Note the constraint is _inside_ the Optional
    x: Optional[Annotated[int, msgspec.Meta(ge=0)]] = None

# Explicit null works
ex = msgspec.json.decode(b'{"x": null}', type=Example)
print(ex)
#> Example(x=None)

# Missing value works
ex = msgspec.json.decode(b'{}', type=Example)
print(ex)
#> Example(x=None)

# Explicit valid integer value works
ex = msgspec.json.decode(b'{"x": 1}', type=Example)
print(ex)
#> Example(x=1)

# Invalid integer value errors
msgspec.json.decode(b'{"x": -1}', type=Example)
#> ValidationError("Expected `int` >= 0 - at `$.x`")

Since Optional[int] is the same as Union[int, None] is the same as int | None, this is a similar request to that in #447. See my response here for why I don't think this is necessarily a good idea.

Since constraints can already be applied to optional fields when applied properly, and I'm not enthused about allowing constraints on union types, I'm going to close this for now.