# How Pyrefly Supports Pydantic Models: Configuration Extraction and Constraint Validation

> Learn how Pyrefly supports Pydantic models by extracting configuration and validating constraints through a three-stage pipeline for Pydantic-compatible diagnostics.

- Repository: [Meta/pyrefly](https://github.com/facebook/pyrefly)
- Tags: deep-dive
- Published: 2026-05-21

---

**Pyrefly treats Pydantic models as specialized Python classes that undergo a three-stage pipeline—configuration detection, field metadata extraction, and solver integration—to enforce constraints and generate Pydantic-compatible diagnostics.**

The facebook/pyrefly type checker provides first-class support for Pydantic models by parsing Pydantic-specific metadata during the binding phase and reusing it during type solving. This allows Pyrefly to validate field constraints, check default values, and emit error messages that mirror the official Pydantic library.

## Three-Stage Architecture for Pydantic Support

Pyrefly processes Pydantic models through a specialized pipeline that bridges class definition analysis with runtime constraint validation.

### Stage 1: Detecting Pydantic Configuration

During the binding phase, Pyrefly scans class bodies for Pydantic configuration via `BindingsBuilder::extract_pydantic_config_dict` in [`pyrefly/lib/binding/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/pydantic.rs). When the analyzer encounters a `model_config` attribute—whether as a call to `pydantic.ConfigDict()` or a literal dictionary—it iterates over key/value pairs and populates a `PydanticConfigDict` struct.

The extractor recognizes standard configuration keys including `frozen`, `extra`, `strict`, `validate_by_name`, and `validate_by_alias`, storing their values for later use by the type solver.

### Stage 2: Collecting Field-Level Metadata

After configuration extraction, the binding module analyzes field definitions and decorators. The `extract_field_validator_fields` function identifies `@field_validator` decorators with `mode='before'` or `mode='plain'`, recording affected field names so that generated `__init__` signatures can accept `Any` for those fields.

Field constraints such as `gt`, `lt`, `ge`, and `le` are captured in `PydanticParamConstraint` structs defined in [`pyrefly/lib/alt/types/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/types/pydantic.rs). When constraint violations occur, the logic in [`pyrefly/lib/alt/class/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/class/pydantic.rs) generates diagnostics matching Pydantic’s native error format:

```

Argument value `Literal[0]` violates Pydantic `gt` constraint `Literal[0]` for field `x`

```

The `PydanticConfig` struct holds the merged configuration, while `PydanticValidationFlags` tracks `validate_by_name` and `validate_by_alias` behavior.

### Stage 3: Solver Integration and Type Checking

The extracted metadata is stored in `ClassMetadata` alongside a `pydantic_model_kind` field that classifies the model as `BaseModel`, `RootModel`, `BaseSettings`, or `DataClass`. During type solving, Pyrefly merges the `PydanticConfigDict` with class-level keywords to produce a complete `PydanticConfig`.

The solver applies recorded range constraints when checking assignments and constructor calls, validating that default values satisfy Pydantic requirements before emitting diagnostics.

## Implementation Details and Code Examples

When defining a Pydantic model, users write standard Python code that Pyrefly analyzes for constraint validation:

```python
from pydantic import BaseModel, Field

class User(BaseModel):
    id: int = Field(gt=0)
    name: str = Field(min_length=3)

```

Pyrefly extracts these constraints in [`pyrefly/lib/alt/class/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/class/pydantic.rs) via `extract_pydantic_field_constraints`, which parses the `Field` call and builds a `PydanticParamConstraint` map keyed by field name. If a user attempts `User(id=0)`, the solver looks up the constraint for `id` and produces the diagnostic:

```

Argument value `Literal[0]` violates Pydantic `gt` constraint `Literal[0]` for field `id`

```

The repository includes a dedicated test harness for Pydantic functionality. The `pydantic_testcase!` macro in [`pyrefly/lib/test/pydantic/util.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/test/pydantic/util.rs) creates isolated environments for testing type-checking behavior:

```rust
pydantic_testcase!(
    r#"
    from pydantic import BaseModel, Field

    class Model(BaseModel):
        x: int = Field(gt=0)

    Model(x=-1)  # E: Argument value `Literal[-1]` violates Pydantic `gt` constraint `Literal[0]` for field `x`

    "#,
);

```

Programmatic access to extracted configuration is available through the binding API:

```rust
let mut cfg = PydanticConfigDict::default();
builder.extract_pydantic_config_dict(expr, name, &mut cfg);
// cfg now contains frozen, extra, strict, etc.

```

## Summary

- **Configuration Detection**: The `BindingsBuilder::extract_pydantic_config_dict` function in [`pyrefly/lib/binding/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/pydantic.rs) parses `model_config` attributes into `PydanticConfigDict` structs.
- **Field Analysis**: Constraint extraction and field validator detection populate `PydanticParamConstraint` and validation flags used for type checking.
- **Solver Integration**: `ClassMetadata` stores the `pydantic_model_kind` and merged configuration, enabling the solver to enforce constraints and emit Pydantic-compatible error messages.
- **Key Files**: Core logic resides in [`pyrefly/lib/binding/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/pydantic.rs), [`pyrefly/lib/alt/types/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/types/pydantic.rs), [`pyrefly/lib/alt/class/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/class/pydantic.rs), and [`pyrefly/lib/test/pydantic/util.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/test/pydantic/util.rs).

## Frequently Asked Questions

### How does Pyrefly detect Pydantic configuration options?

Pyrefly detects Pydantic configurations during the binding phase by scanning class bodies for `model_config` attributes. The `BindingsBuilder::extract_pydantic_config_dict` function in [`pyrefly/lib/binding/pydantic.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/pydantic.rs) recognizes both `pydantic.ConfigDict()` calls and literal dictionaries, extracting keys like `frozen`, `strict`, `extra`, `validate_by_name`, and `validate_by_alias` into a `PydanticConfigDict` struct for solver consumption.

### What field constraints can Pyrefly validate in Pydantic models?

Pyrefly validates numeric range constraints including `gt` (greater than), `lt` (less than), `ge` (greater than or equal), and `le` (less than or equal) through the `PydanticParamConstraint` system. When the solver processes model instantiation, it checks literal values against these constraints and emits violations using error messages that match Pydantic's native formatting.

### How does Pyrefly handle field validators in Pydantic?

The binding logic identifies `@field_validator` decorators with `mode='before'` or `mode='plain'` via the `extract_field_validator_fields` function. Fields processed by these validators are marked to accept `Any` type in their generated `__init__` signatures, allowing the validator function to handle type conversion before standard type checking occurs.

### Which Pydantic model types are supported by Pyrefly?

Pyrefly supports standard Pydantic architectures including `BaseModel`, `RootModel`, `BaseSettings`, and Pydantic `DataClass`. The type checker classifies models using the `pydantic_model_kind` field stored in `ClassMetadata`, applying appropriate validation rules based on the specific model category detected during analysis.