# Copyright (C) 2004 Python Software Foundation
# Author: barry@python.org (Barry Warsaw)

"""A package supporting PEP 292 Simple String Substitutions.

See http://www.python.org/peps/pep-0292.html for full specification.

Two subclasses of the unicode type are provided:

Template - PEP 292 'dollar' strings with the following substitution rules:

    1. $$ is an escape; it is replaced with a single $

    2. $identifier names a substitution placeholder matching a mapping
       key of "identifier".  "identifier" must be a Python identifier
       as defined in [2].  The first non-identifier character after
       the $ character terminates this placeholder specification.

    3. ${identifier} is equivalent to $identifier.  It is required for
       when valid identifier characters follow the placeholder but are
       not part of the placeholder, e.g. "${noun}ification".

    No other characters have special meaning.

    [2] Identifiers and Keywords
    http://www.python.org/doc/current/ref/identifiers.html

SafeTemplate - Like Template, except that KeyErrors will never be raised
when a placeholder is missing from the interpolation dictionary.

You can also derive your own classes from Template to define different
substitution rules.

Examples:

>>> from string import Template
>>> x = Template('$who owes me $what')
>>> print x
$who owes me $what
>>> print x % {'who': 'Tim', 'what': 'a bag of ham'}
Tim owes me a bag of ham

>>> import re
>>> class mstring(Template):
...    pattern = re.compile(
...        r'(\${2})|\$(mm:[_a-z]\w*)|\$({mm:[_a-z]\w*})', re.IGNORECASE)
...
>>> x = mstring('$who owes me $mm:what')
>>> print x % {'who':'Tim', 'mm:what':'nothing'}
$who owes me nothing
"""

__all__ = ['Template', 'SafeTemplate']

import re

class Template(unicode):
    """A string class for supporting $-substitutions."""
    __slots__ = []

    # Search for $$, $identifier, or ${identifier}
    pattern = re.compile(
        r'(\${2})|\$([_a-z][_a-z0-9]*)|\${([_a-z][_a-z0-9]*)}',
        re.IGNORECASE)

    def __mod__(self, mapping):
        def convert(mo):
            escaped, named, braced = mo.groups()
            if escaped is not None:
                return '$'
            else:
                return mapping[named or braced]
        return self.pattern.sub(convert, self)


class SafeTemplate(Template):
    """A string class for supporting $-substitutions.

    This class is 'safe' in the sense that you will never get KeyErrors if
    there are placeholders missing from the interpolation dictionary.  In that
    case, you will get the original placeholder in the value string.
    """
    __slots__ = []

    def __mod__(self, mapping):
        def convert(mo):
            escaped, named, braced = mo.groups()
            if escaped is not None:
                return '$'
            elif named is not None:
                try:
                    return mapping[named]
                except KeyError:
                    return '$' + named
            else:
                try:
                    return mapping[braced]
                except KeyError:
                    return '${' + braced + '}'
        return self.pattern.sub(convert, self)

del re

if __name__ == '__main__':
    import doctest
    print doctest.testmod()
