def all_but_blocks(names, data, newline="\n", remove_empty_next=True,
                   remove_comments=True):
    """
    Multiline string from a list of strings data, removing every
    block with any of the given names, as well as their delimiters.
    Removes the empty lines after BLOCK_END when ``remove_empty_next``
    is True. Returns a joined string with the given newline, or a
    line generator if it's None. If desired, this function use
    ``commentless`` internally to remove the remaining comments.
    """
    @allow_implicit_stop
    def remove_blocks(name, iterable):
        start, end = BLOCK_START % name, BLOCK_END % name
        it = iter(iterable)
        while True:
            line = next(it)
            while line != start:
                yield line
                line = next(it)
            it = tail(itertools.dropwhile(not_eq(end), it))
            if remove_empty_next:
                it = itertools.dropwhile(lambda el: not el.strip(), it)
    if isinstance(names, str):
        names = [names]
    processors = [functools.partial(remove_blocks, name) for name in names]
    if remove_comments:
        processors.append(commentless)
    gen = functools.reduce(lambda result, func: func(result),
                           processors, data)
    return gen if newline is None else newline.join(gen)
Multiline string from a list of strings data, removing every
    block with any of the given names, as well as their delimiters.
    Removes the empty lines after BLOCK_END when ``remove_empty_next``
    is True. Returns a joined string with the given newline, or a
    line generator if it's None. If desired, this function use
    ``commentless`` internally to remove the remaining comments.