Skip to content

Commit a61dcee

Browse files
committed
Use default=True as a sentinel for non-boolean flags
Refs #3111
1 parent 5850ef9 commit a61dcee

1 file changed

Lines changed: 24 additions & 5 deletions

File tree

src/click/core.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2891,13 +2891,32 @@ def to_info_dict(self) -> dict[str, t.Any]:
28912891
def get_default(
28922892
self, ctx: Context, call: bool = True
28932893
) -> t.Any | t.Callable[[], t.Any] | None:
2894+
"""For non-boolean flag options, ``default=True`` is treated as a
2895+
sentinel meaning "activate this flag by default" and is resolved to
2896+
:attr:`flag_value`. This resolution is performed lazily here (rather
2897+
than eagerly in :meth:`__init__`) to prevent callable ``flag_value``
2898+
values (like classes) from being instantiated prematurely.
2899+
2900+
For example, with ``--upper/--lower`` feature switches where
2901+
``flag_value="upper"`` and ``default=True``, the default resolves
2902+
to ``"upper"``.
2903+
2904+
.. caution::
2905+
This substitution only applies to **non-boolean** flags
2906+
(:attr:`is_bool_flag` is ``False``). For boolean flags, ``True`` is
2907+
not a sentinel but a legitimate Python value, so ``default=True`` is
2908+
returned as-is. Without this distinction,
2909+
``flag_value=False, default=True`` would silently always return
2910+
``False``, regardless of whether the flag was passed.
2911+
2912+
.. versionchanged:: 8.3
2913+
``default=True`` is no longer substituted with ``flag_value`` for
2914+
boolean flags, fixing negative boolean flags like
2915+
``flag_value=False, default=True``. :issue:`3111`
2916+
"""
28942917
value = super().get_default(ctx, call=False)
28952918

2896-
# Lazily resolve default=True to flag_value. Doing this here
2897-
# (instead of eagerly in __init__) prevents callable flag_values
2898-
# (like classes) from being instantiated by the callable check below.
2899-
# https://github.com/pallets/click/issues/3121
2900-
if value is True and self.is_flag:
2919+
if value is True and self.is_flag and not self.is_bool_flag:
29012920
value = self.flag_value
29022921
elif call and callable(value):
29032922
value = value()

0 commit comments

Comments
 (0)