diff --git a/pydantic_settings/sources.py b/pydantic_settings/sources.py index 203e103..06fdee9 100644 --- a/pydantic_settings/sources.py +++ b/pydantic_settings/sources.py @@ -1563,6 +1563,15 @@ def _add_parser_args( ) -> ArgumentParser: subparsers: Any = None alias_path_args: dict[str, str] = {} + # Ignore model default if the default is a model and not a subclass of the current model. + model_default = ( + None + if ( + (is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default))) + and not issubclass(type(model_default), model) + ) + else model_default + ) for field_name, field_info in self._sort_arg_fields(model): sub_models: list[type[BaseModel]] = self._get_sub_models(model, field_name, field_info) alias_names, is_alias_path_only = _get_alias_names( diff --git a/tests/test_source_cli.py b/tests/test_source_cli.py index 79aee16..c53e777 100644 --- a/tests/test_source_cli.py +++ b/tests/test_source_cli.py @@ -447,6 +447,46 @@ class MultilineDoc(BaseSettings, cli_parse_args=True): ) +def test_cli_help_union_of_models(capsys, monkeypatch): + class Cat(BaseModel): + meow: str = 'meow' + + class Dog(BaseModel): + bark: str = 'bark' + + class Bird(BaseModel): + caww: str = 'caww' + tweet: str + + class Tiger(Cat): + roar: str = 'roar' + + class Car(BaseSettings, cli_parse_args=True): + driver: Union[Cat, Dog, Bird] = Tiger(meow='purr') + + with monkeypatch.context() as m: + m.setattr(sys, 'argv', ['example.py', '--help']) + + with pytest.raises(SystemExit): + Car() + assert ( + capsys.readouterr().out + == f"""usage: example.py [-h] [--driver JSON] [--driver.meow str] [--driver.bark str] + [--driver.caww str] [--driver.tweet str] + +{ARGPARSE_OPTIONS_TEXT}: + -h, --help show this help message and exit + +driver options: + --driver JSON set driver from JSON string + --driver.meow str (default: purr) + --driver.bark str (default: bark) + --driver.caww str (default: caww) + --driver.tweet str (ifdef: required) +""" + ) + + def test_cli_help_default_or_none_model(capsys, monkeypatch): class DeeperSubModel(BaseModel): flag: bool