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.