Settings
Overview
Wirio includes a built-in settings system. We can load values from multiple sources, then read them as single values, sections or Pydantic models.
Source priority
Wirio supports multiple sources. When the same key exists in multiple sources, the last added sources have more priority.
The following sources are loaded, by default, in this order:
settings.jsonsettings.{environment}.json- Environment variables
So, in the default settings, environment variables have higher priority than JSON files because the source is added after them. This means that if a key exists in both settings.json and environment variables, the value from environment variables will be used.
If we add more sources, those will have higher priority than the defaults. For example, if we add Azure Key Vault as a source, it will override the defaults.
services = ServiceCollection()
services.settings.add_azure_key_vault(
"https://example.vault.azure.net"
)
Naming convention
Each source (environment variables, JSON, Azure Key Vault...) has its own naming convention for keys. Wirio uses snake case for settings keys. When loading from sources, keys are normalized to snake case. For example, the APP_NAME environment variable maps to app_name.
Read a Pydantic model
Create a ServiceCollection, then read a model from services.settings.
from pydantic import BaseModel
from wirio import ServiceCollection
class ApplicationSettings(BaseModel):
app_name: str
port: int
services = ServiceCollection()
application_settings = services.settings.get_model(ApplicationSettings)
Register a settings model as a service
Use services.add_settings when we want to read a model once and register it as a singleton service.
from pydantic import BaseModel
from wirio import ServiceCollection
class ApplicationSettings(BaseModel):
app_name: str
port: int
services = ServiceCollection()
services.add_settings(ApplicationSettings)
After registering it, we can resolve ApplicationSettings from the service provider like any other service.
class EmailService:
def __init__(self, application_settings: ApplicationSettings) -> None:
self.application_settings = application_settings
If the model should be loaded from a specific settings section, use the key parameter.
class DatabaseSettings(BaseModel):
host: str
port: int
services = ServiceCollection()
services.add_settings(DatabaseSettings, key="database")
In this case, Wirio reads the model from services.settings.get_section("database").
Read one value
- Use
get_required_valuewhen the key must exist.
openai_api_key = services.settings.get_required_value("openai_api_key")
timeout_seconds = services.settings.get_required_value("timeout_seconds", int)
By default, the settings system returns values as strings. To validate and convert to another type, pass the type as a second argument.
- Use
get_valuefor optional keys.
openai_api_key = services.settings.get_value("openai_api_key")
timeout_seconds = services.settings.get_value("maximum_retries", int)
Defaults and required fields
If a model field has a default, that default is used when no value is found.
from pydantic import BaseModel
class ApplicationSettings(BaseModel):
app_name: str
port: int | None = None
Here, port defaults to None when missing.
If a required field is missing, get_model raises KeyError.
Sections
Use get_section to read a section. For example, you can read the next JSON:
SettingsSection supports:
- The section value itself with
section.get_required_value()orsection.get_required_value(type). - A child value with
section.get_required_value("child:key")orsection.get_required_value("child:key", type).
If a section has only children and no value at its own path, section.get_value() returns None.
Key format
Nested keys use ::
database:hostdatabase:portlogging:log_level:default
Use settings in factories
A common pattern is to read settings inside a factory.
from pydantic import BaseModel
from wirio import ServiceCollection
class ApplicationSettings(BaseModel):
database_connection_string: str
class DatabaseClient:
def __init__(self, connection_string: str) -> None:
self.connection_string = connection_string
services = ServiceCollection()
def inject_database_client() -> DatabaseClient:
settings = services.settings.get_model(ApplicationSettings)
return DatabaseClient(settings.database_connection_string)
services.add_singleton(inject_database_client)
Azure Key Vault
Wirio can read settings values from Azure Key Vault.
- Install the optional dependency:
- Add Key Vault as a source:
If no credential is provided, Wirio uses DefaultAzureCredential.
We can also pass a custom async Azure credential with the credential parameter.
AWS Secrets Manager
Wirio can read settings values from AWS Secrets Manager.
- Install the optional dependency:
- Add Secrets Manager as a source, specifying the secret identifier and, optionally, the region and URL:
The secret value must be a JSON object. Wirio reads and flattens that JSON into settings keys, following the same key behavior as JSON files.