Hi,
I propose adding !R conversion format to f-string to format error messages and quote properly str subclasses. Example:
class StrSubclass(str):
def __repr__(self):
return '<override>'
abc = StrSubclass('abc')
print(f'{abc!R}')
The output is 'abc', whereas f'{abc!r}' (current !r conversion) returns <override>.
The use case is to replace f"no attribute '{attr}'" with f"no attribute {attr!r}" to handle correctly attr which contains quote characters (' and ").
Do you think that it would be a good idea? Or do you think that it’s overkill to handle str subclasses differently?
Well, an alternative is to not bother with quotes and leave the current code as it is
So for example, continue using '%s' in error messages to format an attribute name.
Problem
While reviewing a PR changing AttributeError, I noticed that strings including a quote character are not formatted properly. Example:
>>> getattr(object(), 'abc')
AttributeError: 'object' object has no attribute 'abc'
>>> getattr(object(), "a'c") # the attribute name contains a quote
AttributeError: 'object' object has no attribute 'a'c'
The ouput string 'a'c' didn’t escape the quote character correctly.
The AttributeError error message is created (in C) using "'%s' object has no attribute '%s'" % (type_name, attr_name). I would prefer to format the attribute name using repr(). Something like:
>>> type_name = '<object>'; attr_name = "a'b"
>>> print("'%s' object has no attribute %r" % (type_name, attr_name))
'<object>' object has no attribute "a'b"
The attribute name is now formatted using double quote ("): "a'b". The new problem is that str subclasses can override __repr__() which isn’t formatted as expected:
class MyStr(str):
def __repr__(self):
return '<custom repr>'
print('object has no attribute %r' % MyStr('abc'))
Output:
AttributeError: 'object' object has no attribute <custom repr>
The error now contains <custom repr> instead of 'abc'. The problem is that getattr() tries to get the object.abc attribute, so the error message is not correct.
Proposed solution
A solution for this problem would be to add !R conversion to f-string and str.format(): similar to !r which calls repr(), but don’t call __repr__() on str subclasses.
In the C API, add also %#R format to PyUnicode_FromFormat(): similar to %R (call repr()), but don’t call __repr__() on str subclasses.