Take the path to a template and return the high data structure
    derived from the template.

    Helpers:

    :param mask_value:
        Mask value for debugging purposes (prevent sensitive information etc)
        example: "mask_value="pass*". All "passwd", "password", "pass" will
        be masked (as text).

def compile_template(template,
                     renderers,
                     default,
                     blacklist,
                     whitelist,
                     saltenv='base',
                     sls='',
                     input_data='',
                     **kwargs):
    '''
    Take the path to a template and return the high data structure
    derived from the template.

    Helpers:

    :param mask_value:
        Mask value for debugging purposes (prevent sensitive information etc)
        example: "mask_value="pass*". All "passwd", "password", "pass" will
        be masked (as text).
    '''

    # if any error occurs, we return an empty dictionary
    ret = {}

    log.debug('compile template: %s', template)

    if 'env' in kwargs:
        # "env" is not supported; Use "saltenv".
        kwargs.pop('env')

    if template != ':string:':
        # Template was specified incorrectly
        if not isinstance(template, six.string_types):
            log.error('Template was specified incorrectly: %s', template)
            return ret
        # Template does not exist
        if not os.path.isfile(template):
            log.error('Template does not exist: %s', template)
            return ret
        # Template is an empty file
        if salt.utils.files.is_empty(template):
            log.debug('Template is an empty file: %s', template)
            return ret

        with codecs.open(template, encoding=SLS_ENCODING) as ifile:
            # data input to the first render function in the pipe
            input_data = ifile.read()
            if not input_data.strip():
                # Template is nothing but whitespace
                log.error('Template is nothing but whitespace: %s', template)
                return ret

    # Get the list of render funcs in the render pipe line.
    render_pipe = template_shebang(template, renderers, default, blacklist, whitelist, input_data)

    windows_newline = '\r\n' in input_data

    input_data = StringIO(input_data)
    for render, argline in render_pipe:
        if salt.utils.stringio.is_readable(input_data):
            input_data.seek(0)      # pylint: disable=no-member
        render_kwargs = dict(renderers=renderers, tmplpath=template)
        render_kwargs.update(kwargs)
        if argline:
            render_kwargs['argline'] = argline
        start = time.time()
        ret = render(input_data, saltenv, sls, **render_kwargs)
        log.profile(
            'Time (in seconds) to render \'%s\' using \'%s\' renderer: %s',
            template,
            render.__module__.split('.')[-1],
            time.time() - start
        )
        if ret is None:
            # The file is empty or is being written elsewhere
            time.sleep(0.01)
            ret = render(input_data, saltenv, sls, **render_kwargs)
        input_data = ret
        if log.isEnabledFor(logging.GARBAGE):  # pylint: disable=no-member
            # If ret is not a StringIO (which means it was rendered using
            # yaml, mako, or another engine which renders to a data
            # structure) we don't want to log this.
            if salt.utils.stringio.is_readable(ret):
                log.debug('Rendered data from file: %s:\n%s', template,
                          salt.utils.sanitizers.mask_args_value(salt.utils.data.decode(ret.read()),
                                                                kwargs.get('mask_value')))  # pylint: disable=no-member
                ret.seek(0)  # pylint: disable=no-member

    # Preserve newlines from original template
    if windows_newline:
        if salt.utils.stringio.is_readable(ret):
            is_stringio = True
            contents = ret.read()
        else:
            is_stringio = False
            contents = ret

        if isinstance(contents, six.string_types):
            if '\r\n' not in contents:
                contents = contents.replace('\n', '\r\n')
                ret = StringIO(contents) if is_stringio else contents
            else:
                if is_stringio:
                    ret.seek(0)
    return ret