How Pyrefly Supports Pydantic Models: Configuration Extraction and Constraint Validation

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. 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. When constraint violations occur, the logic in 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:

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 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 creates isolated environments for testing type-checking behavior:

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:

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

Summary

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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →