3.1 and 2.7 break format() when used with complex (sometimes)

This code works on 2.6 and 3.0:
format(1+1j, '10s') '(1+1j) '
That's because format ends up calling object.__format__ because complex doesn't have its own __format__. Then object.__format__ calls str(self) which returns '(1+1j) '. So the original call basically turns into "format('(1+1j) ', '10s')". In 3.1 (released) and 2.7 (not yet released) I implemented __format__ on complex. So now that same code is an error:
That's because complex._format__ doesn't recognize string formatting codes, in particular 's'. There's a general problem that types that sprout __format__ will break existing usages of format() that use some string formatting codes, unless the types recognize string formats in addition to their own. I think we should change the documentation of format() to warn that you should really call str() on the first argument if you're relying on the second argument being a string formatting code. But what to do about 3.1 and 2.7 with respect to complex? I see 2 options: 1. Leave it as-is, such that 3.1 and 2.7 might break some uses of format(complex, str). 2. Modify format to understand 's' and do the conversion itself. But we don't do this for int and float, that's why we added '!s'. I'm sort of leaning toward #1, but I'd like to know if anyone has an opinion. I haven't heard of anyone complaining about this yet; it would only have tripped up people moving from 3.0 -> 3.1, or from 2.6 -> 3.1 who used format (or str.format()) while specifying 's' or some other str-specific format codes. Eric.

Eric Smith wrote:
Guido pointed out this should have been: """That's because format ends up calling object.__format__ because complex doesn't have its own __format__. Then object.__format__ calls str(self) which returns '(1+1j)'. So the original call basically turns into "format('(1+1j)', '10s')".""" (I had inserted the spaces added by str.__format__ too early.) We discussed this at the sprint. We agreed that we'd just allow this specific issue with complex formatting to possibly break existing uses in 2.7, as it did in 3.1. While that's unfortunate, it's better than the alternatives. The root cause of this problem is object.__format__, which is basically: def __format__(self, fmt): return str(self).__format__(fmt) So here we're changing the type of the object (to str) but still keeping the same format string. That doesn't make any sense: the format string is type specific. I think the correct thing to do here is to make it an error if fmt is non-empty. In 2.7 and 3.2 I can make this a PendingDeprecationWarning, then in 3.3 a DeprecationWarning, and finally make it an error in 3.4. Eric.

Eric Smith wrote:
Guido pointed out this should have been: """That's because format ends up calling object.__format__ because complex doesn't have its own __format__. Then object.__format__ calls str(self) which returns '(1+1j)'. So the original call basically turns into "format('(1+1j)', '10s')".""" (I had inserted the spaces added by str.__format__ too early.) We discussed this at the sprint. We agreed that we'd just allow this specific issue with complex formatting to possibly break existing uses in 2.7, as it did in 3.1. While that's unfortunate, it's better than the alternatives. The root cause of this problem is object.__format__, which is basically: def __format__(self, fmt): return str(self).__format__(fmt) So here we're changing the type of the object (to str) but still keeping the same format string. That doesn't make any sense: the format string is type specific. I think the correct thing to do here is to make it an error if fmt is non-empty. In 2.7 and 3.2 I can make this a PendingDeprecationWarning, then in 3.3 a DeprecationWarning, and finally make it an error in 3.4. Eric.
participants (1)
-
Eric Smith