Skip to content

Commit 1775221

Browse files
committed
CLI submodel suppress.
1 parent d54d146 commit 1775221

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

pydantic_settings/sources/providers/cli.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ def _add_parser_args(
665665
group: Any,
666666
alias_prefixes: list[str],
667667
model_default: Any,
668+
is_model_suppressed: bool = False,
668669
) -> ArgumentParser:
669670
subparsers: Any = None
670671
alias_path_args: dict[str, str] = {}
@@ -738,7 +739,7 @@ def _add_parser_args(
738739
is_parser_submodel = sub_models and not is_append_action
739740
kwargs: dict[str, Any] = {}
740741
kwargs['default'] = CLI_SUPPRESS
741-
kwargs['help'] = self._help_format(field_name, field_info, model_default)
742+
kwargs['help'] = self._help_format(field_name, field_info, model_default, is_model_suppressed)
742743
kwargs['metavar'] = self._metavar_format(field_info.annotation)
743744
kwargs['required'] = (
744745
self.cli_enforce_required and field_info.is_required() and model_default is PydanticUndefined
@@ -782,6 +783,7 @@ def _add_parser_args(
782783
field_info,
783784
alias_names,
784785
model_default=model_default,
786+
is_model_suppressed=is_model_suppressed,
785787
)
786788
elif not is_alias_path_only:
787789
if group is not None:
@@ -869,6 +871,7 @@ def _add_parser_submodels(
869871
field_info: FieldInfo,
870872
alias_names: tuple[str, ...],
871873
model_default: Any,
874+
is_model_suppressed: bool,
872875
) -> None:
873876
if issubclass(model, CliMutuallyExclusiveGroup):
874877
# Argparse has deprecated "calling add_argument_group() or add_mutually_exclusive_group() on a
@@ -906,9 +909,10 @@ def _add_parser_submodels(
906909
model_group_kwargs['description'] = desc_header
907910

908911
preferred_alias = alias_names[0]
912+
is_model_suppressed = self._is_field_suppressed(field_info) or is_model_suppressed
909913
if not self.cli_avoid_json:
910914
added_args.append(arg_names[0])
911-
kwargs['help'] = f'set {arg_names[0]} from JSON string'
915+
kwargs['help'] = CLI_SUPPRESS if is_model_suppressed else f'set {arg_names[0]} from JSON string'
912916
model_group = self._add_group(parser, **model_group_kwargs)
913917
self._add_argument(model_group, *(f'{flag_prefix}{name}' for name in arg_names), **kwargs)
914918
for model in sub_models:
@@ -921,6 +925,7 @@ def _add_parser_submodels(
921925
group=model_group if model_group else model_group_kwargs,
922926
alias_prefixes=[f'{arg_prefix}{name}.' for name in alias_names[1:]],
923927
model_default=model_default,
928+
is_model_suppressed=is_model_suppressed,
924929
)
925930

926931
def _add_parser_alias_paths(
@@ -1013,9 +1018,11 @@ def _metavar_format_recurse(self, obj: Any) -> str:
10131018
def _metavar_format(self, obj: Any) -> str:
10141019
return self._metavar_format_recurse(obj).replace(', ', ',')
10151020

1016-
def _help_format(self, field_name: str, field_info: FieldInfo, model_default: Any) -> str:
1021+
def _help_format(
1022+
self, field_name: str, field_info: FieldInfo, model_default: Any, is_model_suppressed: bool
1023+
) -> str:
10171024
_help = field_info.description if field_info.description else ''
1018-
if _help == CLI_SUPPRESS or CLI_SUPPRESS in field_info.metadata:
1025+
if is_model_suppressed or self._is_field_suppressed(field_info):
10191026
return CLI_SUPPRESS
10201027

10211028
if field_info.is_required() and model_default in (PydanticUndefined, None):
@@ -1035,3 +1042,7 @@ def _help_format(self, field_name: str, field_info: FieldInfo, model_default: An
10351042
default = f'(default factory: {self._metavar_format(field_info.default_factory)})'
10361043
_help += f' {default}' if _help else default
10371044
return _help.replace('%', '%%') if issubclass(type(self._root_parser), ArgumentParser) else _help
1045+
1046+
def _is_field_suppressed(self, field_info: FieldInfo) -> bool:
1047+
_help = field_info.description if field_info.description else ''
1048+
return _help == CLI_SUPPRESS or CLI_SUPPRESS in field_info.metadata

tests/test_source_cli.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,9 +2137,25 @@ def cli_cmd(self) -> None:
21372137

21382138

21392139
def test_cli_suppress(capsys, monkeypatch):
2140+
class DeepHiddenSubModel(BaseModel):
2141+
deep_hidden_a: int
2142+
deep_hidden_b: int
2143+
2144+
class HiddenSubModel(BaseModel):
2145+
hidden_a: int
2146+
hidden_b: int
2147+
deep_hidden_obj: DeepHiddenSubModel
2148+
2149+
class SubModel(BaseModel):
2150+
visible_a: int
2151+
visible_b: int
2152+
deep_hidden_obj: CliSuppress[DeepHiddenSubModel]
2153+
21402154
class Settings(BaseSettings, cli_parse_args=True):
21412155
field_a: CliSuppress[int] = 0
21422156
field_b: str = Field(default=1, description=CLI_SUPPRESS)
2157+
hidden_obj: CliSuppress[HiddenSubModel]
2158+
visible_obj: SubModel
21432159

21442160
with monkeypatch.context() as m:
21452161
m.setattr(sys, 'argv', ['example.py', '--help'])
@@ -2149,10 +2165,18 @@ class Settings(BaseSettings, cli_parse_args=True):
21492165

21502166
assert (
21512167
capsys.readouterr().out
2152-
== f"""usage: example.py [-h]
2168+
== f"""usage: example.py [-h] [--visible_obj JSON] [--visible_obj.visible_a int]
2169+
[--visible_obj.visible_b int]
21532170
21542171
{ARGPARSE_OPTIONS_TEXT}:
2155-
-h, --help show this help message and exit
2172+
-h, --help show this help message and exit
2173+
2174+
visible_obj options:
2175+
--visible_obj JSON set visible_obj from JSON string
2176+
--visible_obj.visible_a int
2177+
(required)
2178+
--visible_obj.visible_b int
2179+
(required)
21562180
"""
21572181
)
21582182

0 commit comments

Comments
 (0)