import itertools


def expand_benchmark_definition(benchmark_definition):
    """
    Takes a benchmark definition, e.g.:
    [{name: 'foo', compiler: ['g++-5', 'g++-6']},
     {name: ['bar', 'baz'], compiler: ['g++-5'], cxx_std: 'c++14'}]

    And expands it into the individual benchmarks to run, in the example above:
    [{name: 'foo', compiler: 'g++-5'},
     {name: 'foo', compiler: 'g++-6'},
     {name: 'bar', compiler: 'g++-5', cxx_std: 'c++14'},
     {name: 'baz', compiler: 'g++-5', cxx_std: 'c++14'}]
    """
    dict_keys = sorted(benchmark_definition.keys())
    # Turn non-list values into single-item lists.
    benchmark_definition = {dict_key: value if isinstance(value, list)
    else [value]
                            for dict_key, value in benchmark_definition.items()}
    # Compute the cartesian product of the value lists
    value_combinations = itertools.product(*(benchmark_definition[dict_key] for dict_key in dict_keys))
    # Then turn the result back into a dict.
    return [dict(zip(dict_keys, value_combination))
            for value_combination in value_combinations]