Unique ID, head/tail lengths and taxonomy info from a sequence header.

    The description is the part of the FASTA/CMA sequence header starting after
    the first space (i.e. excluding ID), to the end of the line.

    This function looks inside the first '{...}' pair to extract info.

    Ex:

    >consensus seq
    >gi|15606894|ref|NP_214275.1|  {|2(244)|<Aquificae(B)>}DNA polymerase III gamma subunit [Aquifex aeolicus VF5] >gi|2984127|gb|AAC07663.1| DNA polymerase III gamma subunit [Aquifex aeolicus VF5] >gi|75
    >gi|3212262|pdb|1A2K|C  {<Chordata(M)>}Chain C, Gdpran-Ntf2 Complex >gi|3212263|pdb|1A2K|D Chain D, Gdpran-Ntf2 Complex >gi|3212264|pdb|1A2K|E Chain E, Gdpran-Ntf2 Complex >gi|5542273|pdb|1IBR|A C

def _parse_seq_header(line):
    """Unique ID, head/tail lengths and taxonomy info from a sequence header.

    The description is the part of the FASTA/CMA sequence header starting after
    the first space (i.e. excluding ID), to the end of the line.

    This function looks inside the first '{...}' pair to extract info.

    Ex:

    >consensus seq
    >gi|15606894|ref|NP_214275.1|  {|2(244)|<Aquificae(B)>}DNA polymerase III gamma subunit [Aquifex aeolicus VF5] >gi|2984127|gb|AAC07663.1| DNA polymerase III gamma subunit [Aquifex aeolicus VF5] >gi|75
    >gi|3212262|pdb|1A2K|C  {<Chordata(M)>}Chain C, Gdpran-Ntf2 Complex >gi|3212263|pdb|1A2K|D Chain D, Gdpran-Ntf2 Complex >gi|3212264|pdb|1A2K|E Chain E, Gdpran-Ntf2 Complex >gi|5542273|pdb|1IBR|A C

    """
    # ENH: use the two functions in esbglib.parseutils
    # or, move one or both of those functions into here
    _parts = line[1:].split(None, 1)
    rec_id = _parts[0]
    descr = _parts[1] if _parts[1:] else ''
    # Database cross references
    dbxrefs = {}
    if '|' in rec_id:
        id_gen = iter(rec_id.rstrip('|').split('|'))
        for key in id_gen:
            try:
                dbxrefs[key] = next(id_gen)
            except StopIteration:
                break

    # Head/tail lengths and taxonomy codes
    headlen = taillen = None
    phylum = taxchar = ''
    if descr.startswith('{'):
        _deets, description = descr[1:].split('}', 1)
        match = re.search(r"""
        (?:
        \| (?P<headlen> \d+)
            \( (?P<taillen> \d+)
            \)
        \|
        )?
        (?:
        <   (?P<phylum> .+?)
            \( (?P<taxchar> \w)
            \)
        >
        )?
        """, _deets, re.VERBOSE)
        if match:
            headlen, taillen, phylum, taxchar = match.groups()
            if headlen is not None:
                headlen = int(headlen)
            if taillen is not None:
                taillen = int(taillen)
            if phylum is None:
                phylum = ''
            if taxchar is None:
                taxchar = ''
        else:
            logging.warn("Couldn't match head/tail: %s", _deets)
    else:
        description = descr

    # TODO - return a dictionary here, update it in _parse_sequences
    return rec_id, dbxrefs, headlen, taillen, phylum, taxchar, description