def from_template(template, destination, values, is_file=True, jinja2=False,
    pre_func=None, post_func=None):
    """
    Return a `template` rendered with `values` using string.format or jinja2 as the template engine.

    * Set `destination` to a filename to store the output, to a Falsy value to skip this feature.
    * Set `is_file` to False to use value of `template` as the content and not a filename to read.
    * Set `{pre,post}_func` to a callback function with the signature f(content, values, jinja2)

    **Example usage**

    >>> template = '{{username={user}; password={pass}}}'
    >>> values = {'user': 'tabby', 'pass': 'miaow', 'other': 10}
    >>> open('config.template', 'w', 'utf-8').write(template)

    The behavior when all keys are given (and even more):

    >>> _ = from_template('config.template', 'config', values)
    >>> print(open('config', 'r', 'utf-8').read())
    {username=tabby; password=miaow}

    >>> _ = from_template(template, 'config', values, is_file=False)
    >>> print(open('config', 'r', 'utf-8').read())
    {username=tabby; password=miaow}

    >>> def post_func(content, values, jinja2):
    ...     return content.replace('tabby', 'tikky')
    >>> print(from_template(template, None, values, is_file=False, post_func=post_func))
    {username=tikky; password=miaow}

    The behavior if a value for a key is missing:

    >>> from_template('config.template', 'config', {'pass': 'miaow', 'other': 10})  # doctest: +ELLIPSIS
    Traceback (most recent call last):
        ...
    KeyError: ...'user'
    >>> print(open('config', 'r', 'utf-8').read())
    {username=tabby; password=miaow}

    >>> os.remove('config.template')
    >>> os.remove('config')
    """
    content = open(template, 'r', 'utf-8').read() if is_file else template
    if pre_func:
        content = pre_func(content, values=values, jinja2=jinja2)
    if jinja2:
        from jinja2 import Template
        content = Template(content).render(**values)
    else:
        content = content.format(**values)
    if post_func:
        content = post_func(content, values=values, jinja2=jinja2)
    if destination:
        with open(destination, 'w', 'utf-8') as f:
            f.write(content)
    return content
