Python String.format With Inline Pipes
Solution 1:
You can actually implement custom conversion functions if you subclass string.Formatter
. Following example is based on this post
import string
classTemplate(string.Formatter):
defconvert_field(self, value, conversion):
if conversion == 'u': # has to be a single charreturn value[:3] # replace with your shorten_url function# otherwise call the default convert_field methodreturnsuper(Template, self).convert_field(value, conversion)
print(Template().format('{url!u}', url='SOME LONG URL'))
Outputs SOM
Another option is to just modify kwargs before you pass it to format:
>>>defsms(**kwargs):... kwargs['shorturl'] = shorten_url(kwargs['url'])...print('test {shorturl}'.format(**kwargs))
Edit:
Based on the fact that you want to use globals()
, you could use something like
defbold(s):
return"<strong>" + s + "</strong>"defspan(s):
return"<span>" + s + "</span>"classTemplate(string.Formatter):
defget_field(self, name, args, kwargs):
parts = name.split('|')
# use first part as actual field name ('url' in this case)
obj, used_key = super(Template, self).get_field(parts.pop(0), args, kwargs)
forfilterin parts:
obj = globals()[filter](obj) # call remaining parts as filter functionsreturn obj, used_key
print(Template().format('{url|bold|span}', url='SOME LONG URL'))
# Outputs: <span><strong>SOME LONG URL</strong></span>
The |
char seems to be passed through with the field name, so you can (ab)use this as required. I would recommend adding some error handling and checking the call order on the functions is what you expect. I'm also not sure that using globals()
is a great idea, especially if you're going to be processing unsafe format strings.
Solution 2:
Pipes, or better "filters", are not implemented in Python stdlib templating.
Standard Python libraries offer various formatting options (justification, padding, number formatting), but it has certainly some limits.
Many templating packages do support custom filters, one of them being jinja2
:
from jinja2 import Environment
def dumb_shorten_url(url):
# just shortening forfun, implement real shorteningreturn url[6:]
env = Environment()
env.filters["shorten_url"] = dumb_shorten_url
templ = env.from_string("Sorry {{name}}, but your payment was rejects. ""Please visit {{url|shorten_url}} and try again.")
kwargs = {"name": "James", "url": "http://acme.com/one/two"}
print templ.render(**kwargs)
There is much more what jinja2
offers (templates read from file system, from directories, loops,
conditional expressions, escaping HTML...), but the example above shall demonstrate, it works with
"pipes".
Solution 3:
So this is more along the line of what I was looking for:
import re
defbold(string):
return"<strong>" + string + "</strong>"defformat(string, **kwargs):
# using the global scope, we can pipe kwargs through functions!
scope = globals()
defreplace(substr):
pieces = substr.group()[1:-1].split("|")
value = kwargs.get(pieces[0])
iflen(pieces) > 1:
pipes = pieces[1:]
for pipe in pipes:
value = scope[pipe](value)
return value
return re.sub(r"\{\S+\}", replace, string)
format("Hello {name|bold}, {yo}", **{"name":"Joe Schmo", "yo":"gimme more"})
It works, but the whole globals()
thing concerns me. What if I define a function in another scope in another file that I want to use?
Post a Comment for "Python String.format With Inline Pipes"