-
-
Notifications
You must be signed in to change notification settings - Fork 115
Description
We have a case where we would like to leverage validation_alias in our configs to be the value of the environment variable it reads from, ignoring any nesting for the configuration object.
Similar request threads:
pydantic/pydantic#8989
pydantic/pydantic#8221
Originally this issue was filed as a regression with 2.12, but this was due to me using 2.11 incorrectly, so keeping the original issue below:
Hi! We seem to be hitting a regression with config overrides when updated from 2.11 to 2.12. We have a few layers of config sources that we use to build our config objects.
We set our order to init > env > dotenv > multiple toml files.
We also use validation_alias to make our env variables more explicit (especially when deeply nested). So we expect our env/dotenv sources to use the validation_alias, but all other configs would use the normal field name.
For the regression, if a value is set in a nested object (top level seems to work as expected) in our toml source, we are now unable to override it with environment variables. However, init overrides still work. This leads me to think it is a regression in #688 for fixing #180 (fyi @chbndrhnns @hramezani).
The following is a simple repro for the regression:
from typing import Annotated, ClassVar, override
from pydantic import Field
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
TomlConfigSettingsSource,
)
with open(".env", "w") as f:
_ = f.write("""\
CONFIG_API_KEY=dotenv-api-key
""")
with open("config.toml", "w") as f:
_ = f.write("""\
[api]
api_key = "api-key-from-toml"
""")
class ApiConfig(BaseSettings):
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
env_file=".env",
populate_by_name=True,
extra="ignore",
)
api_key: Annotated[str, Field(validation_alias="CONFIG_API_KEY")]
class Config(BaseSettings):
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
env_file=".env",
populate_by_name=True,
extra="ignore",
)
api: ApiConfig
@override
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
env_settings,
dotenv_settings,
TomlConfigSettingsSource(settings_cls, "config.toml"),
)
print(Config())
# 2.11: api=ApiConfig(api_key='dotenv-api-key') [correct]
# 2.12: api=ApiConfig(api_key='api-key-from-toml') [wrong]
print(Config(api=ApiConfig(api_key="init-api")))
# 2.11: api=ApiConfig(api_key='api-key-from-toml') [wrong]
# 2.12: api=ApiConfig(api_key='init-api') [correct]Thanks for the help!