Source code for potpy.template
import re
def _parse(template):
parts = []
bracket_level = 0
start = 0
capture_bracket = False
for i, c in enumerate(template):
if c == '{':
if capture_bracket:
capture_bracket = False
continue
if template[i+1:i+2] == '{':
capture_bracket = True
continue
if not bracket_level:
part = template[start:i] \
.replace('\\', '\\\\').replace('{{', '{')
parts.append((part, None))
start = i + 1
bracket_level += 1
elif c == '}':
if not bracket_level:
continue
bracket_level -= 1
if not bracket_level:
bracket = template[start:i]
if ':' in bracket:
name, regex = bracket.split(':', 1)
else:
name = bracket
regex = '.*'
parts.append((regex, name))
start = i + 1
if bracket_level:
raise ValueError('unbalanced brackets')
part = template[start:].replace('\\', '\\\\').replace('{{', '{')
parts.append((part, None))
return parts
def _make_pattern(parsed):
return ''.join(
'(?P<%s>%s)' % (name, part) if name else part.replace('{', r'\{')
for part, name in parsed
) + '$'
def _make_fill_template(parsed):
return ''.join(
'%%(%s)s' % (name,) if name else part.replace('%', '%%')
for part, name in parsed
)
[docs]class Template(object):
"""
A simple string template class.
Allows you to match against a string, extracting a dictionary of template
parameters, with optional parameter type conversion. An example template::
>>> t = Template('Hello my name is {name}!')
Using the :meth:`~Template.match` method, you can extract information from
a string::
>>> t.match('Hello my name is David!')
{'name': 'David'}
>>> t.match('This string does not match.')
Parameter type conversion allows you to coerce parameters to Python
types::
>>> t = Template('The answer is {answer}', answer=int)
>>> t.match('The answer is 42')
{'answer': 42}
You can also specify a regex in your parameter spec to further refine
matches::
>>> t = Template('/posts/{post_id:\d+}', post_id=int)
>>> t.match('/posts/37')
{'post_id': 37}
>>> t.match('/posts/foo')
The reverse of matching is filling. Use the :meth:`~Template.fill` method
to insert information into your template string::
>>> t = Template('The answer is {answer}')
>>> t.fill(answer=42)
'The answer is 42'
"""
def __init__(self, template, **type_converters):
self.template = template
self.type_converters = type_converters
parsed = _parse(template)
self.regex = re.compile(_make_pattern(parsed))
self.fill_template = _make_fill_template(parsed)
[docs] def match(self, string):
"""Match a string against the template.
If the string matches the template, return a dict mapping template
parameter names to converted values, otherwise return ``None``.
>>> t = Template('Hello my name is {name}!')
>>> t.match('Hello my name is David!')
{'name': 'David'}
>>> t.match('This string does not match.')
"""
m = self.regex.match(string)
if m:
c = self.type_converters
return dict((k, c[k](v) if k in c else v)
for k, v in m.groupdict().iteritems())
return None
[docs] def fill(self, **kwargs):
"""Fill a template string with the given parameters.
>>> Template('The answer is {answer}').fill(answer=42)
'The answer is 42'
"""
return self.fill_template % kwargs