Allow simple field replacements, using the python str.format() syntax.

    When a string is passed that is tagged with :func:`~django.utils.safestring.mark_safe`,
    the context variables will be escaped before replacement.

    This function is used instead of lazily using Django templates,
    which can also the {% load %} stuff and {% include %} things.

def replace_fields(text, context, autoescape=None, errors='inline'):
    """
    Allow simple field replacements, using the python str.format() syntax.

    When a string is passed that is tagged with :func:`~django.utils.safestring.mark_safe`,
    the context variables will be escaped before replacement.

    This function is used instead of lazily using Django templates,
    which can also the {% load %} stuff and {% include %} things.
    """
    raise_errors = errors == 'raise'
    ignore_errors = errors == 'ignore'
    inline_errors = errors == 'inline'

    if autoescape is None:
        # When passing a real template context, use it's autoescape setting.
        # Otherwise, default to true.
        autoescape = getattr(context, 'autoescape', True)

    is_safe_string = isinstance(text, SafeData)
    if is_safe_string and autoescape:
        escape_function = conditional_escape
        escape_error = lambda x: u"<span style='color:red;'>{0}</span>".format(x)
    else:
        escape_function = force_text
        escape_error = six.text_type

    # Using str.format() may raise a KeyError when some fields are not provided.
    # Instead, simulate its' behavior to make sure all items that were found will be replaced.
    start = 0
    new_text = []
    for match in RE_FORMAT.finditer(text):
        new_text.append(text[start:match.start()])
        start = match.end()

        # See if the element was found
        key = match.group('var')
        try:
            value = context[key]
        except KeyError:
            logger.debug("Missing key %s in email template %s!", key, match.group(0))
            if raise_errors:
                raise
            elif ignore_errors:
                new_text.append(match.group(0))
            elif inline_errors:
                new_text.append(escape_error("!!missing {0}!!".format(key)))
            continue

        # See if further processing is needed.
        attr = match.group('attr')
        if attr:
            try:
                value = getattr(value, attr)
            except AttributeError:
                logger.debug("Missing attribute %s in email template %s!", attr, match.group(0))
                if raise_errors:
                    raise
                elif ignore_errors:
                    new_text.append(match.group(0))
                elif inline_errors:
                    new_text.append(escape_error("!!invalid attribute {0}.{1}!!".format(key, attr)))
                continue

        format = match.group('format')
        if format:
            try:
                template = u"{0" + format + "}"
                value = template.format(value)
            except ValueError:
                logger.debug("Invalid format %s in email template %s!", format, match.group(0))
                if raise_errors:
                    raise
                elif ignore_errors:
                    new_text.append(match.group(0))
                elif inline_errors:
                    new_text.append(escape_error("!!invalid format {0}!!".format(format)))
                continue
        else:
            value = escape_function(value)

        # Add the value
        new_text.append(value)

    # Add remainder, and join
    new_text.append(text[start:])
    new_text = u"".join(new_text)

    # Convert back to safestring if it was passed that way
    if is_safe_string:
        return mark_safe(new_text)
    else:
        return new_text