Query a list of dicts with a list of queries and return a table.

    A "table" is a list of OrderedDicts each having the same keys in the same
    order.

    :param dicts: the list of input dicts
    :type dicts: list of dicts

    :param columns: the list of column query dicts, or the path to a JSON file
        containing the list of column query dicts
    :type columns: list of dicts, or string

    :param csv: return a UTF8-encoded, CSV-formatted string instead of a list
        of dicts
    :type csv: bool

    :rtype: list of dicts, or CSV string

def table(dicts, columns, csv=False, pretty=False):
    """Query a list of dicts with a list of queries and return a table.

    A "table" is a list of OrderedDicts each having the same keys in the same
    order.

    :param dicts: the list of input dicts
    :type dicts: list of dicts

    :param columns: the list of column query dicts, or the path to a JSON file
        containing the list of column query dicts
    :type columns: list of dicts, or string

    :param csv: return a UTF8-encoded, CSV-formatted string instead of a list
        of dicts
    :type csv: bool

    :rtype: list of dicts, or CSV string

    """
    # Optionally read columns from file.
    if isinstance(columns, basestring):
        columns = _read_columns_file(columns)

    # Either "pattern" or "pattern_path" (but not both) is allowed in the
    # columns.json file, but "pattern" gets normalised to "pattern_path" here.
    for column in columns.values():
        if "pattern" in column:
            assert "pattern_path" not in column, (
                'A column must have either a "pattern" or a "pattern_path"'
                "but not both")
            column["pattern_path"] = column["pattern"]
            del column["pattern"]


    table_ = []
    for d in dicts:
        row = collections.OrderedDict()  # The row we'll return in the table.
        for column_title, column_spec in columns.items():
            if not column_spec.get('return_multiple_columns', False):
                row[column_title] = query(dict_=d, **column_spec)
            else:
                multiple_columns = query(dict_=d, **column_spec)
                for k, v in multiple_columns.items():
                    row[k] = v

        table_.append(row)

    if pretty:
        # Return a pretty-printed string (looks like a nice table when printed
        # to stdout).
        return tabulate.tabulate(table_, tablefmt="grid", headers="keys")
    elif csv:
        # Return a string of CSV-formatted text.
        return _table_to_csv(table_)
    else:
        # Return a list of dicts.
        return table_