pydantic
Skill1 starsChanged 27 days agoMITView on GitHub
Pydantic is a Python data validation and serialization library, based on type hints. Use this skill whenever you need to do relatively complex data modeling using Pydantic, e.g. when adding constraints, defining a model hierarchy with subclasses, etc.
8 other projects use a version of this.
Adopted by
Most recently taken up by
- pydantic/skillsword for word, first seen Oct 16, 2026
- nek1987/auto-agent-harnesstheir own version, first seen Oct 11, 2026
- kid-sid/claude-spellbooktheir own version, first seen Oct 9, 2026
- L-1124/QQMusicApitheir own version, first seen Oct 8, 2026
- pydantic/pydanticword for word, first seen Aug 25, 2026
- d3vzer0/agent-skillstheir own version, first seen Jul 28, 2026
Other versions8 projects use a version of this.
Word for word · 2
- pydantic/pydantic★ 29k100% same
- pydantic/skills★ 144100% same
Edited · 0
None found.
Wrote their own version · 6
- L-1124/QQMusicApi★ 529same name
- kid-sid/claude-spellbook★ 190same name
- arnabdeypolimi/claude_code_setup★ 4same name
- nek1987/auto-agent-harness★ 4same name
- d3vzer0/agent-skills★ 1same name
- Danifelipaez/NeoCafeIA★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository12 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- async-python-patternsSkill
- building-pydantic-ai-agentsSkill
- conventional-commitSkill
- domain-modelingSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- pydantic-ai-harnessSkill
- python-code-styleSkill
- python-testing-patternsSkill
- uv-package-managerSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 147
If possible, prefer using the annotated pattern for validators:
- Very specific: line 198
If you are defining Pydantic models in a module, avoid using from __future__ import annotations if possible (which stringifies all …
- Very specific: line 222
Instead, use an explicit type alias (type on Python 3.12+, or TypeAliasType), which Pydantic can resolve:
- Very specific: line 272
Instead, try to use discriminated unions (provided that you can set a type field to distinguish models):
- Distinctive: line 310
If neither discriminated unions nor generics fit, polymorphic serialization (in Pydantic >=2.13) or [*serialize as
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pydantic Validation
In a nutshell, Pydantic is dataclasses with runtime validation. It leverages type hints to understand how validation (and serialization) should be performed. It is mostly useful when dealing with external untrusted data, for example when defining an HTTP API.
It is generally not recommended to use Pydantic to define classes that are instantiated within the user code. By doing so, you will lose flexibility (e.g. you cannot use types not supported by Pydantic, and it is harder to perform post-init changes). It is usually better to use vanilla classes (or standard library dataclasses) in this case, as a static type checker will already catch type mismatches.
Basic usage
Here is a simple example of using a Pydantic model:
from datetime import date
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str
age: int = Field(description='The age of the person')
birthdate: date | None = None
p = Person(name='John', age=20, birthdate='1970-01-01')Pydantic coerces compatible input: the ISO date string '1970-01-01' is parsed into a date.
Constraints and field metadata
The Field() function is used to provide metadata and constraints.
You need to distinguish two types of metadata:
- field specific metadata: metadata such as
deprecatedandalias, that only have meaning when attached to a field. - type specific metadata: this includes constraints such as
gt,max_length, and also metadata that affects the JSON Schema (e.g.description,title).
Model fields are declared with Field() using the assignment form:
from pydantic import BaseModel, Field
class User(BaseModel):
first_name: str = Field(alias='name')or using the annotated pattern:
from typing import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
value: Annotated[int, Field(deprecated=True)] = 1The annotated pattern has some advantages:
- Using the
f: <type> = Field()form (no default) can be confusing and might trick users into thinkingfhas a default value, while in reality the field is still required. - You can provide an arbitrary amount of metadata elements for a field. As shown in the example above,
the
Field()function only supports a limited set of constraints/metadata, and you may have to use different Pydantic utilities such asWithJsonSchemain some cases.
But note that:
- You should use the assignment form for metadata that has a meaning for static type checkers. This includes:
alias,defaultanddefault_factory. field specific metadata can only be used on the "top-level" type. A common pitfall is to do the following:
from typing import Annotated from pydantic import BaseModel, Field class Model(BaseModel): field_bad: Annotated[int, Field(deprecated=True)] | None = None field_ok: Annotated[int | None, Field(deprecated=True)] = Nonefield specific metadata should apply to the whole union in this example.
Constraints
As much as possible, use the "built-in" validation constraints, instead of defining custom validators:
from typing import Annotated
from annotated_types import Gt # annotated_types is an alternative to the `Field()` function.
from pydantic import BaseModel, field_validator
class Model(BaseModel):
constrained_int_ok: Annotated[int, Gt(1)] # This is good
constrained_int_bad: int
@field_validator('constrained_int_bad') # This is bad
@classmethod
def validate(cls, v: int) -> int:
if not v > 1:
raise ValueError('Value is not greater than 1')
return vSometimes, constraints can't be expressed using the Field() function. For example, string constraints such
as strip_whitespace, to_upper, to_lower and ascii_only can only be specified using pydantic.StringConstraints:
from typing import Annotated
from pydantic import BaseModel, StringConstraints
class Model(BaseModel):
# Do this instead of a validator calling s.strip():
a: Annotated[str, StringConstraints(strip_whitespace=True)]https://pydantic.dev/docs/validation/latest/api/pydantic/standard_library_types/ is the canonical documentation for all supported standard library types and their constraints.
Validators
In some cases, you may have to use custom validators. As much as possible, use after validators. Because they run after Pydantic validation, the value is already the field's type. If you use before validators, the input data can literally be anything, so it is more error-prone (especially for model validators, the input isn't necessarily a dict, it can also be an arbitrary object).
If possible, prefer using the annotated pattern for validators:
from typing import Annotated
from pydantic import AfterValidator, BaseModel, field_validator
def is_even(value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value
class Model(BaseModel):
# Prefer this form: the validator is right next to the field, making it easy to understand
even: Annotated[int, AfterValidator(is_even)]
odd: int
# If you define a validator as decorator, make sure to define it as classmethod.
@field_validator('odd', mode='after')
@classmethod
def is_odd(cls, value: int) -> int:
if value % 2 == 0:
raise ValueError(f'{value} is not an odd number')
return valueUsing the decorator pattern can lead to unclear behavior, especially regarding the order in which validators run (in particular on subclasses).
Type coercion, collections and unions
Unless you are using strict mode, Pydantic applies
type coercion in most cases. For instance, for a field typed as int, strings like '123' will be accepted. This also
applies to collection types: list[str] also accepts tuples, sets etc.
This is why you should avoid:
- using unions such as
int | str, if your goal is to coerce thestrto anintvia a validator. - using abstract collections such as
collections.abc.Sequence, if your goal is to accept both lists and tuples. Using these abstract collections is inefficient.
In the general case, unions are best avoided because every use of the field will need to check for each type before doing anything with it.
Forward annotations
Python has the ability to write annotations as forward references, by using strings. This can cause challenges for Pydantic to evaluate them, so they are best avoided if possible.
If you are defining Pydantic models in a module, avoid using from __future__ import annotations if possible
(which stringifies all annotations by default). Only add explicit quotes to annotations that aren't defined yet, e.g.:
from pydantic import BaseModel
class Model(BaseModel):
self_ref: 'Model'Also note that in Python >= 3.14, annotation evaluation is deferred, so you should not use string annotations at all.
Recursive type aliases
You might be tempted to define aliases like this:
from typing import TypeAlias
JsonValue: TypeAlias = 'list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None'The alias needs to be quoted because it is recursive. Pydantic will generally not be able to evaluate a quoted TypeAlias.
Instead, use an explicit type alias (type on Python 3.12+, or TypeAliasType), which Pydantic can resolve:
type JsonValue = list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None
# Or, if not on Python >= 3.12:
from typing_extensions import TypeAliasType
JsonValue = TypeAliasType('JsonValue', 'list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None')Model subclasses, discriminated unions
Subclassing is a really common Python pattern, but can be a footgun in Pydantic. You might be tempted to do:
from pydantic import BaseModel
class Base(BaseModel):
base_field: int
def common_method(self) -> None: ...
class Sub1(Base):
sub1_field: str
class Sub2(Base):
sub2_field: bool
class Main(BaseModel):
model: Base
m: Main = Main(model=Sub1(base_field=1, sub1_field='test'))This example works, but will not behave as expected when serializing m:
m.model_dump()
#> {'model': {'base_field': 1}} -> sub1_field missingThis is because Pydantic serializes according to the declared type (Base), not the runtime subclass.
Validation follows the same rule: Main(model={'base_field': 1, 'sub1_field': 'test'}) validates against Base,
so sub1_field is ignored rather than producing a Sub1 instance.
Instead, try to use discriminated unions (provided that you can set a type field to distinguish models):
from typing import Annotated, Literal, TypeAlias
from pydantic import BaseModel, Field
class Sub1(Base):
type: Literal['sub1']
sub1_field: str
class Sub2(Base):
type: Literal['sub2']
sub2_field: bool
Subs: TypeAlias = Annotated[Sub1 | Sub2, Field(discriminator='type')]
class Main(BaseModel):
model: Subsor generics:
from pydantic import BaseModel
class Main[BaseT: Base](BaseModel):
model: BaseT
m: Main[Sub1] = Main[Sub1](model={'base_field': 1, 'sub1_field': 'test'}) # Will workIf neither discriminated unions nor generics fit, polymorphic serialization (in Pydantic >=2.13) or serialize as any (in Pydantic <2.13) can be used as a last resort.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

