code: def all_but_blocks(names, data, newline="\n", remove_empty_next=True,
                     remove_comments=True):
      
      @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)
docs: 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.