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}}")