def METHOD_NAME(input_file):
    """Yield dicts for rows from a CSV file, whether it has a header or not
    The reason we don’t just add a header to every .csv file and use DictReader
    is that these .csv files may have arbitrarily many columns.
    """
    reader = csv.reader(input_file)
    have_header = None
    column_count = None
    have_notes = False
    seen_queries = set()
    first_row = True
    for row_index, row in enumerate(reader):
        if first_row:
            first_row = False
            have_header = row[0].lower().strip() == "query"
            if have_header:
                column_count = len(row)
            if have_header and row[-1].lower().strip() == "notes":
                if row[-2].strip():
                    raise Exception("Must have blank column before notes column")
                have_notes = True
            if have_header:
                # Don’t yield first line
                continue
        if have_header:
            if len(row) > column_count:
                raise Exception(f"More entries in row {row} than header values")
        query_term = row[0].strip()
        # max_col may be None here, but that’s ok, because `row[1:None]` is
        # the Python way of saying `row[1:]` when the blank in the middle
        # of `:]` is actually a variable in `row[1:max_col]` below.
        max_col = column_count
        if have_notes:
            if row[column_count - 2].strip():
                raise Exception("Must have blank column before notes column")
            max_col = column_count - 2
        values = row[1:max_col]
        while not values[-1].strip():
            values = values[:-1]
        if query_term in seen_queries:
            raise Exception(f"Duplicate query term {query_term}")
        seen_queries.add(query_term)
        yield {"Query": query_term, "Values": values}