95

I am looking for either technique or templating system for Python for formatting output to simple text. What I require is that it will be able to iterate through multiple lists or dicts. It would be nice if I would be able to define template into separate file (like output.templ) instead of hardcoding it into source code.

As simple example what I want to achieve, we have variables title, subtitle and list

title = 'foo'
subtitle = 'bar'
list = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

And running throught a template, output would look like this:

Foo
Bar

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

How to do this? Thank you.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
Gargauth
  • 2,495
  • 6
  • 27
  • 30

4 Answers4

272

You can use the standard library string an its Template class.

Having a file foo.txt:

$title
$subtitle
$list

And the processing of the file (example.py):

from string import Template

d = {
    'title': 'This is the title',
    'subtitle': 'And this is the subtitle',
    'list': '\n'.join(['first', 'second', 'third'])
}

with open('foo.txt', 'r') as f:
    src = Template(f.read())
    result = src.substitute(d)
    print(result)

Then run it:

$ python example.py
This is the title
And this is the subtitle
first
second
third
ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
ThibThib
  • 8,010
  • 3
  • 30
  • 37
19

There are quite a number of template engines for python: Jinja, Cheetah, Genshi etc. You won't make a mistake with any of them.

jammon
  • 3,404
  • 3
  • 20
  • 29
19

If your prefer to use something shipped with the standard library, take a look at the format string syntax. By default it is not able to format lists like in your output example, but you can handle this with a custom Formatter which overrides the convert_field method.

Supposed your custom formatter cf uses the conversion code l to format lists, this should produce your given example output:

cf.format("{title}\n{subtitle}\n\n{list!l}", title=title, subtitle=sibtitle, list=list)

Alternatively you could preformat your list using "\n".join(list) and then pass this to your normal template string.

Oben Sonne
  • 9,893
  • 2
  • 40
  • 61
  • 1
    Dead simple for CLI output templating :) – m3nda Oct 09 '15 at 20:20
  • 3
    Here is an example how to do this: https://makina-corpus.com/blog/metier/2016/the-worlds-simplest-python-template-engine . A simple template engine with loops and conditions in 10 lines of code. – asmaier Nov 08 '16 at 14:34
1

if you want arbitrary prefixes/suffixes to identify your variables, you can simply use re.sub with a lambda expression:

from pathlib import Path
import re

def tpl(fn:Path, v:dict[str,str]) -> str:
    text = fn.with_suffix('.html').read_text()
    return re.sub("(<!-- (.+?) -->)", lambda m: v[m[2].lower()], text)

html = tpl(Path(__file__), {
    'title' : 't',
    'body' : 'b'
})
user1050755
  • 11,218
  • 4
  • 45
  • 56