Repository navigation
Boolean argument conversion does not convert all values to bool #5646
Description
Boolean argument conversion currently works like this:
- Integers, floats, strings and
Noneare accepted. Other values cause an error. - Integers, floats and
Noneare passed as-is without conversion. - Strings that are case-insensitively considered true (
True,1,yes, etc. depending on language configuration) yield BooleanTrue. - Strings that are case-insensitively considered false (
False,0,no, ...) yield BooleanFalse. - String
None, case-insensitively, yields actualNoneobject. - Other strings are passed as-is without conversion.
This is rather surprising, because most values are not actually converted to Boolean values but instead passed as-is. Due to integers, floats, None and strings having sane "truth values", this doesn't matter much if arguments are used like if arg:. It is, however, strange that if you have a keyword like def example(arg: bool), the values that are passed in are not guaranteed to have bool type.
Even though the current behavior is rather strange, it works as it does by design. The main motivation was handling cases where the argument type is got based on the default value and where converting all values would have caused problems. For example, even standard libraries had keywords like below before type hints were added in RF 7.4 (#5373):
def should_be_equal(first, second, .., strip_spaces=False, ...):
...
if strip_spaces == "LEADING":
first = first.lstrip()
second = second.lstrip()
elif strip_spaces == "TRAILING":
first = first.rstrip()
second = second.rstrip()
elif strip_spaces:
first = first.strip()
second = second.strip()
...It is likely that there exist keywords like above still today and thus changing the Boolean conversion behavior when the type is got based on the default value is not a good idea. We could, however, change it if the type is specified explicitly like arg: bool. If you actually want to accept strings or None in addition to Boolean values, you can use arg: bool | str or arg: bool | None. If you only want to accept certain strings, using something like arg: bool | Literal["LEADING", "TRAILING"] is very convenient and that's what the standard libraries nowadays use.
My concrete proposal is this:
- If argument type is specified explicitly to be
bool, convert values all accepted values either toTrueorFalse. - If argument type is got based on the default value, preserve the current behavior.
- Consider changing conversion so that in the explicit type case all values are accepted and converted to
bool. This should perhaps get a separate issue.
Making this change is likely to require some changes to how explicitly (e.g. arg: bool) and implicitly (e.g. arg=True) got types are handled. That makes the change a bit bigger, but I expect the resulting code to be simpler than it is nowadays. We probably should implement #4881 as part of that change too.
Activity
pekkaklarck commentedon Apr 1, 2026
This issue is related to #5643 and originally reported as part of #5622.
pekkaklarck commentedon Apr 1, 2026
This change is backwards incompatible even if we change the behavior only in the
arg: boolcase. I believe that is not a problem, at least in a major version, but it nevertheless needs to be documented in the release notes.[-]Boolean argument conversion does not actually convert values to Boolean[/-][+]Boolean argument conversion does not convert all values to Boolean[/+][-]Boolean argument conversion does not convert all values to Boolean[/-][+]Boolean argument conversion does not convert all values to `bool`[/+]feat(pr): embed the regression test's actual source in the PR body