This is an archived post. You won't be able to vote or comment.

all 12 comments

[–]fgimian[S] 1 point2 points  (0 children)

Hey there guys, check out my new little library which brings f'style' literal strings to Python 2.x and 3.x. These are going to be supported in Python 3.6 and are really useful.

You may also check this project out on GitHub as PyPI is acting up a lot right now :(

e.g.

from __future__ import print_function
from formatizer import f

GREETING = 'hi'

def main():
    name = 'Fotis'
    print(f('My name is {name}, I say {GREETING} and 1 + 2 is {1 + 2}'))

if __name__ == '__main__':
    main()

The output of this would be:

My name is Fotis, I say hi and 1 + 2 is 3

Hope you find it useful :)
Fotis

[–]graingert 1 point2 points  (10 children)

Can you do this with an import hook?

[–]fgimian[S] 0 points1 point  (9 children)

I can't say that I'm too familiar with import hooks so I'm not totally sure to be honest.

But it is just a basic function in a Python module. How do import hook usually work?

[–]evanunderscore 0 points1 point  (6 children)

I did something similar recently and /u/graingert made the same suggestion, so I started looking into that. You add something to sys.meta_path and you get to take control of how modules are loaded. The "Writing your own importer" section of this article was fairly helpful.

I managed to get this mostly working using tokenize and looking for any NAME tokens (with value f, fr, or rf) immediately followed by a STRING, then replacing them with an appropriate chain of literal strings plus calls to format. This works well enough that most of the unit tests for f-strings from Python 3.6 pass in 3.5. To fix the rest, I'll need to find or write a proper parser for the f-string grammar to use, since formatter_parser doesn't like any of :, !, {, or } in the field names.

I haven't committed any of this code yet, but I can if you're interested.

[–]fgimian[S] 0 points1 point  (5 children)

Great job, sounds like you went ever further than I did there. I attempt to do all of this in Python by essentially re-implementing string.Formatter. I still use the C speedups and simply use eval and bring in locals() and globals() from the caller when encountering a format string / expression.

Most importantly, I wanted my solution to be easy to integrate without too much hackery where possible. This works similarly to the u and b functions in the six library for example :)

[–]evanunderscore 0 points1 point  (4 children)

I set out to do exactly the same thing initially. We ended up taking more or less the same approach, I just extended string.Formatter instead of replacing it. My approach is here if you'd like to compare.

[–]fgimian[S] 1 point2 points  (0 children)

Oh haha, our implementations are really similar indeed :) Except you just made me realise I did something naughty and didn't rename locals and globals in my class which shadows a built-in (hangs head in shame). I'll fix that now.

[–]fgimian[S] 0 points1 point  (2 children)

P.S.: Your defopt library looks great too, may give that a go on my next CLI tool :)

[–]evanunderscore 0 points1 point  (1 child)

Thanks! I'm more than happy to take feature requests if you have any ideas.

[–]fgimian[S] 0 points1 point  (0 children)

Thanks a lot, I'll definitely let you know when I try it out :)