[Python-ideas] Draft PEP on string interpolation
Eric Fahlgren
ericfahlgren at gmail.com
Sun Aug 30 20:40:05 CEST 2015
On Sat Aug 29 18:41:55 CEST 2015, Ron Adam wrote:
> One is nested evaluations, but that may be fixable.
>
> Nesting arguments and more complex examples:
>
> >>> for align, text in zip('<^>', ['left', 'center', 'right']):
> ... '{0:{fill}{align}16}'.format(text, fill=align, align=align)
> ...
> 'left<<<<<<<<<<<<'
> '^^^^^center^^^^^'
> '>>>>>>>>>>>right'
>
> I haven't gotten this example to work yet.
I've deployed an implementation of my interpretation of PEP-498 in our 2.7
production code, tested in 3.4.
Here's the function and your test case working properly:
#!/bin/env python
from __future__ import print_function, division
import sys as _sys
try:
import _string # Py3
def _parseFormat(string):
return _string.formatter_parser(string)
unicode = str
except ImportError: # Py2
def _parseFormat(string):
return string._formatter_parser()
def _stringInterpolater(s, depth=1, evalCallback=None, context=None,
**kwds):
"""
$uuid:4cbb6191-464a-56dd-88e2-52f4f861527e$
Experimental implementation of the behavior described in
https://www.python.org/dev/peps/pep-0498/
The first extension, ``depth``, allows for reimplementation from a
function that looks deeper into the call stack for the "current" context
(see ``printi``, below).
The second extension, ``evalCallback``, allows us to intercept the value
before formatting so that we can, for example, convert a Marker object
to an Adams id (see Simulatable.formatFunction for details).
``context`` allows the user to pass a dictionary to completely replace
the namespace calculations performed below. It too gets superseded by
``kwds``.
"""
# Local frame is needed for error reporting, so calculate it even
# when the user supplies a context.
localFrame = _sys._getframe()
while depth:
localFrame = localFrame.f_back
depth -= 1
if context:
localDict = kwds
globalDict = context.copy()
else:
localDict = localFrame.f_locals.copy() # Must copy to avoid side
effects.
localDict.update(kwds) # Our **kwds override
locals.
globalDict = localFrame.f_globals
def doFormat(formatString):
""" $uuid:7c45191f-741b-51a0-b75a-a534e99f58cb$ """
result = list()
for text, expression, formatSpec, conversion in
_parseFormat(formatString):
if text:
result.append(text)
if expression is None:
break
value = eval(expression, globalDict, localDict)
if evalCallback:
value = evalCallback(value)
if conversion == "r":
if isinstance(value, unicode): # Delete this check in Py3.
value = str(value) # Eat the annoying "u" prefix in Py2.
value = repr(value)
elif conversion == "s":
value = str(value)
formatSpec = doFormat(formatSpec) # Recurse to evaluate embedded
formats.
try:
result.append(format(value, formatSpec))
except ValueError as e:
raise ValueError("{}, object named '{}'".format(str(e),
expression), localFrame)
return "".join(result)
return doFormat(s)
f = _stringInterpolater
def printi(*args, **kwds):
"""
$uuid:15cccafc-f4ae-58df-ab3e-03553e567bf0$
"""
sep = kwds.pop("sep", " ") # Py2 smell, in Py3 you'd just put them in
the signature.
end = kwds.pop("end", "\n")
file = kwds.pop("file", _sys.stdout)
newArgs = list()
for arg in args:
if isinstance(arg, (str, unicode)):
newArgs.append(f(arg, depth=2, **kwds))
print(*newArgs, sep=sep, end=end, file=file)
if __name__ == "__main__":
strings = {
"<" : "left ",
"^" : " center ",
">" : " right",
}
width = 2*5 + max(len(s) for s in strings.values())
for align in strings:
fill = align
print(f("{strings[align]:{fill}{align}{width}}"))
printi("{strings[align]:{fill}{align}{width}}")
More information about the Python-ideas
mailing list