You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Binary flags vs default=None: semantics and issue with prompt #1992
if default is not provided, the option default is set to False
if default is set to None, default is set to None.
I think this may be accidental behavior but it's not senseless: settings default=None you can force the prompt to require an explicit value to be provided (it'll be [y/n] rather than [y/N]).
Nonetheless, I noticed that if you provide default=None to a flag not having a secondary option, the prompt message is broken: it won't include "[y/n]" at the end.
forbid default=None for boolean flags; required=True would have no effect, since boolean flags would always have a default value.
allow for default=None; fix the already described prompt issue.
Let's explore different cases with default=None:
prompt=False and required=False -- this means that the option has 3 possible states (None, False, True): in my opinion, such an option shouldn't be a boolean flag in the first place, it should be a ternary click.Choice
prompt=True -- asking the value of a flag with a prompt is more reasonable but default=None has little to no relevance here: the only difference is that setting default=None the prompt won't have a default value
required=True -- personally, I'd find weird a CLI with a required boolean flag (and no prompt for it) as I perceive boolean flags as the epitome of something optional; I'm aware this is not a real argument but I'm open to read about some real or hypothetical examples where this makes sense.
All this considered, I currently lean towards option 1 (but not strongly).
Additional note: allowing for default=None means accepting the fact that not providing default versus setting default=None have different semantics for boolean flags. Currently, we can rely on the presence/absence of the keyword default in the kwargs provided to option() but if the intention is that of removing **kwargs from signatures, the default argument will need to be set to a special MISSING value to distinguish it from None.
Didn't expect this to be related, but as part of rewriting the parser in #2205, I've found that the interaction between boolean flags, default, and flag_value is caught up in how the existing parser was constructed. I started untangling it and realized there was this open issue about the same thing.
prompt=False and required=False -- this means that the option has 3 possible states (None, False, True): in my opinion, such an option shouldn't be a boolean flag in the first place, it should be a ternary click.Choice
prompt=True -- asking the value of a flag with a prompt is more reasonable but default=None has little to no relevance here: the only difference is that setting default=None the prompt won't have a default value
required=True -- personally, I'd find weird a CLI with a required boolean flag (and no prompt for it) as I perceive boolean flags as the epitome of something optional; I'm aware this is not a real argument but I'm open to read about some real or hypothetical examples where this makes sense.
Additional note: allowing for default=None means accepting the fact that not providing default versus setting default=None have different semantics for boolean flags. Currently, we can rely on the presence/absence of the keyword default in the kwargs provided to option() but if the intention is that of removing **kwargs from signatures, the default argument will need to be set to a special MISSING value to distinguish it from None.
Awesome work on the PR, @kdeldycke! I had a look at the tests and they all makes sense to me. To be nitpicky, I'd prefer a stricter behavior in the following case:
# Random string default is treated as a truthy value, so it prompts with [Y/n].
({"prompt": True, "default": "foo"}, [], "[Y/n]", "", True),
({"prompt": True, "default": "foo"}, [], "[Y/n]", "y", True),
({"prompt": True, "default": "foo"}, [], "[Y/n]", "n", False),
i.e. raising a TypeError for any default value that is not bool | None, but haven't looked at the code and I presume there might be backward compatibility concerns as well. I'm totally fine with current behavior anyway. Great work again!
I like your proposal on being a bit stricter in general to stir people into using good and explicit values. Unless the behavior is well documented, especially for auto-magic detections.
But yeah, you guessed right, Click internals needs some refactors and we can't really be aggressive on that side. See how we had to yank the latest release because of all the unaddressed edge-cases. But with time we'll manage to do it.
Activity
janluke commentedon Aug 4, 2021
This issue needs some discussion. Related issue:
required=Truedoes not work for flags #1978.I considered two options:
default=Nonefor boolean flags;required=Truewould have no effect, since boolean flags would always have a default value.default=None; fix the already described prompt issue.Let's explore different cases with
default=None:prompt=False and required=False-- this means that the option has 3 possible states (None,False,True): in my opinion, such an option shouldn't be a boolean flag in the first place, it should be a ternaryclick.Choiceprompt=True-- asking the value of a flag with a prompt is more reasonable butdefault=Nonehas little to no relevance here: the only difference is that settingdefault=Nonethe prompt won't have a default valuerequired=True-- personally, I'd find weird a CLI with a required boolean flag (and no prompt for it) as I perceive boolean flags as the epitome of something optional; I'm aware this is not a real argument but I'm open to read about some real or hypothetical examples where this makes sense.All this considered, I currently lean towards option 1 (but not strongly).
Additional note: allowing for
default=Nonemeans accepting the fact that not providingdefaultversus settingdefault=Nonehave different semantics for boolean flags. Currently, we can rely on the presence/absence of the keyworddefaultin thekwargsprovided tooption()but if the intention is that of removing**kwargsfrom signatures, thedefaultargument will need to be set to a specialMISSINGvalue to distinguish it fromNone.EDIT: added the additional note.
davidism commentedon Mar 5, 2022
Didn't expect this to be related, but as part of rewriting the parser in #2205, I've found that the interaction between boolean flags,
default, andflag_valueis caught up in how the existing parser was constructed. I started untangling it and realized there was this open issue about the same thing.davidism commentedon Jul 1, 2025
@kdeldycke was this addressed by #2956?
kdeldycke commentedon Aug 9, 2025
This is now supported by #3030.
I covered all these cases in #3030.
These unittests should demonstrate the new (fixed) behavior: https://github.com/pallets/click/pull/3030/files#diff-e9b43a3662176468b449eee04c777e29d67212bfc0dc0ca42a03b32d970b0deaR491-R800
I also introduced a new
UNSETsentinel to differentiate the absence of value from the value of absence: https://github.com/pallets/click/pull/3030/files#diff-0140337741a334b642a5170e3bd7c408eea4bb4394df9226cd033fae8f5c0a19R33-R48@janluke Unless you spot something weird, I'll consider this issue addressed by #3030.
janluke commentedon Aug 27, 2025
Awesome work on the PR, @kdeldycke! I had a look at the tests and they all makes sense to me. To be nitpicky, I'd prefer a stricter behavior in the following case:
i.e. raising a
TypeErrorfor any default value that is notbool | None, but haven't looked at the code and I presume there might be backward compatibility concerns as well. I'm totally fine with current behavior anyway. Great work again!kdeldycke commentedon Aug 28, 2025
Thanks @janluke for double-checking my cases!
I like your proposal on being a bit stricter in general to stir people into using good and explicit values. Unless the behavior is well documented, especially for auto-magic detections.
But yeah, you guessed right, Click internals needs some refactors and we can't really be aggressive on that side. See how we had to yank the latest release because of all the unaddressed edge-cases. But with time we'll manage to do it.
kdeldycke commentedon Aug 28, 2025
Oh and I keep a note on your proposal. Maybe I'll be able to produce to be discussed with the community when things are more relaxed.