Poor man's f-strings in Python 2.6 f-strings is a new feature in the upcoming python release which basically lets you do this:

&gt;&gt;&gt; s = 'qwerty'
&gt;&gt;&gt; f'{s} {1+2}'
'qwerty 3'


Not pleased at having to wait until f-strings are widely supported, I came up with the following function, which should work for any Python version starting with 2.6.

def f(s):
    return s.format(**inspect.currentframe().f_back.f_locals)

&gt;&gt;&gt; s = 'qwerty'
&gt;&gt;&gt; f('{s} abc')
'qwerty abc'


Switching to f-strings in the future should be as easy as removing the two parentheses.

Of course, my function doesn't support the more advanced features of f-strings (like evaluation of any expression).

My questions are:


If I start widespread use of this function in my code, is there a chance this bites me in the future? Is there anything objectionable in my current implementation?
How would you implement a more advanced f-function, supporting evaluation of arbitrary expressions? Preferably without having performance suffer too much - my benchmark shows the f-function is only slightly slower than directly calling format. 


Python version 2.6.
The thing most likely, in my mind, to cause problems here is that using frames is not guaranteed to work in any other Python implementation besides CPython.

inspect.currentframe()
Return the frame object for the caller’s stack frame.
CPython implementation detail: This function relies on Python stack frame support in the interpreter, which isn’t guaranteed to exist in all implementations of Python. If running in an implementation without Python stack frame support this function returns None.

This will end up throwing an error if you try to unpack None because the implementation doesn't support your function. If you were dead-set on using stack inspection, then you should give a more useful error message than AttributeError: 'NoneType' object has no attribute 'f_back'. You should test for if you get a frame or not, and then use platform to print a more helpful error message.
def f(s):
    frame = inspect.currentframe()
    if frame is not None:
        return s.format(**frame.f_back.f_locals)
    else:
        raise NotImplementedError(
            &quot;Python implementation {} does not support stack inspection.&quot;.format(
                platform.python_implementation()
            )
        )

At this point, then, you should make sure this is a well-documented limitation of your library, and I would probably write some unit tests where it asserts that is raised for certain implementations.
The real problem, however, isn't that you're using something that isn't guaranteed (although that's certainly a problem). The real problem is that stack inspection is almost never the right answer. We don't need it here either.  What we're going to want to do is something like this:
def _poor_fstring(namespace):
    def _():
        return namespace
    return _

get_namespace = _poor_fstring(locals())

def f(s):
    return s.format(**get_namespace())

This still isn't ideal - you'll need to redeclare get_namespace in any scope you want access to it - but you avoid stack inspection, which is worth it in my mind.
Really though, I don't see a huge benefit between using your function and just calling s.format(**namespace), where namespace is whatever lookup table you want.  The one benefit would be if, as you said, it supported arbitrary expressions. And the only way to get arbitrary expressions is to, well, evaluate them.  You'll basically have to parse the string and extract everything between curly braces. Then try a lookup in the namespace and replace that, otherwise compile it, and then eval what you find. Hopefully I don't need to point out that evaling random strings is... dangerous.
Going into actually parsing a format string is way beyond the scope of this question.
In short, my recommendation would be to just suffer through... or upgrade your Python version.