Even though env_prefix='USER_' is set as shown below, environment variables without the prefix are still being used.
It’s hard to believe that this behavior is intentional—having env_prefix applied but ignored for aliases is not a realistic use case. This seems to be the same kind of issue described in #441.
Is this behavior meant to mirror how Field(env=...) worked in v1 by mapping it to Field(validation_alias=...) in v2?
Ignoring the prefix makes sense for the env argument, but doing so for validation_alias seems like a bug to me.
If ignoring the prefix is necessary, shouldn’t a separate mechanism be added to explicitly ignore the prefix?
Introduced in #25
related: #220, #245, #441, #648
import os
import sys
from pydantic import AliasChoices, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class User(BaseSettings, cli_parse_args=True):
model_config = SettingsConfigDict(env_prefix='USER_')
first_name: str = Field(validation_alias=AliasChoices('fname'))
last_name: str = Field(validation_alias=AliasChoices('l', 'lname'))
@classmethod
def dump(cls):
try:
print(cls().model_dump())
except Exception as e:
print(e)
print('----')
sys.argv = ['example.py']
os.environ.clear()
os.environ['FNAME'] = 'John'
os.environ['LNAME'] = 'Doe'
User.dump()
# {'first_name': 'John', 'last_name': 'Doe'}
os.environ.clear()
os.environ['USER_FNAME'] = 'John'
os.environ['USER_LNAME'] = 'Doe'
User.dump()
# pydantic_core._pydantic_core.ValidationError: 2 validation errors for User
# fname
# Field required [type=missing, input_value={}, input_type=dict]
# For further information visit https://errors.pydantic.dev/2.12/v/missing
# l
# Field required [type=missing, input_value={}, input_type=dict]
# For further information visit https://errors.pydantic.dev/2.12/v/missing
A concrete example of when this becomes a problem
As a common case, consider a --path parameter with a short alias -p, and assume env_prefix='USER_'.
Without an alias, I get the expected error when no arguments are provided. With an alias, however, $PATH is passed instead. This is almost certainly not what anyone would want.
class LongOnly(
BaseSettings,
cli_parse_args=True,
cli_implicit_flags='toggle',
):
model_config = SettingsConfigDict(env_prefix='USER_')
path: pathlib.Path
class LongShort(
BaseSettings,
cli_parse_args=True,
cli_implicit_flags='toggle',
):
model_config = SettingsConfigDict(env_prefix='USER_')
path: pathlib.Path = Field(validation_alias=AliasChoices('p', 'path'))
def main(args: list[str] | None = None):
print(CliApp.run(LongShort, cli_args=args))
print(CliApp.run(LongOnly, cli_args=args))
if __name__ == '__main__':
main()
Even though
env_prefix='USER_'is set as shown below, environment variables without the prefix are still being used.It’s hard to believe that this behavior is intentional—having
env_prefixapplied but ignored for aliases is not a realistic use case. This seems to be the same kind of issue described in #441.Is this behavior meant to mirror how
Field(env=...)worked in v1 by mapping it toField(validation_alias=...)in v2?Ignoring the prefix makes sense for the
envargument, but doing so forvalidation_aliasseems like a bug to me.If ignoring the prefix is necessary, shouldn’t a separate mechanism be added to explicitly ignore the prefix?
Introduced in #25
related: #220, #245, #441, #648
A concrete example of when this becomes a problem
As a common case, consider a
--pathparameter with a short alias-p, and assumeenv_prefix='USER_'.Without an alias, I get the expected error when no arguments are provided. With an alias, however,
$PATHis passed instead. This is almost certainly not what anyone would want.