[Python-ideas] Escaped braces in format specifiers

Eric V. Smith eric at trueblade.com
Sat May 19 07:06:41 EDT 2018


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


More information about the Python-ideas mailing list