Pydantic

Pydantic-Settings: Powerful Application Configuration Management.

Application config, environmental variables and secrets, all validated and available with ease.

This powerful library, built on top of Pydantic, provides an elegant way to define and manage settings in your Python applications. In this article, we’ll explore the installation process, delve into the basics, and showcase some examples to help you harness the full potential of Pydantic-Settings.

Important note, this library used to be a part of Pydantic proper until quite recently, so you may find that a bit of googling doesn’t provide that much for Pydantic-Settings as a standalone lib.

Get Started

I’m a fan of Poetry, so I’ll reference that here, but you can use any of the package management flavours you fancy.

poetry add pydantic-settings

And you’re good to go.

So, the whole point is to import and validate application settings, environmental variables, secrets, etc. into a single config object that can be referred to throughout your application, something that is not particularly easy, elegant or performant with a mountain of os.environ.get() calls everywhere. How we do this is very simply a class that inherits the provided BaseSettings class.

from pydantic_settings import BaseSettings


class AppSettings(BaseSettings):
mailchimp_api_key: str


app_settings = AppSettings()


def main():
print(f"Mailchimp API key is {app_settings.mailchimp_api_key}")


if __name__ == "__main__":
main()

Running this returns the following:

pydantic_core._pydantic_core.ValidationError: 1 validation error for AppSettings
mailchimp_api_key
Field required [type=missing, input_value={}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.7/v/missing

Fantastic, we have the most rudimentary validation that checks an attribute is actually available to our application, and we’ve not actually done anything ourselves at all. Let’s see what happens if we give it what it’s asking for with environmental variables.

$ MAILCHIMP_API_KEY=foo python main.py
Mailchimp API key is foo

Super easy peasy. Now, what if you have your environmental variables in a .env file? For that we simply add a model_config attribute to our AppSettings class like so…

from pydantic_settings import BaseSettings, SettingsConfigDict


class AppSettings(BaseSettings):
mailchimp_api_key: str

model_config = SettingsConfigDict(env_file='.env')

Now, we can move our variables off the CLI and into a .env file.

# .env
MAILCHIMP_API_KEY=foo

$ python main.py
Mailchimp API key is foo

In the real world, we often have multiple .env files to reflect the different environments our application will live in, examples would be development, testing and production. Whilst the above example reflects how we would go about a single environmental file, how I would go about defining the settings class based on the actual environment would be something like below:

import os
from pydantic_settings import BaseSettings, SettingsConfigDict


class AppSettingsBase(BaseSettings):
mailchimp_api_key: str


class AppSettingsDev(AppSettingsBase):
model_config = SettingsConfigDict(env_file='.env.dev')


class AppSettingsTesting(AppSettingsBase):
model_config = SettingsConfigDict(env_file='.env.test')


class AppSettingsProd(AppSettingsBase):
model_config = SettingsConfigDict(env_file='.env')


def load_settings():
return {
"dev": AppSettingsDev,
"test": AppSettingsTesting,
"production": AppSettingsProd
}[os.environ.get("ENV", "production")]()


def main(app_settings: AppSettingsBase):
print(f"Mailchimp API key is {app_settings.mailchimp_api_key}")


if __name__ == "__main__":
app_settings = load_settings()
main(app_settings)

$ python main.py
Mailchimp API key is bar

$ ENV=production python main.py
Mailchimp API key is bar

$ ENV=dev python main.py
Mailchimp API key is foo

$ ENV=test python main.py
Mailchimp API key is baz

By loading an AppSettingsBase-derived object based on a defined environment, we can have as many versions of the application as we want.