[Python-ideas] Draft PEP on string interpolation

Mike Miller python-ideas at mgmiller.net
Mon Aug 24 22:57:45 CEST 2015


Hi, here's my latest idea, riffing on other's latest this weekend.

Let's call these e-strings (for expression), as it's easier to refer to the 
letter of the proposals than three digit numbers.

So, an e-string looks like an f-string, though at compile-time, it is converted 
to an object instead (like i-string):

     print(e'Hello {friend}, filename: {filename}.')   # converts to ==>

     print(estr('Hello {friend}, filename: {filename}.', friend=friend,
                                                         filename=filename))

An estr is a subclass of str, therefore able to do the nice things a string can 
do.  Rendering is deferred, and it also has a raw member, escape(), and 
translate() methods:

     class estr(str):
         # init: saves self.raw, args, kwargs for later
         # methods, ops render it
         # def escape(self, escape_func):  # handles escaping
         # def translate(self, template, safe=True): # optional i18n support

To make it as simple as possible to use by end-developers, it 1) doesn't require 
str() to be run explicitly, it renders itself when needed via its various 
methods and operators.  Look for .raw, if you need the original.

Also, 2) a bit of responsibility is pushed to stdlib/pypi.  In a handful of 
sensitive places, the object is checked beforehand and escaped when needed:

     def os_system(command):   # imagine os.system, subprocess, dbapi, etc.
         if isinstance(command, estr):
             command = command.escape(shlex.quote)  # each chooses its own rules
         do_something(command)

This means a billion lines of code using e-strings won't have to care about 
them, only a handful of places.  What is easiest to type is now safe as well:

     os.system(e'cat {filename}')  # sleep easy

A translate method might available also (though we may have given up on i18n 
already), to provide a new raw string from a message catalog:

     rendered = message.translate(translated_message)  # fmt syntax TBD

This should enable the safety and features we'd like, without burdening the 
everyday user.  I've created a sample script, here is the output:

     # consider:   estr('Hello {friend}, filename: {filename}.')
     friend:       'John'
     filename:     "somefile; rm -rf ~ 'foo' <html>"

     original:     Hello {friend}, filename: {filename}.
     print():      Hello John, filename: somefile; rm -rf ~ 'foo' <html>.

     shell escape:
         Hello John, filename: 'somefile; rm -rf ~ '"'"'foo'"'"' <html>'.
     html escape:
         Hello John, filename: somefile; rm -rf ~ &#x27;foo&#x27; <html>.
     sql escape:   Hello "John", filename: "somefile; rm -rf ~ 'foo' <html>".
     logger DEBUG  Hello John, filename: somefile; rm -rf ~ 'foo' <html>.

     upper+utf8:   b"HELLO JOHN, FILENAME: SOMEFILE; RM -RF ~ 'FOO' <HTML>."
     translated:   Hola John, archivo: somefile; rm -rf ~ 'foo' <html>.


Anything I've missed?

-Mike


On 08/20/2015 04:10 PM, Mike Miller wrote:
> The ground seems to be settling on the issue, so I have tried my hand at a grand
> unified pep for string interpolation.
>


More information about the Python-ideas mailing list