On Thu, Sep 17, 2020 at 1:13 PM Eric V. Smith <eric@trueblade.com> wrote:
On 9/17/2020 12:24 PM, Chris Angelico wrote:
> On Fri, Sep 18, 2020 at 1:38 AM Eric V. Smith <eric@trueblade.com> wrote:
>> In general, there's no way to start with a format specifier string and
>> from it get the type of the object that it should be applied to. For
>> example, any string without %'s is a valid datetime format specifier (of
>> dubious value, but such is life). Perhaps a better example is decimal
>> vs. float. What if I want %.2f to return a decimal? Would that just not
>> be possible?
>>
>> So I think you'd have to limit this to a small set of built-in types.
>>
>> In general, I think overloading f-strings as assignment targets would be
>> confusing. But I've been wrong before.
>>
> It doesn't have to be absolutely identical, just as long as it's
> mostly parallel. Consider C's printf and scanf formats; any whitespace
> in scanf matches any whitespace, and the integer handlers are always
> happy to accept a leading plus sign (which has to be specifically
> requested in printf). Conversely, scanf can be more restrictive, eg
> %[a-z] which will match some sequence of lowercase ASCII letters.

Sure. And the less it looks like "real" f-strings, the worse it is, IMO.

If someone wanted to play with this in pure Python, string.Formatter's
parse() method would do all of the parsing for you.

Surprisingly it is not *quite* all. The format specification mini language parser is not exposed for use. This has bummed me out more than once in the past.

Here is my attempt at recreating it from a couple years ago. Do with it what you will.

>>> r=r'(([\s\S])?([<>=\^]))?([\+\- ])?([#])?([0])?(\d)*([,])?((\.)(\d)*)?([sbcdoxXneEfFgGn%])?'
>>>
>>> from collections import namedtuple as nt
>>> FormatSpec = nt('FormatSpec', 'fill align sign alt zero_padding width comma decimal precision type')
>>>
>>> import re
>>> spec = FormatSpec(*re.fullmatch(r,'x>5.2f').group(2,3,4,5,6,7,8,10,11,12)) # skip groups not interested in
>>> spec
FormatSpec(fill='x', align='>', sign=None, alt=None, zero_padding=None, width='5', comma=None, decimal='.', precision='2', type='f')
>>> ''.join(s for s in spec if s is not None)  # recreate the input spec
'x>5.2f'

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler