def METHOD_NAME(df, order_by, group_by=(), timecontext=None, **kwargs):
    sort_keys = []
    ascending = []
    for value in group_by:
        sort_keys.append(value)
        ascending.append(True)
    for key in order_by:
        sort_keys.append(key)
        ascending.append(key.ascending)
    new_columns = {}
    computed_sort_keys = []
    for key in sort_keys:
        computed_sort_key, temporary_column = compute_sort_key(
            key, df, timecontext, **kwargs
        )
        computed_sort_keys.append(computed_sort_key)
        if temporary_column is not None:
            new_columns[computed_sort_key] = temporary_column
    result = df.assign(**new_columns)
    result = result.sort_values(
        computed_sort_keys, ascending=ascending, kind="mergesort"
    )
    # TODO: we'll eventually need to return this frame with the temporary
    # columns and drop them in the caller (maybe using post_execute?)
    ngrouping_keys = len(group_by)
    return (
        result,
        computed_sort_keys[:ngrouping_keys],
        computed_sort_keys[ngrouping_keys:],
    )