There are many applications for this basic technique ranging from web development to document generation.
Existing Templating Solutions
Python comes with a few templating mechanisms already. I've listed a few common ones below:- '%d bottles of beer on the wall' % 50 # the old way
- '{amount} bottles of beer on the wall'.format(amount=50) # the new way
- '{amount} bottles of beer on the wall'.format(**{'amount': 50}) # unpack dictionary (handy trick)
- string.Template('$amount bottles of beer on the wall').format(amount=50) # the verbose way
- string.Template('$amount bottles of beer on the wall').format({'amount': 50}) # no need to unpack. See Template Strings for more information
As you might guess, there are a lot of templating engines available for Python. In my case using one seemed a bit overkill and I ended up coming up with a simple pattern that goes beyond what Python provides by default.
Simple Templating Engine
In my case I wanted to use a recursive dictionary structure that contains the context. This is something I construct based on a collection of JSON files. This task is very simple thanks to Python's json module. Unfortunately the tools above break down when trying to access recursive data.
After thinking about it for a while, the solution wasn't that difficult. It did mean I had to use a regular expression but nothing too shady. You can find my rendering function below:
import re def render(tpl, context): """Substitute text in <>with corresponding variable value.""" regex = re.compile('\<([a-zA-Z.]+)\>', re.MULTILINE) def repl(m): group = m.group(1) parts = group.split('.') value = context for part in parts: try: value = value[part] value = str(value) if isinstance(value, int) else value except KeyError: value = '' return value return regex.sub(repl, tpl) print(render('<amount>bottles of <bottle.label> , { 'amount': 10, 'bottle': { 'label': 'Amarillo' } }))'
The solution above takes a template and fields (a dictionary) as its parameters and returns a rendered template. There is some error handling in place and it deals with a recursive context.
There are likely other ways to achieve the same result but this one seemed to fit my purposes just fine. This is a tiny bit of an invoice generator we've been working on at our co-op. We're trying to replace our Google Drive based solution with something nicer and programmatic. So far it looks pretty good and might make a cool web service later on even!
Conclusion
I think the example highlights the usefulness of regular expressions. The expression doesn't look too bad. If you investigate it further, you noticed I've used a group to extract the data out of a match. It's a handy trick. You can even extract multiple groups in the same time.There are likely other ways to achieve the same result but this one seemed to fit my purposes just fine. This is a tiny bit of an invoice generator we've been working on at our co-op. We're trying to replace our Google Drive based solution with something nicer and programmatic. So far it looks pretty good and might make a cool web service later on even!