Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pydantic_settings/sources/providers/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def get_enum_names(
for type_ in get_args(annotation):
enum_names += cls.get_enum_names(type_, kebab_case)
if annotation and _lenient_issubclass(annotation, Enum):
enum_names += tuple(cls.get_kebab_case(val.name, kebab_case == 'all') for val in annotation)
enum_names += tuple(cls.get_kebab_case(name, kebab_case == 'all') for name in annotation.__members__.keys())
return enum_names

def subcommand_alias(self, sub_model: type[BaseModel]) -> str:
Expand Down Expand Up @@ -1255,7 +1255,7 @@ def _metavar_format_recurse(self, obj: Any) -> str:
return self._metavar_format_choices(list(map(str, self._get_modified_args(obj))))
elif _lenient_issubclass(obj, Enum):
return self._metavar_format_choices(
[_CliArg.get_kebab_case(val.name, self.cli_kebab_case == 'all') for val in obj]
[_CliArg.get_kebab_case(name, self.cli_kebab_case == 'all') for name in obj.__members__.keys()]
)
elif isinstance(obj, _WithArgsTypes):
return self._metavar_format_choices(
Expand Down
4 changes: 2 additions & 2 deletions pydantic_settings/sources/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,15 @@ def _strip_annotated(annotation: Any) -> Any:
def _annotation_enum_val_to_name(annotation: type[Any] | None, value: Any) -> str | None:
for type_ in (annotation, get_origin(annotation), *get_args(annotation)):
if _lenient_issubclass(type_, Enum):
if value in tuple(val.value for val in type_):
if value in type_.__members__.values():
return type_(value).name
return None


def _annotation_enum_name_to_val(annotation: type[Any] | None, name: Any) -> Any:
for type_ in (annotation, get_origin(annotation), *get_args(annotation)):
if _lenient_issubclass(type_, Enum):
if name in tuple(val.name for val in type_):
if name in type_.__members__.keys():
return type_[name]
return None

Expand Down
21 changes: 13 additions & 8 deletions tests/test_source_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,7 @@ class Pet(IntEnum):
dog = 0
cat = 1
bird = 2
crow = 2

class Cfg(BaseSettings):
pet: Pet = Pet.dog
Expand All @@ -1452,15 +1453,18 @@ class Cfg(BaseSettings):
cfg = CliApp.run(Cfg, cli_args=['--pet', 'cat', '--union_pet', 'dog'])
assert cfg.model_dump() == {'pet': Pet.cat, 'union_pet': Pet.dog}

cfg = CliApp.run(Cfg, cli_args=['--pet', 'crow', '--union_pet', 'dog'])
assert cfg.model_dump() == {'pet': Pet.crow, 'union_pet': Pet.dog}

with pytest.raises(ValidationError) as exc_info:
CliApp.run(Cfg, cli_args=['--pet', 'rock'])
assert exc_info.value.errors(include_url=False) == [
{
'type': 'enum',
'loc': ('pet',),
'msg': 'Input should be 0, 1 or 2',
'msg': 'Input should be 0, 1, 2 or 2',
'input': 'rock',
'ctx': {'expected': '0, 1 or 2'},
'ctx': {'expected': '0, 1, 2 or 2'},
}
]

Expand All @@ -1469,22 +1473,23 @@ class Cfg(BaseSettings):

if PYTHON_3_14:
if IS_WINDOWS:
text = ' [--union_pet {{dog,cat,bird},int}]'
text = ' [--union_pet {{dog,cat,bird,crow},int}]'
else:
text = ' [--union_pet {{dog,cat,bird},int}]'
text = ' [--union_pet {{dog,cat,bird,crow},int}]'
else:
text = ' [--union_pet {{dog,cat,bird},int}]'
text = ' [--union_pet {{dog,cat,bird,crow},int}]'
with pytest.raises(SystemExit):
CliApp.run(Cfg)
assert (
sanitize_cli_output(capsys.readouterr().out)
== f"""usage: example.py [-h] [--pet {{dog,cat,bird}}]
== f"""usage: example.py [-h] [--pet {{dog,cat,bird,crow}}]
{text}

{ARGPARSE_OPTIONS_TEXT}:
-h, --help show this help message and exit
--pet {{dog,cat,bird}} (default: dog)
--union_pet {{{{dog,cat,bird}},int}}
--pet {{dog,cat,bird,crow}}
(default: dog)
--union_pet {{{{dog,cat,bird,crow}},int}}
(default: 43)
"""
)
Expand Down