Get keys from template

You could render it once with an instrumented dictionary which records calls, or a defaultdict, and then check what it asked for.

from collections import defaultdict
d = defaultdict("bogus")
text%d
keys = d.keys()

If it's okay to use string.format, consider using built-in class string.Formatter which has a parse() method:

>>> from string import Formatter
>>> [i[1] for i in Formatter().parse('Hello {1} {foo}')  if i[1] is not None]
['1', 'foo']

See here for more details.


The string.Template class has the pattern that is uses as an attribute. You can print the pattern to get the matching groups

>>> print string.Template.pattern.pattern

    \$(?:
      (?P<escaped>\$) |   # Escape sequence of two delimiters
      (?P<named>[_a-z][_a-z0-9]*)      |   # delimiter and a Python identifier
      {(?P<braced>[_a-z][_a-z0-9]*)}   |   # delimiter and a braced identifier
      (?P<invalid>)              # Other ill-formed delimiter exprs
    )

And for your example,

>>> string.Template.pattern.findall("$one is a $lonely $number.")
[('', 'one', '', ''), ('', 'lonely', '', ''), ('', 'number', '', '')]

As you can see above, if you do ${one} with braces it will go to the third place of the resulting tuple:

>>> string.Template.pattern.findall('${one} is a $lonely $number.')
[('', '', 'one', ''), ('', 'lonely', '', ''), ('', 'number', '', '')]

So if you want to get all the keys, you'll have to do something like:

>>> [s[1] or s[2] for s in string.Template.pattern.findall('${one} is a $lonely $number.$$') if s[1] or s[2]]
['one', 'lonely', 'number']