ruff
f619cb9b - [ty] Distinguish lax and strict mode for Pydantic models (#26587)

Commit
15 hours ago
[ty] Distinguish lax and strict mode for Pydantic models (#26587) ## Summary By default, Pydantic models can convert input values. This is called ["lax mode"](https://pydantic.dev/docs/validation/latest/concepts/strict_mode/). For example: ```py class Model(BaseModel): values: list[int] Model(values=[1, 2, 3]) # okay Model(values=["1", "2", "3"]) # also okay Model(values=set(["1", "2", "3"])) # also okay Model(values=[None]) # error Model(values=[[1, 2]]) # error ``` Allowed conversions are listed in [this table](https://pydantic.dev/docs/validation/latest/concepts/conversion_table) in Pydantic's documentation. To support this, I ended up converging to a solution that is very similar to what Pyrefly does (according to its documentation, I did not consult the implementation). We add a few type aliases like `type LaxInt = int | bytes | str | float | Decimal` to `ty_extensions.pydantic`, and use those to eagerly transform the input types. For example, we map `list[int] -> Iterable[LaxInt]` (see mdtests for why we use `Iterable`). I did consider several alternatives though: - A very permissive mode in which we accept `Any` input type in lax mode. I considered this to be a viable alternative since the benefit of modeling this more precisely seems relatively small. Most Pydantic models will be constructed from data that enters through an I/O boundary, not via an explicit `Model(values=[1, 2, 3])` call. So it doesn't seem very likely that we'll catch a lot of errors with this. I ended up rejecting this idea because I didn't like the impact on our LSP: instead of showing useful constructor signatures, we would end up showing `Any` types everywhere. - Instead of introducing various `IntLax`, `IntStr`, ... type aliases, I started implementing a version where we would have a `Lax[..]` special form. Input types would be converted by simply wrapping the annotated type in `Lax[..]`, e.g. `list[int] -> Lax[list[int]]`. The problem with this is that we would need to apply this type mapping lazily (since we would like to preserve `Lax[list[int]]` in the annotation). This would require an entirely new `Type` enum variant, and would require us to implement the conversion table inside our type relations. This seemed too complex / invasive. ## Test Plan Updated and added Markdown tests, verified every single test against Pydantic's runtime behavior.
Author
Parents
Loading