@@ -1382,6 +1382,7 @@ def _connect_root_parser(
1382
1382
subcommand_prefix = self .env_prefix ,
1383
1383
group = None ,
1384
1384
alias_prefixes = [],
1385
+ model_default = PydanticUndefined ,
1385
1386
)
1386
1387
1387
1388
def _add_parser_args (
@@ -1393,6 +1394,7 @@ def _add_parser_args(
1393
1394
subcommand_prefix : str ,
1394
1395
group : Any ,
1395
1396
alias_prefixes : list [str ],
1397
+ model_default : Any ,
1396
1398
) -> ArgumentParser :
1397
1399
subparsers : Any = None
1398
1400
alias_path_args : dict [str , str ] = {}
@@ -1425,16 +1427,19 @@ def _add_parser_args(
1425
1427
subcommand_prefix = f'{ subcommand_prefix } { field_name } .' ,
1426
1428
group = None ,
1427
1429
alias_prefixes = [],
1430
+ model_default = PydanticUndefined ,
1428
1431
)
1429
1432
else :
1430
1433
resolved_names , is_alias_path_only = self ._get_resolved_names (field_name , field_info , alias_path_args )
1431
1434
arg_flag : str = '--'
1432
1435
kwargs : dict [str , Any ] = {}
1433
1436
kwargs ['default' ] = SUPPRESS
1434
- kwargs ['help' ] = self ._help_format (field_info )
1437
+ kwargs ['help' ] = self ._help_format (field_name , field_info , model_default )
1435
1438
kwargs ['dest' ] = f'{ arg_prefix } { resolved_names [0 ]} '
1436
1439
kwargs ['metavar' ] = self ._metavar_format (field_info .annotation )
1437
- kwargs ['required' ] = self .cli_enforce_required and field_info .is_required ()
1440
+ kwargs ['required' ] = (
1441
+ self .cli_enforce_required and field_info .is_required () and model_default is PydanticUndefined
1442
+ )
1438
1443
if kwargs ['dest' ] in added_args :
1439
1444
continue
1440
1445
if _annotation_contains_types (
@@ -1462,8 +1467,10 @@ def _add_parser_args(
1462
1467
arg_flag ,
1463
1468
arg_names ,
1464
1469
kwargs ,
1470
+ field_name ,
1465
1471
field_info ,
1466
1472
resolved_names ,
1473
+ model_default = model_default ,
1467
1474
)
1468
1475
elif is_alias_path_only :
1469
1476
continue
@@ -1502,19 +1509,33 @@ def _add_parser_submodels(
1502
1509
arg_flag : str ,
1503
1510
arg_names : list [str ],
1504
1511
kwargs : dict [str , Any ],
1512
+ field_name : str ,
1505
1513
field_info : FieldInfo ,
1506
1514
resolved_names : tuple [str , ...],
1515
+ model_default : Any ,
1507
1516
) -> None :
1508
1517
model_group : Any = None
1509
1518
model_group_kwargs : dict [str , Any ] = {}
1510
1519
model_group_kwargs ['title' ] = f'{ arg_names [0 ]} options'
1511
- model_group_kwargs ['description' ] = (
1512
- None
1513
- if sub_models [0 ].__doc__ is None
1514
- else dedent (sub_models [0 ].__doc__ )
1515
- if self .cli_use_class_docs_for_groups and len (sub_models ) == 1
1516
- else field_info .description
1517
- )
1520
+ model_group_kwargs ['description' ] = field_info .description
1521
+ if self .cli_use_class_docs_for_groups and len (sub_models ) == 1 :
1522
+ model_group_kwargs ['description' ] = None if sub_models [0 ].__doc__ is None else dedent (sub_models [0 ].__doc__ )
1523
+
1524
+ if model_default not in (PydanticUndefined , None ):
1525
+ if is_model_class (type (model_default )) or is_pydantic_dataclass (type (model_default )):
1526
+ model_default = getattr (model_default , field_name )
1527
+ else :
1528
+ if field_info .default is not PydanticUndefined :
1529
+ model_default = field_info .default
1530
+ elif field_info .default_factory is not None :
1531
+ model_default = field_info .default_factory
1532
+ if model_default is None :
1533
+ desc_header = f'default: { self .cli_parse_none_str } (undefined)'
1534
+ if model_group_kwargs ['description' ] is not None :
1535
+ model_group_kwargs ['description' ] = dedent (f'{ desc_header } \n { model_group_kwargs ["description" ]} ' )
1536
+ else :
1537
+ model_group_kwargs ['description' ] = desc_header
1538
+
1518
1539
if not self .cli_avoid_json :
1519
1540
added_args .append (arg_names [0 ])
1520
1541
kwargs ['help' ] = f'set { arg_names [0 ]} from JSON string'
@@ -1529,6 +1550,7 @@ def _add_parser_submodels(
1529
1550
subcommand_prefix = subcommand_prefix ,
1530
1551
group = model_group if model_group else model_group_kwargs ,
1531
1552
alias_prefixes = [f'{ arg_prefix } { name } .' for name in resolved_names [1 :]],
1553
+ model_default = model_default ,
1532
1554
)
1533
1555
1534
1556
def _add_parser_alias_paths (
@@ -1618,14 +1640,19 @@ def _metavar_format_recurse(self, obj: Any) -> str:
1618
1640
def _metavar_format (self , obj : Any ) -> str :
1619
1641
return self ._metavar_format_recurse (obj ).replace (', ' , ',' )
1620
1642
1621
- def _help_format (self , field_info : FieldInfo ) -> str :
1643
+ def _help_format (self , field_name : str , field_info : FieldInfo , model_default : Any ) -> str :
1622
1644
_help = field_info .description if field_info .description else ''
1623
- if field_info .is_required ():
1645
+ if field_info .is_required () and model_default in ( PydanticUndefined , None ) :
1624
1646
if _CliPositionalArg not in field_info .metadata :
1625
- _help += ' (required)' if _help else '(required)'
1647
+ ifdef = 'ifdef: ' if model_default is None else ''
1648
+ _help += f' ({ ifdef } required)' if _help else f'({ ifdef } required)'
1626
1649
else :
1627
1650
default = f'(default: { self .cli_parse_none_str } )'
1628
- if field_info .default not in (PydanticUndefined , None ):
1651
+ if is_model_class (type (model_default )) or is_pydantic_dataclass (type (model_default )):
1652
+ default = f'(default: { getattr (model_default , field_name )} )'
1653
+ elif model_default not in (PydanticUndefined , None ) and callable (model_default ):
1654
+ default = f'(default factory: { self ._metavar_format (model_default )} )'
1655
+ elif field_info .default not in (PydanticUndefined , None ):
1629
1656
default = f'(default: { field_info .default } )'
1630
1657
elif field_info .default_factory is not None :
1631
1658
default = f'(default: { field_info .default_factory } )'
0 commit comments