Skip to content

There is no way to apply env_prefix to aliases #748

@kzrnm

Description

@kzrnm

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()

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions