Preface

Pydantic is a powerful data validation and settings management library for Python, engineered to enhance the robustness and reliability of your codebase. From basic tasks, such as checking whether a variable is an integer, to more complex tasks, like ensuring highly-nested dictionary keys and values have the correct data types, Pydantic can handle just about any data validation scenario with minimal boilerplate code.

In this article I try to rectify the core principles behind the description.

Zero Shot

Can you explain what is the main features of Pydantic Python library?

Pydantic is a data validation and settings management library for
Python, built on top of Python's type hints. It allows you to define
data models with validation and conversion rules, leveraging Python
type annotations.

Basics of Pydantic

Pydantic allows you to define models using Python's typing annotations. Fields can have default values, and default factories, or be required.

from typing import Optional
from datetime import datetime
from pydantic import BaseModel, ValidationError


class User(BaseModel):
    id: int
    name: str
    signup_ts: Optional[datetime] = None
    friends: list[int] = []

Data Validation and Parsing

Pydantic automatically validates and parses input data, if correction is obvious it implements it on the fly, if correction is obscure and controversial (the data doesn't conform to the model's type annotations) it raising errors.

user = User(id='123', name='John Doe', signup_ts='2023-01-01T00:00:00', friends=[1, 2, '3'])
print(user)
id=123 name='John Doe' signup_ts=datetime.datetime(2023, 1, 1, 0, 0) friends=[1, 2, 3]
try:
    user = User(id='a23', name='John Doe', signup_ts='2023-01-01T00:00:00', friends=[1, 2, 'b'])
except ValidationError as error_message:
    print(error_message)    
2 validation errors for User
id
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a23', input_type=str]
    For further information visit https://errors.pydantic.dev/2.8/v/int_parsing
friends.2
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str]
    For further information visit https://errors.pydantic.dev/2.8/v/int_parsing

Model Composition

Pydantic models can be nested, allowing for complex data structures.

class Address(BaseModel):
    street: str
    city: str

class User(BaseModel):
    id: int
    name: str
    address: Address

address_data = {'street': '123 Main St', 'city': 'Anytown'}
user_data = {'id': 1, 'name': 'John Doe', 'address': address_data}
user = User(**user_data)  # Automatically handles the nesting
print(user)
id=1 name='John Doe' address=Address(street='123 Main St', city='Anytown')

Data Serialization

Pydantic models can be easily serialized to and deserialized from formats like JSON.

user_json = user.json()  # Serialize to JSON string
user_dict = user.dict()  # Serialize to dictionary
user_copy = User.parse_raw(user_json)  # Deserialize from JSON string
print(user_json)
print(user_dict)
print(user_copy)
{"id":1,"name":"John Doe","address":{"street":"123 Main St","city":"Anytown"}}
{'id': 1, 'name': 'John Doe', 'address': {'street': '123 Main St', 'city': 'Anytown'}}
id=1 name='John Doe' address=Address(street='123 Main St', city='Anytown')

Custom Data Types

Pydantic allows the creation of custom data types by extending the pydantic.BaseModel and providing custom validation logic.

from typing_extensions import Annotated
from pydantic import Field
#from pydantic import ConstrainedInt <-- obsolete

#class PositiveInt(ConstrainedInt):
#    gt = 0

PositiveInt = Annotated[int, Field(ge=0)]

class User(BaseModel):
    id: PositiveInt

user = User(id=42)  # Valid
print(user)
try: user = User(id=-42)  # Raises validation error
except ValidationError as error_message:
    print(error_message)     
id=42
1 validation error for User
id
  Input should be greater than or equal to 0 [type=greater_than_equal, input_value=-42, input_type=int]
    For further information visit https://errors.pydantic.dev/2.8/v/greater_than_equal

Settings Management

Pydantic can be used for settings management by leveraging environment variables through pydantic.BaseSettings.

pip list | grep pydantic

pydantic          2.8.2
pydantic_core     2.20.1

Install pydantic_settings module:

pip install pydantic_settings
Collecting pydantic_settings
  Downloading pydantic_settings-2.3.4-py3-none-any.whl (22 kB)
=2.7.0 in /home/alioth/.virtualenvs/FastAPI/lib/python3.11/site-packages (from pydantic_settings) (2.8.2)
=0.21.0 in /home/alioth/.virtualenvs/FastAPI/lib/python3.11/site-packages (from pydantic_settings) (1.0.1)
=0.4.0 in /home/alioth/.virtualenvs/FastAPI/lib/python3.11/site-packages (from pydantic>=2.7.0->pydantic_settings) (0.7.0)
=2.7.0->pydantic_settings) (2.20.1)
=4.6.1 in /home/alioth/.virtualenvs/FastAPI/lib/python3.11/site-packages (from pydantic>=2.7.0->pydantic_settings) (4.12.2)
Installing collected packages: pydantic_settings
Successfully installed pydantic_settings-2.3.4
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str
    admin_email: str

    class Config:
        env_prefix = 'MY_APP_'  # Environment variables should start with MY_APP_

settings = Settings(app_name='philomath', admin_email='tonyphilomath@gmail.com')
print(settings.app_name)  # Read the value from the MY_APP_APP_NAME environment variable
philomath

Integration and Use Cases

Pydantic integrates well with frameworks like FastAPI for creating web APIs, and Django for model management, among other use cases.

FastAPI Integration:

Pydantic is the data validation and serialization backbone of FastAPI.

Django Integration:

Pydantic models can be used to validate API response payloads.

Further reading and documentation

can be found here:

The Essence

This should provide a comprehensive understanding of Pydantic's main features and capabilities.