Iterate of MAF format file and yield <yield_class> objects for each block.

  MAF files are arranged in blocks. Each block is a multiple alignment. Within
  a block, the first character of a line indicates what kind of line it is:

  a -- key-value pair meta data for block; one per block, should be first line
  s -- a sequence line; see __build_sequence() for details.
  i -- always come after s lines, and contain information about the context of
       the sequence; see __annotate_sequence_with_context() for details.
  e -- indicates that there is no aligning sequence for a species, but that
       there are blocks before and after this one that do align. See
       __build_unknown_sequence() for details.
  q -- quality information about an aligned base in a species. See
       __annotate_sequence_with_quality()

  :param yield_class:   yield objects returned by this function/constructor;
                        must accept key-word args of 'sequences' and
                        'meta_data' which are list and dictionary respectively.
                        Default is MultipleSequenceAlignment
  :param yield_kw_args: extra keyword args to pass to 'yield_class'

def maf_iterator(fn, index_friendly=False,
                 yield_class=MultipleSequenceAlignment, yield_kw_args={},
                 verbose=False):
  """
  Iterate of MAF format file and yield <yield_class> objects for each block.

  MAF files are arranged in blocks. Each block is a multiple alignment. Within
  a block, the first character of a line indicates what kind of line it is:

  a -- key-value pair meta data for block; one per block, should be first line
  s -- a sequence line; see __build_sequence() for details.
  i -- always come after s lines, and contain information about the context of
       the sequence; see __annotate_sequence_with_context() for details.
  e -- indicates that there is no aligning sequence for a species, but that
       there are blocks before and after this one that do align. See
       __build_unknown_sequence() for details.
  q -- quality information about an aligned base in a species. See
       __annotate_sequence_with_quality()

  :param yield_class:   yield objects returned by this function/constructor;
                        must accept key-word args of 'sequences' and
                        'meta_data' which are list and dictionary respectively.
                        Default is MultipleSequenceAlignment
  :param yield_kw_args: extra keyword args to pass to 'yield_class'
  """

  try:
    fh = open(fn)
  except (TypeError):
    fh = fn
  if index_friendly:
    fh = iter(fh.readline, '')

  sequences = []
  meta_data = {}
  for line in fh:
    line = line.strip()
    if line == "" or line[0] == "#":
      continue
    parts = line.split()
    line_type = parts[0].strip()
    if line_type == A_LINE:
      if sequences != []:
        meta_data[SEQ_ORDER_KEY] = [s.name for s in sequences]
        kw_args = merge_dictionaries({"sequences": sequences,
                                      "meta_data": meta_data}, yield_kw_args)
        yield yield_class(**kw_args)
      sequences = []
      meta_data = {}
      for i in range(1, len(parts)):
        assert(parts[i].count("=") == 1)
        piv = parts[i].find("=")
        meta_data[parts[i][:piv]] = parts[i][piv + 1:]
    elif line_type == S_LINE:
      sequences.append(__build_sequence(parts))
    elif line_type == I_LINE:
      if len(sequences) < 1:
        raise MAFError("found information line with no preceeding sequence " +
                       "in block")
      __annotate_sequence_with_context(sequences[-1], parts)
    elif line_type == E_LINE:
      sequences.append(__build_unknown_sequence(parts))
    elif line_type == Q_LINE:
      if len(sequences) < 1:
        raise MAFError("found quality line with no preceeding sequence in " +
                       "block")
      __annotate_sequence_with_quality(sequences[-1], parts)
    else:
      raise MAFError("Unknown type of MAF line: " + line)

  # don't forget to yield the final block
  if sequences != []:
    meta_data[SEQ_ORDER_KEY] = [s.name for s in sequences]
    kw_args = merge_dictionaries({"sequences": sequences,
                                  "meta_data": meta_data},
                                 yield_kw_args)
    yield yield_class(**kw_args)