Skip to content

Commit 0e9b329

Browse files
kschwabhramezani
andauthored
CLI JSON Optional Default (#581)
Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com>
1 parent fde79e8 commit 0e9b329

File tree

3 files changed

+54
-35
lines changed

3 files changed

+54
-35
lines changed

pydantic_settings/main.py

+1
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ def run(
559559
if not issubclass(model_cls, BaseSettings):
560560

561561
class CliAppBaseSettings(BaseSettings, model_cls): # type: ignore
562+
__doc__ = model_cls.__doc__
562563
model_config = SettingsConfigDict(
563564
nested_model_default_partial_update=True,
564565
case_sensitive=True,

pydantic_settings/sources/providers/cli.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,9 @@ def _add_parser_submodels(
908908
preferred_alias = alias_names[0]
909909
if not self.cli_avoid_json:
910910
added_args.append(arg_names[0])
911-
kwargs['help'] = f'set {arg_names[0]} from JSON string'
911+
kwargs['nargs'] = '?'
912+
kwargs['const'] = '{}'
913+
kwargs['help'] = f'set {arg_names[0]} from JSON string (default: {{}})'
912914
model_group = self._add_group(parser, **model_group_kwargs)
913915
self._add_argument(model_group, *(f'{flag_prefix}{name}' for name in arg_names), **kwargs)
914916
for model in sub_models:

tests/test_source_cli.py

+50-34
Original file line numberDiff line numberDiff line change
@@ -499,14 +499,14 @@ class Car(BaseSettings, cli_parse_args=True):
499499
Car()
500500
assert (
501501
capsys.readouterr().out
502-
== f"""usage: example.py [-h] [--driver JSON] [--driver.meow str] [--driver.bark str]
503-
[--driver.caww str] [--driver.tweet str]
502+
== f"""usage: example.py [-h] [--driver [JSON]] [--driver.meow str]
503+
[--driver.bark str] [--driver.caww str] [--driver.tweet str]
504504
505505
{ARGPARSE_OPTIONS_TEXT}:
506506
-h, --help show this help message and exit
507507
508508
driver options:
509-
--driver JSON set driver from JSON string
509+
--driver [JSON] set driver from JSON string (default: {{}})
510510
--driver.meow str (default: purr)
511511
--driver.bark str (default: bark)
512512
--driver.caww str (default: caww)
@@ -540,73 +540,75 @@ class Settings(BaseSettings, cli_parse_args=True):
540540
Settings()
541541
assert (
542542
capsys.readouterr().out
543-
== f"""usage: example.py [-h] [--flag bool] [--sub_model JSON]
544-
[--sub_model.flag bool] [--sub_model.deep JSON]
543+
== f"""usage: example.py [-h] [--flag bool] [--sub_model [JSON]]
544+
[--sub_model.flag bool] [--sub_model.deep [JSON]]
545545
[--sub_model.deep.flag bool]
546-
[--sub_model.deep.deeper {{JSON,null}}]
546+
[--sub_model.deep.deeper [{{JSON,null}}]]
547547
[--sub_model.deep.deeper.flag bool]
548-
[--opt_model {{JSON,null}}] [--opt_model.flag bool]
549-
[--opt_model.deeper {{JSON,null}}]
550-
[--opt_model.deeper.flag bool] [--fact_model JSON]
551-
[--fact_model.flag bool] [--fact_model.deep JSON]
548+
[--opt_model [{{JSON,null}}]] [--opt_model.flag bool]
549+
[--opt_model.deeper [{{JSON,null}}]]
550+
[--opt_model.deeper.flag bool] [--fact_model [JSON]]
551+
[--fact_model.flag bool] [--fact_model.deep [JSON]]
552552
[--fact_model.deep.flag bool]
553-
[--fact_model.deep.deeper {{JSON,null}}]
553+
[--fact_model.deep.deeper [{{JSON,null}}]]
554554
[--fact_model.deep.deeper.flag bool]
555555
556556
{ARGPARSE_OPTIONS_TEXT}:
557557
-h, --help show this help message and exit
558558
--flag bool (default: True)
559559
560560
sub_model options:
561-
--sub_model JSON set sub_model from JSON string
561+
--sub_model [JSON] set sub_model from JSON string (default: {{}})
562562
--sub_model.flag bool
563563
(default: False)
564564
565565
sub_model.deep options:
566-
--sub_model.deep JSON
567-
set sub_model.deep from JSON string
566+
--sub_model.deep [JSON]
567+
set sub_model.deep from JSON string (default: {{}})
568568
--sub_model.deep.flag bool
569569
(default: True)
570570
571571
sub_model.deep.deeper options:
572572
default: null (undefined)
573573
574-
--sub_model.deep.deeper {{JSON,null}}
575-
set sub_model.deep.deeper from JSON string
574+
--sub_model.deep.deeper [{{JSON,null}}]
575+
set sub_model.deep.deeper from JSON string (default:
576+
{{}})
576577
--sub_model.deep.deeper.flag bool
577578
(ifdef: required)
578579
579580
opt_model options:
580581
default: null (undefined)
581582
Group Doc
582583
583-
--opt_model {{JSON,null}}
584-
set opt_model from JSON string
584+
--opt_model [{{JSON,null}}]
585+
set opt_model from JSON string (default: {{}})
585586
--opt_model.flag bool
586587
(ifdef: required)
587588
588589
opt_model.deeper options:
589590
default: null (undefined)
590591
591-
--opt_model.deeper {{JSON,null}}
592-
set opt_model.deeper from JSON string
592+
--opt_model.deeper [{{JSON,null}}]
593+
set opt_model.deeper from JSON string (default: {{}})
593594
--opt_model.deeper.flag bool
594595
(ifdef: required)
595596
596597
fact_model options:
597-
--fact_model JSON set fact_model from JSON string
598+
--fact_model [JSON] set fact_model from JSON string (default: {{}})
598599
--fact_model.flag bool
599600
(default factory: <lambda>)
600601
601602
fact_model.deep options:
602-
--fact_model.deep JSON
603-
set fact_model.deep from JSON string
603+
--fact_model.deep [JSON]
604+
set fact_model.deep from JSON string (default: {{}})
604605
--fact_model.deep.flag bool
605606
(default factory: <lambda>)
606607
607608
fact_model.deep.deeper options:
608-
--fact_model.deep.deeper {{JSON,null}}
609-
set fact_model.deep.deeper from JSON string
609+
--fact_model.deep.deeper [{{JSON,null}}]
610+
set fact_model.deep.deeper from JSON string (default:
611+
{{}})
610612
--fact_model.deep.deeper.flag bool
611613
(default factory: <lambda>)
612614
"""
@@ -1529,13 +1531,13 @@ class Settings(BaseSettings):
15291531

15301532
assert (
15311533
capsys.readouterr().out
1532-
== f"""usage: example.py [-h] [--sub_model JSON] [--sub_model.v1 int]
1534+
== f"""usage: example.py [-h] [--sub_model [JSON]] [--sub_model.v1 int]
15331535
15341536
{ARGPARSE_OPTIONS_TEXT}:
15351537
-h, --help show this help message and exit
15361538
15371539
sub_model options:
1538-
--sub_model JSON set sub_model from JSON string
1540+
--sub_model [JSON] set sub_model from JSON string (default: {{}})
15391541
--sub_model.v1 int (required)
15401542
"""
15411543
)
@@ -1573,13 +1575,13 @@ class Settings(BaseSettings):
15731575

15741576
assert (
15751577
capsys.readouterr().out
1576-
== f"""usage: example.py [-h] [--sub_model JSON]
1578+
== f"""usage: example.py [-h] [--sub_model [JSON]]
15771579
15781580
{ARGPARSE_OPTIONS_TEXT}:
1579-
-h, --help show this help message and exit
1581+
-h, --help show this help message and exit
15801582
15811583
sub_model options:
1582-
--sub_model JSON set sub_model from JSON string
1584+
--sub_model [JSON] set sub_model from JSON string (default: {{}})
15831585
"""
15841586
)
15851587

@@ -1653,7 +1655,7 @@ class Settings(BaseSettings):
16531655

16541656
assert (
16551657
capsys.readouterr().out
1656-
== f"""usage: example.py [-h] [--sub_model JSON] [--sub_model.v1 int]
1658+
== f"""usage: example.py [-h] [--sub_model [JSON]] [--sub_model.v1 int]
16571659
16581660
My application help text.
16591661
@@ -1663,7 +1665,7 @@ class Settings(BaseSettings):
16631665
sub_model options:
16641666
The help text from the field description
16651667
1666-
--sub_model JSON set sub_model from JSON string
1668+
--sub_model [JSON] set sub_model from JSON string (default: {{}})
16671669
--sub_model.v1 int (required)
16681670
"""
16691671
)
@@ -1673,7 +1675,7 @@ class Settings(BaseSettings):
16731675

16741676
assert (
16751677
capsys.readouterr().out
1676-
== f"""usage: example.py [-h] [--sub_model JSON] [--sub_model.v1 int]
1678+
== f"""usage: example.py [-h] [--sub_model [JSON]] [--sub_model.v1 int]
16771679
16781680
My application help text.
16791681
@@ -1683,7 +1685,7 @@ class Settings(BaseSettings):
16831685
sub_model options:
16841686
The help text from the class docstring
16851687
1686-
--sub_model JSON set sub_model from JSON string
1688+
--sub_model [JSON] set sub_model from JSON string (default: {{}})
16871689
--sub_model.v1 int (required)
16881690
"""
16891691
)
@@ -2411,3 +2413,17 @@ class Root(BaseModel):
24112413
--deep-arg str (required)
24122414
"""
24132415
)
2416+
2417+
2418+
def test_cli_json_optional_default():
2419+
class Nested(BaseModel):
2420+
foo: int = 1
2421+
bar: int = 2
2422+
2423+
class Options(BaseSettings):
2424+
nested: Nested = Nested(foo=3, bar=4)
2425+
2426+
assert CliApp.run(Options, cli_args=[]).model_dump() == {'nested': {'foo': 3, 'bar': 4}}
2427+
assert CliApp.run(Options, cli_args=['--nested']).model_dump() == {'nested': {'foo': 1, 'bar': 2}}
2428+
assert CliApp.run(Options, cli_args=['--nested={}']).model_dump() == {'nested': {'foo': 1, 'bar': 2}}
2429+
assert CliApp.run(Options, cli_args=['--nested.foo=5']).model_dump() == {'nested': {'foo': 5, 'bar': 2}}

0 commit comments

Comments
 (0)