Skip to content

Commit a90398b

Browse files
committed
Docs.
1 parent d0ec5e0 commit a90398b

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

docs/index.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,71 @@ class ImplicitSettings(BaseSettings, cli_parse_args=True, cli_implicit_flags=Tru
13311331
"""
13321332
```
13331333

1334+
Implicit flag behavior can be further refined using the "toggle" or "dual" mode settings. Similarly, the provided
1335+
`CliToggleFlag` and `CliDualFlag` annotations can be used for more granular control.
1336+
1337+
For "toggle" flags, if default=`False`, `--flag` will store `True`. Otherwise, if default=`True`, `--no-flag` will store
1338+
`False`.
1339+
1340+
```py
1341+
from pydantic_settings import BaseSettings, CliDualFlag, CliToggleFlag
1342+
1343+
1344+
class ImplicitDualSettings(
1345+
BaseSettings, cli_parse_args=True, cli_implicit_flags='dual'
1346+
):
1347+
"""With cli_implicit_flags='dual', implicit flags are dual by default."""
1348+
1349+
implicit_req: bool
1350+
"""
1351+
--implicit_req, --no-implicit_req (required)
1352+
"""
1353+
1354+
implicit_dual_opt: bool = False
1355+
"""
1356+
--implicit_dual_opt, --no-implicit_dual_opt (default: False)
1357+
"""
1358+
1359+
# Implicit flags are dual by default, so must override toggle flags with annotation
1360+
flag_a: CliToggleFlag[bool] = False
1361+
"""
1362+
--flag_a (default: False)
1363+
"""
1364+
1365+
# Implicit flags are dual by default, so must override toggle flags with annotation
1366+
flag_b: CliToggleFlag[bool] = True
1367+
"""
1368+
--no-flag_b (default: True)
1369+
"""
1370+
1371+
1372+
class ImplicitToggleSettings(
1373+
BaseSettings, cli_parse_args=True, cli_implicit_flags='toggle'
1374+
):
1375+
"""With cli_implicit_flags='toggle', implicit flags are toggle by default."""
1376+
1377+
implicit_req: bool
1378+
"""
1379+
--implicit_req, --no-implicit_req (required)
1380+
"""
1381+
1382+
# Implicit flags are toggle by default, so must override dual flags with annotation
1383+
implicit_dual_opt: CliDualFlag[bool] = False
1384+
"""
1385+
--implicit_dual_opt, --no-implicit_dual_opt (default: False)
1386+
"""
1387+
1388+
flag_a: bool = False
1389+
"""
1390+
--flag_a (default: False)
1391+
"""
1392+
1393+
flag_b: bool = True
1394+
"""
1395+
--no-flag_b (default: True)
1396+
"""
1397+
```
1398+
13341399
#### Ignore and Retrieve Unknown Arguments
13351400

13361401
Change whether to ignore unknown CLI arguments and only parse known ones using `cli_ignore_unknown_args`. By default, the CLI

pydantic_settings/main.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class SettingsConfigDict(ConfigDict, total=False):
6161
cli_exit_on_error: bool
6262
cli_prefix: str
6363
cli_flag_prefix_char: str
64-
cli_implicit_flags: bool | None
64+
cli_implicit_flags: bool | Literal['dual', 'toggle'] | None
6565
cli_ignore_unknown_args: bool | None
6666
cli_kebab_case: bool | Literal['all', 'no_enums'] | None
6767
cli_shortcuts: Mapping[str, str | list[str]] | None
@@ -153,8 +153,12 @@ class BaseSettings(BaseModel):
153153
Defaults to `True`.
154154
_cli_prefix: The root parser command line arguments prefix. Defaults to "".
155155
_cli_flag_prefix_char: The flag prefix character to use for CLI optional arguments. Defaults to '-'.
156-
_cli_implicit_flags: Whether `bool` fields should be implicitly converted into CLI boolean flags.
157-
(e.g. --flag, --no-flag). Defaults to `False`.
156+
_cli_implicit_flags: Controls how `bool` fields are exposed as CLI flags.
157+
158+
- False (default): no implicit flags are generated; booleans must be set explicitly (e.g. --flag=true).
159+
- True / 'dual': optional boolean fields generate both positive and negative forms (--flag and --no-flag).
160+
- 'toggle': required boolean fields remain in 'dual' mode, while optional boolean fields generate a single
161+
flag aligned with the default value (if default=False, expose --flag; if default=True, expose --no-flag).
158162
_cli_ignore_unknown_args: Whether to ignore unknown CLI args and parse only known ones. Defaults to `False`.
159163
_cli_kebab_case: CLI args use kebab case. Defaults to `False`.
160164
_cli_shortcuts: Mapping of target field name to alias names. Defaults to `None`.
@@ -184,7 +188,7 @@ def __init__(
184188
_cli_exit_on_error: bool | None = None,
185189
_cli_prefix: str | None = None,
186190
_cli_flag_prefix_char: str | None = None,
187-
_cli_implicit_flags: bool | None = None,
191+
_cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
188192
_cli_ignore_unknown_args: bool | None = None,
189193
_cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
190194
_cli_shortcuts: Mapping[str, str | list[str]] | None = None,
@@ -271,7 +275,7 @@ def _settings_build_values(
271275
_cli_exit_on_error: bool | None = None,
272276
_cli_prefix: str | None = None,
273277
_cli_flag_prefix_char: str | None = None,
274-
_cli_implicit_flags: bool | None = None,
278+
_cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
275279
_cli_ignore_unknown_args: bool | None = None,
276280
_cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
277281
_cli_shortcuts: Mapping[str, str | list[str]] | None = None,

pydantic_settings/sources/providers/cli.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,12 @@ class CliSettingsSource(EnvSettingsSource, Generic[T]):
274274
Defaults to `True`.
275275
cli_prefix: Prefix for command line arguments added under the root parser. Defaults to "".
276276
cli_flag_prefix_char: The flag prefix character to use for CLI optional arguments. Defaults to '-'.
277-
cli_implicit_flags: Whether `bool` fields should be implicitly converted into CLI boolean flags.
278-
(e.g. --flag, --no-flag). Defaults to `False`.
277+
cli_implicit_flags: Controls how `bool` fields are exposed as CLI flags.
278+
279+
- False (default): no implicit flags are generated; booleans must be set explicitly (e.g. --flag=true).
280+
- True / 'dual': optional boolean fields generate both positive and negative forms (--flag and --no-flag).
281+
- 'toggle': required boolean fields remain in 'dual' mode, while optional boolean fields generate a single
282+
flag aligned with the default value (if default=False, expose --flag; if default=True, expose --no-flag).
279283
cli_ignore_unknown_args: Whether to ignore unknown CLI args and parse only known ones. Defaults to `False`.
280284
cli_kebab_case: CLI args use kebab case. Defaults to `False`.
281285
cli_shortcuts: Mapping of target field name to alias names. Defaults to `None`.
@@ -307,7 +311,7 @@ def __init__(
307311
cli_exit_on_error: bool | None = None,
308312
cli_prefix: str | None = None,
309313
cli_flag_prefix_char: str | None = None,
310-
cli_implicit_flags: bool | None = None,
314+
cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
311315
cli_ignore_unknown_args: bool | None = None,
312316
cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
313317
cli_shortcuts: Mapping[str, str | list[str]] | None = None,
@@ -1011,7 +1015,9 @@ def _add_parser_args(
10111015
if isinstance(group, dict):
10121016
group = self._add_group(parser, **group)
10131017
context = parser if group is None else group
1014-
arg.args = [f'{flag_prefix[: len(name)]}{name}' for name in arg_names]
1018+
if arg.kwargs.get('action') == 'store_false':
1019+
flag_prefix += 'no-'
1020+
arg.args = [f'{flag_prefix[: 1 if len(name) == 1 else None]}{name}' for name in arg_names]
10151021
self._add_argument(context, *arg.args, **arg.kwargs)
10161022
added_args += list(arg_names)
10171023

@@ -1366,7 +1372,11 @@ def _serialized_args(self, model: PydanticModel, _is_submodel: bool = False) ->
13661372
continue
13671373

13681374
# Note: prepend 'no-' for boolean optional action flag if model_default value is False and flag is not a short option
1369-
if arg.kwargs.get('action') == BooleanOptionalAction and model_default is False and flag_chars == '--':
1375+
if (
1376+
arg.kwargs.get('action') in (BooleanOptionalAction, 'store_false')
1377+
and model_default is False
1378+
and flag_chars == '--'
1379+
):
13701380
flag_chars += 'no-'
13711381

13721382
optional_args.append(f'{flag_chars}{arg_name}')

0 commit comments

Comments
 (0)