mrkeyoor.com_
Sat 19 Sept 06:44 UTC
PyPIUtilsupdated 19 Sept 2026

pydantic review

Pydantic turns Python type annotations into runtime parsing and validation rules. A BaseModel accepts dictionaries, JSON, or object attributes, returns typed fields, reports errors with nested locations, and emits JSON Schema for the same contract. Version 2.13.4 is a small patch that preserves RootModel core metadata and adjusts packaging for its pydantic-core dependency on macOS. The current API is the v2 family: model_validate, model_dump, field_validator, and ConfigDict. Old examples built around parse_obj, dict, or validator describe the compatibility layer, not the preferred interface.

Verdict

Pydantic is a strong default at Python data boundaries, especially when a framework already speaks its models. Learn the v2 contract and make coercion, mutation, and serialization choices explicit before those defaults become API behavior.

We installed it

Lab card: what happened when we installed pydanticScreenshot of pydantic documentation
Install✓ · 0.4s5 packages on disk · 8 MB
Importimport pydantic in 0.19s · pure Python · py.typed · requires Python >=3.9
Known vulns0(pip-audit)

Answers from our run

Does pydantic install cleanly?

Yes. In a fresh container with an empty cache, pip install pydantic finished in 0.4s, leaving 5 packages and 8 MB on disk. pip-audit reported no known vulnerabilities.

What does pydantic need to run?

Python >=3.9, and nothing compiled: it is pure Python. In our run import pydantic succeeded in 0.19s, and the package ships py.typed for type checkers.

pydantic or msgspec: which should you use?

msgspec: Use it for tightly scoped typed serialization where throughput and low overhead lead the decision. Pydantic is a strong default at Python data boundaries, especially when a framework already speaks its models.

When should you not use pydantic?

Use dataclasses or attrs when objects are constructed only by trusted Python code and runtime parsing adds no value

API stability4/5The v2 naming and validator model has settled into a consistent interface, and the package still exposes a pydantic.v1 namespace for older applications. The large v1-to-v2 change remains visible across search results and third-party integrations, however. Release notes and migration documentation are required reading when upgrading older code, especially around validators, serialization, settings, and subclass handling.
Docs5/5The official documentation has focused sections for models, fields, validators, strict mode, serialization, JSON Schema, errors, performance, integrations, and the v1 migration. Examples show both behavior and failure output, while API pages connect configuration flags to their actual types. The main source of confusion is external material written for v1, and the official site labels the version distinction clearly.
Maintenance5/5The repository was pushed on 2026-08-21 and had 28,591 stars plus 581 open issues and pull requests when checked. Version 2.13.4 was released on 2026-05-06 with a RootModel metadata fix and packaging updates, following active work across the Python layer and pydantic-core integration. The project publishes a detailed changelog and maintains a dedicated documentation site for the current and older lines.
Ecosystem5/5Pydantic models are accepted directly by FastAPI and are common in SDKs, configuration systems, and structured AI output. Companion packages cover settings and extra types, while JSON Schema creates a useful bridge to OpenAPI and tool definitions. That broad use means extensions may support v1 and v2 on different schedules, so application upgrades still need dependency-wide tests rather than a single package bump.

Discussed on

  1. hnPEP 563, PEP 649 and the future of pydantic and FastAPI282 points
  2. hnBuilding your own CLI coding agent with Pydantic-AI197 points
  3. hnRun structured extraction on documents/images locally with Ollama and Pydantic170 points
  4. hnApproximating sum types in Python with Pydantic158 points
  5. hnShow HN: Oxyde – Pydantic-native async ORM with a Rust core155 points

Use it if

  • You receive JSON, environment values, webhook bodies, or tool output and need typed values plus structured validation errors
  • Your framework already consumes Pydantic models for request parsing, response schemas, or generated API documentation
  • You need JSON Schema from the same definitions used by Python code, including aliases, unions, constraints, and nested models
  • You want coercion for ordinary boundary data but need the option to turn on strict parsing globally or for individual fields
Skip it if

Setup reality

Our fresh Python 3.12 install of Pydantic 2.13.4 finished in 0.4 seconds. It produced 5 packages using 8 MB on disk, and pip-audit reported 0 known vulnerabilities. Pydantic declares 6 direct dependencies, requires Python 3.9 or newer, is packaged as pure Python, and ships py.typed. The import test completed in 0.19 seconds. Its installed dependency set includes the separate validation engine used by v2.

Start with the v2 method names. Use model_validate for Python input, model_validate_json for JSON text or bytes, and model_dump or model_dump_json for output. Pydantic normally converts compatible input, including numeric strings. ConfigDict(strict=True) changes that contract. Decide this at the boundary rather than letting different callers assume different coercion rules.

Settings and several specialized types live outside the base package. Environment-backed models require pydantic-settings; email validation commonly uses the email extra. Model construction can also perform network-free schema work at import time, so build reusable TypeAdapter instances once instead of creating them inside a request loop. Forward references may need model_rebuild after all referenced classes exist.

Validation protects model creation, not every later mutation. Enable validate_assignment if fields will be changed and must remain valid. model_construct skips validation and should be reserved for already trusted data. Serialization has its own rules for aliases, excluded values, computed fields, and subclasses, so test the exact model_dump options used at an API or persistence boundary.

Patterns

Parse input into a model define-model

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    tags: list[str] = []

user = User.model_validate({"id": "42", "name": "Ada"})

Default validation converts compatible values, so the string ID becomes an integer. Enable strict mode when that is undesirable.

Return structured validation errors read-validation-errors

from pydantic import ValidationError

try:
    User.model_validate({"id": "wrong", "name": 7})
except ValidationError as exc:
    for error in exc.errors():
        print(error["loc"], error["type"], error["msg"])

The error list is suitable for API responses, but review whether the included input values could expose secrets.

Normalize and validate one field add-field-validator

from pydantic import BaseModel, field_validator

class Account(BaseModel):
    handle: str

    @field_validator("handle")
    @classmethod
    def normalize_handle(cls, value: str) -> str:
        value = value.strip().lower()
        if " " in value:
            raise ValueError("spaces are not allowed")
        return value

A validator must return the accepted value. ValueError is converted into a field-scoped ValidationError entry.

Check a relationship after field parsing validate-related-fields

from typing_extensions import Self
from pydantic import BaseModel, model_validator

class Window(BaseModel):
    start: int
    end: int

    @model_validator(mode="after")
    def ordered(self) -> Self:
        if self.start > self.end:
            raise ValueError("start must be at most end")
        return self

An after validator receives the model instance. A before validator receives raw input and must handle shapes other than a dictionary.

Reject implicit conversions enable-strict-mode

from pydantic import BaseModel, ConfigDict

class Event(BaseModel):
    model_config = ConfigDict(strict=True)
    count: int

Event.model_validate({"count": 3})

With strict mode enabled, a value such as the string '3' fails instead of being converted.

Serialize with aliases and exclusions serialize-model

payload = user.model_dump(
    mode="json",
    by_alias=True,
    exclude_none=True,
)
text = user.model_dump_json(by_alias=True, exclude_none=True)

model_dump_json returns a string. Use mode='json' when the dictionary itself must contain JSON-compatible values.

Reuse a TypeAdapter for a non-model type validate-arbitrary-type

from pydantic import TypeAdapter

rows_adapter = TypeAdapter(list[dict[str, int]])
rows = rows_adapter.validate_json('[{"count": "3"}]')
schema = rows_adapter.json_schema()

Create the adapter once and reuse it; rebuilding validators inside a hot function wastes schema construction work.

Select a tagged union branch discriminate-union

from typing import Annotated, Literal
from pydantic import BaseModel, Field

class Email(BaseModel):
    kind: Literal["email"]
    address: str

class Sms(BaseModel):
    kind: Literal["sms"]
    number: str

Channel = Annotated[Email | Sms, Field(discriminator="kind")]

The discriminator avoids trying every union member and gives errors tied to the selected branch.

Recheck values when fields change validate-assignment

from pydantic import BaseModel, ConfigDict, Field

class Job(BaseModel):
    model_config = ConfigDict(validate_assignment=True)
    retries: int = Field(ge=0, le=10)

job = Job(retries=2)
job.retries = 3

Without validate_assignment, later writes bypass field validation even though initial construction was checked.

Generate JSON Schema for a model emit-json-schema

schema = User.model_json_schema(by_alias=True)
print(schema["properties"])

Schema customization and runtime validation are related but separate; test the generated document consumed by OpenAPI or another tool.

Alternatives

PackageRegistryPick it when
msgspecPyPIUse it for tightly scoped typed serialization where throughput and low overhead lead the decision.
attrsPyPIUse it to define ergonomic Python classes without treating external input as a validation problem.
marshmallowPyPIUse it when serialization schemas should remain separate from domain classes.

More utils guides

lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.