f-string anomaly

Ken Kundert theNurd at nurdletech.com
Mon May 14 01:08:08 EDT 2018


Terry,
    Thanks for your response.

I apologize about the double posting. I am well aware how doing so is
bad form. My double posting was unintentional; it occurred when my news
reader misbehaved.

What I did in my code was to put double braces inside the format_spec,
which the syntax specification in the f-string documentation does not
allow, and so it should be a syntax error. But it does not generate a
syntax error. So the question becomes, how does it interpret {{k}} and
{{v}} in the format spec. I was expecting it to treat the double braces
as escaped braces, and that is what the format method does. And by the
last sentence in the following paragraph, it seems like f-strings should
as well.

    Top-level format specifiers may include nested replacement fields.
    These nested fields may include their own conversion fields and
    format specifiers, but may not include more deeply-nested
    replacement fields. The format specifier mini-language is the same
    as that used by the string .format() method.

> All names in the expression are resolved in the local namespace of the
> f string.  There are other differences.  Nesting can only be one level
> deep.

I tried adding k and v to the local namespace:

    ...
    k = 6
    v = 9
    print(f'Email: {d:{{k}} {{v}}}')

I still got:

    NameError: name 'k' is not defined

I don't know where it is looking for k, but it is not in the local
namespace.

And I don't think the comment on nesting applies. The format string does
not include nested references. Rather "{{k}} {{v}}" is meant to be
treated as a literal string that should de-escaped and passed to the
__format__ method, but no values should be interpolated. What the
__format__ method does with the format spec is not in anyway restricted
by the f string as long as __format__ returns a string.

The comment that the mini-language is the same between the format method
and the f string seems compelling to me. I think this is a bug. I think
the f-string syntax should be updated to explicitly allow escaped braces
in the format_spec, and the implementation of f-strings should be
updated to be consistent with the syntax specification, and with the
format method.

-Ken



On 05/13/2018 07:08 PM, Terry Reedy wrote:
> On 5/13/2018 3:22 PM, Ken Kundert wrote:
> 
> Please do not double post.
> 
>> I am seeing an unexpected difference between the behavior of the string
>> format method and f-strings.
> 
> Read
> https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals
> carefully.
> 
>> Here is an example:
>>
>>      import sys, os
>>      from inform import error, os_error
>>
>>      class mydict(dict):
>>          def __format__(self, template):
>>              print('Template:', template)
>>              return ', '.join(template.format(v, k=k, v=v) for k, v in
>> self.items())
>>
>>
>>      d = mydict(bob='239-8402', ted='371-8567', carol='891-5810',
>> alice='552-2219')
>>
>>      print('Using format():')
>>      print('Email: {0:{{k}}: {{v}}}'.format(d))
>>      print()
>>      print('Using f-string:')
>>      print(f'Email: {d:{{k}} {{v}}}')
>>      print()
>>      print('Using f-string:')
>>      print(f'Email: {d:{{k}} {{v}}}', k=6, v=9)
>>
>>
>> It generates the following response:
>>
>>      Using format():
>>      Template: {k}: {v}
>>      Email: bob: 239-8402, ted: 371-8567, carol: 891-5810, alice:
>> 552-2219
>>
>>      Using f-string:
>>      Traceback (most recent call last):
>>      File "tryit", line 18, in <module>
>>          print(f'Email: {d:{{k}} {{v}}}')
>>      NameError: name 'k' is not defined
> 
> This is what I expected.
> 
>> Essentially I am using a format string as the template that indicates
>> how to format each member of a dictionary, {{k}} should interpolate the
>> key and {{v}} interpolates the value.  This format string is embedded
>> inside another format string, so the braces are doubled up so that they
>> will be ignored by the outer format string.
> 
> "The parts of the string outside curly braces are treated literally,
> except that any doubled curly braces '{{' or '}}' are replaced with the
> corresponding single curly brace. " note 'outside'
> 
>> This idea seems to work okay when using the format() method. You can see
>> I added a print statement inside __format__ that shows that the method
>> is being called.
>>
>> However, trying the same idea with f-strings results in a NameError.  It
>> appears that the escaping does not work when used within the template.
>> It appears the error occurs before __format__ is called (there is no
>> output from the print function).
>>
>> Does anybody know why the format() method would work in this case but
>> the f-string would not?
> 
> All names in the expression are resolved in the local namespace of the f
> string.  There are other differences.  Nesting can only be one level deep.
> 
>> Is this a bug in f-strings?
> 
> Not to me.
> 
> 




More information about the Python-list mailing list