pydantic / pydantic-settings

Settings management using pydantic

Home Page:https://docs.pydantic.dev/latest/usage/pydantic_settings/

Repository from Github https://github.compydantic/pydantic-settingsRepository from Github https://github.compydantic/pydantic-settings

CLI use of `IntEnum` not working with nested models

lsg551 opened this issue · comments

Description

The pydantic docs about the usage of enums in CLIs state that enum.IntEnum can perfectly be used with CLIs, where the enum's field names are eventually accepted as flag values.

[excerpt] from the docs:

CLI argument parsing of literals and enums are converted into CLI choices.

import sys
from enum import IntEnum
from pydantic_settings import BaseSettings

class Fruit(IntEnum):
    pear = 0
    kiwi = 1
    lime = 2

class Settings(BaseSettings, cli_parse_args=True):
    fruit: Fruit

sys.argv = ['example.py', '--fruit', 'lime']
print(Settings().model_dump())
#> {'fruit': <Fruit.lime: 2>}

And this works totally fine. But as soon as a model is nested, it raises a validation error. For example, consider this scenario:

from enum import IntEnum

class LogLevel(IntEnum):
    CRITICAL = 50
    ERROR = 40
    WARNING = 30
    INFO = 20
    DEBUG = 10

@pydantic.dataclasses.dataclass
class LoggingOptions:
    level: LogLevel = LogLevel.INFO

class Settings(BaseSettings):

    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_kebab_case=True,
        cli_use_class_docs_for_groups=True,
        cli_avoid_json=True,
        frozen=True,
    )

    level: LogLevel = LogLevel.INFO # this works

    logging: LoggingOptions # this DOES NOT work

When tested, the root level flags work as intended with integer enums:

 sys.argv = ['example.py', '--level', 'DEBUG']
 settings = Settings()

However, nested ones do not:

 sys.argv = ['example.py', '--logging.level', 'DEBUG']
 settings = Settings()

and this will raise:

Traceback (most recent call last):
  File "/project/sandbox/main.py", line 55, in <module>
    settings = Settings()
               ^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic_settings/main.py", line 176, in __init__
    super().__init__(
  File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 253, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
logging.level
  Input should be 50, 50, 40, 30, 30, 20, 10 or 0 [type=enum, input_value='DEBUG', input_type=str]
    For further information visit https://errors.pydantic.dev/2.11/v/enum

Expected Behaviour

I expected this to work properly with nested models too, just like root level objects. However, I am not familiar enough with pydantic or pydantic-settings to tell whether this is intended / expected behaviour.

I searched the docs and issues, but couldn't find related information. Sorry, if this was already brought up.

How to replicate

Note that the code of the above examples is simplified, but I created a codesandbox to replicate this: https://codesandbox.io/p/devbox/59rv9y?embed=1&file=%2Fmain.py

Affected Versions

I tested with the latest versions of pydantic and pydantic-settings:

  • pydantic == 2.11.3
  • pydantic-settings == 2.8.1

and used Python 3.12 as well as 3.13.

@kschwab please take a look when you have time. sorry for pinging you too much, you are the only person who is expert in CLI.

No worries @hramezani, happy to help. Opened #589 for resolution.

Thanks @kschwab for the fix PR and sorry for asking you to fix a non cli bug.

Greatly appreciate that, thanks!