def _reconstruct(keyvals, dialect, keep_order=False,
                 sort_attribute_values=False):
    """
    Reconstructs the original attributes string according to the dialect.

    Parameters
    ==========
    keyvals : dict
        Attributes from a GFF/GTF feature

    dialect : dict
        Dialect containing info on how to reconstruct a string version of the
        attributes

    keep_order : bool
        If True, then perform sorting of attribute keys to ensure they are in
        the same order as those provided in the original file.  Default is
        False, which saves time especially on large data sets.

    sort_attribute_values : bool
        If True, then sort values to ensure they will always be in the same
        order.  Mostly only useful for testing; default is False.
    """
    if not dialect:
        raise AttributeStringError()
    if not keyvals:
        return ""
    parts = []

    # May need to split multiple values into multiple key/val pairs
    if dialect['repeated keys']:
        items = []
        for key, val in keyvals.items():
            if len(val) > 1:
                for v in val:
                    items.append((key, [v]))
            else:
                items.append((key, val))
    else:
        items = list(keyvals.items())

    def sort_key(x):
        # sort keys by their order in the dialect; anything not in there will
        # be in arbitrary order at the end.
        try:
            return dialect['order'].index(x[0])
        except __HOLE__:
            return 1e6

    if keep_order:
        items.sort(key=sort_key)

    for key, val in items:

        # Multival sep is usually a comma:
        if val:
            if sort_attribute_values:
                val = sorted(val)

            val_str = dialect['multival separator'].join(val)

            if val_str:

                # Surround with quotes if needed
                if dialect['quoted GFF2 values']:
                    val_str = '"%s"' % val_str

                # Typically "=" for GFF3 or " " otherwise
                part = dialect['keyval separator'].join([key, val_str])
        else:
            part = key
        parts.append(part)

    # Typically ";" or "; "
    parts_str = dialect['field separator'].join(parts)

    # Sometimes need to add this
    if dialect['trailing semicolon']:
        parts_str += ';'

    return parts_str


# TODO:
# Cythonize -- profiling shows that the bulk of the time is spent on this
# function...
dataset/ETHPy150Open daler/gffutils/gffutils/parser.py/_reconstruct