4141)
4242from pydantic_settings .sources import (
4343 CLI_SUPPRESS ,
44+ CliDualFlag ,
4445 CliExplicitFlag ,
4546 CliImplicitFlag ,
4647 CliMutuallyExclusiveGroup ,
4748 CliPositionalArg ,
4849 CliSettingsSource ,
4950 CliSubCommand ,
5051 CliSuppress ,
52+ CliToggleFlag ,
5153 CliUnknownArgs ,
5254 get_subcommand ,
5355)
@@ -1600,18 +1602,26 @@ class CliFlagNotBool(BaseSettings, cli_parse_args=True):
16001602
16011603 CliFlagNotBool ()
16021604
1605+ with pytest .raises (
1606+ SettingsError , match = 'CliToggleFlag argument CliToggleNoDefault.flag must have a default bool value'
1607+ ):
1608+
1609+ class CliToggleNoDefault (BaseSettings , cli_parse_args = True ):
1610+ flag : CliToggleFlag [bool ]
1611+
1612+ CliToggleNoDefault ()
1613+
16031614
16041615@pytest .mark .parametrize ('enforce_required' , [True , False ])
1605- def test_cli_bool_flags (monkeypatch , enforce_required ):
1606- class ExplicitSettings (BaseSettings , cli_enforce_required = enforce_required ):
1607- explicit_req : bool
1608- explicit_opt : bool = False
1609- implicit_req : CliImplicitFlag [bool ]
1610- implicit_opt : CliImplicitFlag [bool ] = False
1611-
1612- class ImplicitSettings (BaseSettings , cli_implicit_flags = True , cli_enforce_required = enforce_required ):
1616+ @pytest .mark .parametrize ('implicit_flags_mode' , [None , True , 'dual' , 'toggle' ])
1617+ def test_cli_bool_flags (monkeypatch , enforce_required , implicit_flags_mode ):
1618+ class FlagSettings (BaseSettings , cli_implicit_flags = implicit_flags_mode , cli_enforce_required = enforce_required ):
16131619 explicit_req : CliExplicitFlag [bool ]
16141620 explicit_opt : CliExplicitFlag [bool ] = False
1621+ dual_req : CliDualFlag [bool ]
1622+ dual_opt : CliDualFlag [bool ] = False
1623+ toggle_def_t : CliToggleFlag [bool ] = True
1624+ toggle_def_f : CliToggleFlag [bool ] = False
16151625 implicit_req : bool
16161626 implicit_opt : bool = False
16171627
@@ -1620,38 +1630,69 @@ class ImplicitSettings(BaseSettings, cli_implicit_flags=True, cli_enforce_requir
16201630 'explicit_opt' : False ,
16211631 'implicit_req' : True ,
16221632 'implicit_opt' : False ,
1633+ 'dual_req' : True ,
1634+ 'dual_opt' : False ,
1635+ 'toggle_def_t' : True ,
1636+ 'toggle_def_f' : False ,
16231637 }
1624-
1625- explicit_settings = CliApp .run (ExplicitSettings , cli_args = ['--explicit_req=True' , '--implicit_req' ])
1626- assert explicit_settings .model_dump () == expected
1627- serialized_args = CliApp .serialize (explicit_settings )
1628- assert serialized_args == ['--explicit_req' , 'True' , '--implicit_req' ]
1629- assert CliApp .run (ExplicitSettings , cli_args = serialized_args ).model_dump () == expected
1630-
1631- implicit_settings = CliApp .run (ImplicitSettings , cli_args = ['--explicit_req=True' , '--implicit_req' ])
1638+ flag_args = [
1639+ '--explicit_req' ,
1640+ 'True' ,
1641+ '--dual_req' ,
1642+ ]
1643+ flag_args += ['--implicit_req' ] if implicit_flags_mode is not None else ['--implicit_req' , 'True' ]
1644+ implicit_settings = CliApp .run (FlagSettings , cli_args = flag_args )
16321645 assert implicit_settings .model_dump () == expected
16331646 serialized_args = CliApp .serialize (implicit_settings )
1634- assert serialized_args == ['--explicit_req' , 'True' , '--implicit_req' ]
1635- assert CliApp .run (ImplicitSettings , cli_args = serialized_args ).model_dump () == expected
1647+ assert serialized_args == flag_args
16361648
16371649 expected = {
16381650 'explicit_req' : False ,
16391651 'explicit_opt' : False ,
16401652 'implicit_req' : False ,
16411653 'implicit_opt' : False ,
1654+ 'dual_req' : False ,
1655+ 'dual_opt' : False ,
1656+ 'toggle_def_t' : False ,
1657+ 'toggle_def_f' : False ,
16421658 }
1659+ flag_args = [
1660+ '--explicit_req' ,
1661+ 'False' ,
1662+ '--no-dual_req' ,
1663+ '--no-toggle_def_t' ,
1664+ ]
1665+ flag_args += ['--no-implicit_req' ] if implicit_flags_mode is not None else ['--implicit_req' , 'False' ]
1666+ implicit_settings = CliApp .run (FlagSettings , cli_args = flag_args )
1667+ assert implicit_settings .model_dump () == expected
1668+ serialized_args = CliApp .serialize (implicit_settings )
1669+ assert serialized_args == flag_args
16431670
1644- explicit_settings = CliApp .run (ExplicitSettings , cli_args = ['--explicit_req=False' , '--no-implicit_req' ])
1645- assert explicit_settings .model_dump () == expected
1646- serialized_args = CliApp .serialize (explicit_settings )
1647- assert serialized_args == ['--explicit_req' , 'False' , '--no-implicit_req' ]
1648- assert CliApp .run (ExplicitSettings , cli_args = serialized_args ).model_dump () == expected
1649-
1650- implicit_settings = CliApp .run (ImplicitSettings , cli_args = ['--explicit_req=False' , '--no-implicit_req' ])
1671+ expected = {
1672+ 'explicit_req' : True ,
1673+ 'explicit_opt' : True ,
1674+ 'implicit_req' : True ,
1675+ 'implicit_opt' : True ,
1676+ 'dual_req' : True ,
1677+ 'dual_opt' : True ,
1678+ 'toggle_def_t' : True ,
1679+ 'toggle_def_f' : True ,
1680+ }
1681+ flag_args = [
1682+ '--explicit_req' ,
1683+ 'True' ,
1684+ '--explicit_opt' ,
1685+ 'True' ,
1686+ '--dual_req' ,
1687+ '--dual_opt' ,
1688+ '--toggle_def_f' ,
1689+ ]
1690+ flag_args += ['--implicit_req' ] if implicit_flags_mode is not None else ['--implicit_req' , 'True' ]
1691+ flag_args += ['--implicit_opt' ] if implicit_flags_mode is not None else ['--implicit_opt' , 'True' ]
1692+ implicit_settings = CliApp .run (FlagSettings , cli_args = flag_args )
16511693 assert implicit_settings .model_dump () == expected
16521694 serialized_args = CliApp .serialize (implicit_settings )
1653- assert serialized_args == ['--explicit_req' , 'False' , '--no-implicit_req' ]
1654- assert CliApp .run (ImplicitSettings , cli_args = serialized_args ).model_dump () == expected
1695+ assert serialized_args == flag_args
16551696
16561697
16571698def test_cli_avoid_json (capsys , monkeypatch ):
@@ -2706,17 +2747,30 @@ class SettingsAll(BaseSettings):
27062747def test_cli_kebab_case_all_with_implicit_flag ():
27072748 class Settings (BaseSettings ):
27082749 model_config = SettingsConfigDict (cli_kebab_case = 'all' )
2709- test_bool_flag : CliImplicitFlag [bool ]
2750+ test_bool_flag_a : CliImplicitFlag [bool ]
2751+ test_bool_flag_b : CliToggleFlag [bool ] = True
2752+ test_bool_flag_c : CliToggleFlag [bool ] = False
2753+ test_bool_flag_d : CliDualFlag [bool ] = False
27102754
27112755 assert CliApp .run (
27122756 Settings ,
2713- cli_args = ['--test-bool-flag' ],
2714- ).model_dump () == {'test_bool_flag' : True }
2757+ cli_args = ['--test-bool-flag-a' , '--test-bool-flag-c' , '--test-bool-flag-d' ],
2758+ ).model_dump () == {
2759+ 'test_bool_flag_a' : True ,
2760+ 'test_bool_flag_b' : True ,
2761+ 'test_bool_flag_c' : True ,
2762+ 'test_bool_flag_d' : True ,
2763+ }
27152764
27162765 assert CliApp .run (
27172766 Settings ,
2718- cli_args = ['--no-test-bool-flag' ],
2719- ).model_dump () == {'test_bool_flag' : False }
2767+ cli_args = ['--no-test-bool-flag-a' , '--no-test-bool-flag-b' , '--no-test-bool-flag-d' ],
2768+ ).model_dump () == {
2769+ 'test_bool_flag_a' : False ,
2770+ 'test_bool_flag_b' : False ,
2771+ 'test_bool_flag_c' : False ,
2772+ 'test_bool_flag_d' : False ,
2773+ }
27202774
27212775
27222776def test_cli_with_unbalanced_brackets_in_json_string ():
0 commit comments