[Python-ideas] String interpolation for all literal strings

Nick Coghlan ncoghlan at gmail.com
Fri Aug 7 09:33:33 CEST 2015


On 7 August 2015 at 04:27, Barry Warsaw <barry at python.org> wrote:
> On Aug 06, 2015, at 11:01 PM, Nick Coghlan wrote:
>
>>* you can't restrict them to "literals only", so you run a much higher risk
>>of code injection attacks
>
> In an i18n context you do sometimes need to pass in non-literals.  Restricting
> this thing to literals only doesn't really increase the attack vector
> significantly, and does close off an important use case.

flufl.il8n, gettext, etc wouldn't go away - my "allow il8n use as
well" idea was just aimed at making interpolated strings easy to
translate by default. If f-strings are always eagerly interpolated
prior to translation, then I can foresee a lot of complaints from
folks asking why this doesn't work right:

    print(_(f"This is a translated message with {a} and {b} interpolated"))

When you're mixing translation with interpolation, you really want the
translation lookup to happen first, when the placeholders are still
present in the format string:

    print(_("This is a translated message with {a} and {b}
interpolated").format(a=a, b=b))

I've made the lookup explicit there, but of course sys._getframe()
also allows it to be implicit.

We could potentially make f-strings translation friendly by
introducing a bit of indirection into the f-string design: an
__interpolate__ builtin, along the lines of __import__.

That system could further be designed so that, by default,
"__interpolate__ = str.format", but a module could also do something
like "from flufl.il8n import __interpolate__" to get translated
f-strings in that module (preferably using the PEP 292/215 syntax,
rather than adding yet another spelling for string interpolation).

>>* you can only implement them via stack walking, so name resolution doesn't
>>work right. You can get at the locals and globals for the calling frame, but
>>normal strings are opaque to the compiler, so lexical scoping doesn't trigger
>>properly
>
> In practice, you need sys._getframe(2) to make it work, although flufl.i18n
> does allow you to specify a different depth.  In practice you could probably
> drop that for the most part. (ISTR an obscure use case for depth>2 but can't
> remember the details.)
>
> Really, the only nasty bit about flufl.i18n's implementation is the use of
> sys._getframe().  Fortunately, it's a big of ugliness that's buried in the
> implementation and never really seen by users.  If there was a more better way
> of getting at globals and locals, that was Python-implementation independent,
> that would clean up this little wart.

sys._getframe() usage is what I meant by stack walking. It's not
*really* based on walking the stack, but you're relying on poking
around in runtime state to do dynamic scoping, rather than being able
to do lexical analysis at compile time (and hence why static analysers
get confused about apparently unused local variables).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list