• Custom Search

string.Template with default substitution value

I wanted a string.Template class that will substitute placeholders with a default value if the mapping is not provided.

For example, say I create a template

A template with $value1, $value2

When I substitute this template with values, say I provide only $value1, I want other $variables to be substituted with a default value. If my default value is HAHA. and I provide this map {'value1': 'first_value'}, the result is

A template with first_value, HAHA

Creating the class was very simple (in retrospect).

I just subclassed string.Template and provided my own version of safe_substitute.

 

Here's the class, complete with doctest code.

 

import string
 
class Template(string.Template):
    """
        Template with modified functions
 
    -- doctests ----
    >>> t = 'this is a $test with $result'
    >>> st = Template(t, 'haha')
    >>> d = {'test': 'a big test'}
    >>> result = st.safe_substitute(d)
    >>> expected_result = 'this is a a big test with haha'
    >>> test_result = True if expected_result == result else 'expected %s >>> but got <<< %s' % (expected_result,  result)
    >>> test_result
    True
    >>> st = Template(t)
    >>> d = {'test': 'a big test'}
    >>> result = st.safe_substitute(d)
    >>> expected_result = 'this is a a big test with $result'
    >>> test_result = True if expected_result == result else 'expected %s >>> but got <<< %s' % (expected_result,  result)
    >>> test_result
    True
 
 
    """
 
 
    def __init__(self, template, default_substitution_value = None):
        string.Template(template)
        self.default_substitution_value = default_substitution_value
        self.template = template
 
 
    def safe_substitute(self, *args, **kws):
        '''
            Returns template with placeholders substituted
              if no substitution value is specified, the
                  default_substitution_value
                  is used
        '''
        if len(args) > 1:
            raise TypeError('Too many positional arguments')
        if not args:
            mapping = kws
        elif kws:
            mapping = _multimap(kws, args[0])
        else:
            mapping = args[0]
        # Helper function for .sub()
        def convert(mo):
            named = mo.group('named')
            if named is not None:
                try:
                    # We use this idiom instead of str() because the latter
                    # will fail if val is a Unicode containing non-ASCII
                    return '%s' % (mapping[named],)
                except KeyError:
                    if self.default_substitution_value:
                        return self.default_substitution_value
                    return self.delimiter + named
 
            braced = mo.group('braced')
            if braced is not None:
                try:
                    return '%s' % (mapping[braced],)
                except KeyError:
                    return self.delimiter + '{' + braced + '}'
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                return self.delimiter
            raise ValueError('Unrecognized named group in pattern',
                             self.pattern)
        return self.pattern.sub(convert, self.template)

 

I built the above because I was building an interface to populate P2 Energy's Field Operations equipment readings. The application sends data via xml files and there are many attributes in the file that I did not care to populate. Rather than hand code a default value for each of them, I built the above python class.

Read more python articles

This entry was posted in python. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*