[Python-ideas] Escaped braces in format specifiers
Ken Kundert
python-ideas at shalmirane.com
Sat May 19 13:49:17 EDT 2018
On Sat, May 19, 2018 at 07:06:41AM -0400, Eric V. Smith wrote:
> On 5/19/2018 1:56 AM, Ken Kundert wrote:
> >On Tue, May 15, 2018 at 04:23:26PM -0400, Eric V. Smith wrote:
> >>I'm busy at the sprints, so I don't have a lot of time to think about this.
> >>
> >>However, let me just say that recursive format specs are supported,
> >>to a depth of 1.
> >>
> >>>>>width=10
> >>>>>f'{"test":{width}}'
> >>'test'
> >>
> >>So first the string is basically expanded to:
> >>f'{"test":10}'
> >>Then the string is formatted again to produce the final result.
> >>
> >>That is why the braces must match: they're being used for recursive
> >>format specs. There's no mechanism for having braces that aren't
> >>inspected by the f-string machinery.
> >>
> >>Eric
> >
> >
> >Eric,
> > You say that recursive format specs are supported to a depth of 1, but it
> >does not seem like true recursion to me. If it were true recursion, wouldn't the
> >escaped braces be interpreted properly inside the format spec, just as they are
> >in the literal portion of the f-string?
> >
> >Is there some reason why escaped braces are not supported in the format specs?
> >
> >Consider the following example:
> >
> > >>> class myclass:
> > ... def __format__(self, fmt_spec):
> > ... return 'fmt_spec: ' + fmt_spec
> >
> > >>> d = myclass()
> > >>> x = 3
> >
> > >>> print('1: ext={{x}} {d:int={{x}}}'.format(d=d))
> > 1: ext={x} fmt_spec: int={x}
> >
> > >>> print(f'2: ext={{x}} {d:int={{x}}}')
> > 2: ext={x} fmt_spec: int={3}
> >
> > >>> print(f'3: ext={set([x])} {d:int={set([x])}}')
> > 3: ext={3} fmt_spec: int={3}
> >
> >In each case, the same substring is found in the literal portion (ext) of the
> >format string and in the format spec (int).
> >
> >In case 1, the format string is passed to a format method and the substring is
> >{{x}}. In both cases, the double braces are interpreted as escaped braces, and
> >both are converted to '{x}' in the final printed result.
> >
> >In case 2, the format string is an f string. The substring is still {{x}}. The
> >first instance of {{x}} is found in the literal portion of the format string and
> >the double braces are interpreted as escaped braces. It expands to {x} like in
> >the first case. However, the second instance is found in the format spec, and
> >this case the outer braces are stripped off and what is left over is treated as
> >a Python expression. Here {x} is treated as a set literal containing one value,
> >x. Since x is 3, the value of int is {3}.
> >
> >Case 3 confirms the interpretation of case 2 by simply replacing {x} with an
> >alternate way of specifying a set literal that contains a single value. In this
> >case there are no double braces and so both instances are treated the same, both
> >expand to {3}.
> >
> >So the format method supports escaped braces in both the literal part of the
> >format string and the format spec. f-strings supports escaped braces in the
> >literal part of the format string, but in the format spec the outer level of
> >braces are stripped off and what remains is interpreted as a Python expression.
> >
> >Is there some benefit to this difference?
>
> Is the difference you're seeing just in how f-strings and
> str.format() differ in evaluating expressions? str.format() isn't
> evaluating "int={x}", which makes sense to me. It never evaluates
> expressions, by design, it just treats strings as literals. If it
> did evaluate expressions, there's too great a chance of code being
> evaluated from user-defined strings.
>
> Eric
Eric,
My example does highlight the fact that f-strings do evaluate the embedded
expressions while str.format() does not, but that is not the point I was trying
to make.
The substring 'int={{x}}' has two possible interpretations, it is either
a string that contains escaped braces and when the escaping is processed becomes
'int={x}', or the outer braces signal an embedded expression, the expression
being {x}, a set literal, and when interpolated the substring becomes 'int={3}'.
My point was that str.format() uses the first interpretation for both ext and
int, but f-strings use the first interpretation for ext and the second
interpretation for int.
Using the second interpretation for int in the f-string seems inconsistent.
Specifically, int and ext are interpreted differently in the f-string, and
int is interpreted differently by str.format() and the f-string.
This difference does not seem to provide any benefit, so why does it exist?
In other words, why aren't the escaped braces supported in the format_spec of
f-strings?
-Ken
More information about the Python-ideas
mailing list