Not sure if this is expected behavior or could be considered a bug, just something I noticed this morning.
Python 3.12.4 | packaged by conda-forge | (main, Jun 17 2024, 10:23:07) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = True
>>> f"{a}"
'True' # as expected
>>> f"{a:5}"
' 1' # huh?
>>> f"{a!s:5}"
'True ' # that's what I wanted!
>>>
It was surprising to me that, when I started to add formatting to my f-string, it converted the bool to an int. Clearly I can get around it for now, but I wasn’t sure if this behavior is expected.
I guess the root cause here is that bool doesn’t have a __format__ method and so it uses the superclass (int) method:
>>> type(a)
<class 'bool'>
>>> type(a).__format__
<method '__format__' of 'int' objects>
bool inherits from int and it’s probably reverting to that in the formatting. TBH, I’m not sure how much of a use case there is for formatting a bool so this might’ve been overlooked unintentionally.
It’s “intended” in the sense that this is the obvious result of the way bool is defined. I suspect that your suspected behavior of behaving more like str by default would be useful, but I fear that it would break a surprising amount of code. But that would have to be checked, and I have no idea how to do that.
I think the surprising thing here is that int.__format__[1] has a special-case to be equivalent to str() when there’s no format string. So even though bool use the int formatting code, it ends up calling PyObject_Str.
Like you, I have no idea if anyone relies on this behavior, but presumably someone does somewhere.
This is not surprising IMO, this is more or less required by the contract for __format__. It should always delicate to __str__ if the format string is empty, for all classes. Note that if this weren’t the case, subclasses of int, str, float, … would never be correctly displayed by either of the "{}" syntax options unless explicitly using !s.
Well not never: only if their string representation is different from the superclass, as in bool. If I subclass int to do something weird but I still want it to display normally, I’d let the normal formatter handle everything.
So the specific thing I find surprising here[1] is that booleans have their own __str__ but not their own __format__.