Reads codon alignment from file.

    *fastafile* is the name of an existing FASTA file.

    *checknewickvalid* : if *True*, we require that names are unique and do
    **not** contain spaces, commas, colons, semicolons, parentheses, square
    brackets, or single or double quotation marks.
    If any of these disallowed characters are present, raises an Exception.

    Reads the alignment from the *fastafile* and returns the aligned
    sequences as a list of 2-tuple of strings *(header, sequence)*
    where *sequence* is upper case.

    If the terminal codon is a stop codon for **all** sequences, then
    this terminal codon is trimmed. Raises an exception if the sequences
    are not aligned codon sequences that are free of stop codons (with
    the exception of a shared terminal stop) and free of ambiguous nucleotides.

    Read aligned sequences in this example:

    >>> seqs = [('seq1', 'ATGGAA'), ('seq2', 'ATGAAA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> seqs == a
    True

    Trim stop codons from all sequences in this example:

    >>> seqs = [('seq1', 'ATGTAA'), ('seq2', 'ATGTGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> [(head, seq[ : -3]) for (head, seq) in seqs] == a
    True

    Read sequences with gap:

    >>> seqs = [('seq1', 'ATG---'), ('seq2', 'ATGAGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> [(head, seq) for (head, seq) in seqs] == a
    True

    Premature stop codon gives error:

    >>> seqs = [('seq1', 'TGAATG'), ('seq2', 'ATGAGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True) # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ValueError:

def ReadCodonAlignment(fastafile, checknewickvalid):
    """Reads codon alignment from file.

    *fastafile* is the name of an existing FASTA file.

    *checknewickvalid* : if *True*, we require that names are unique and do
    **not** contain spaces, commas, colons, semicolons, parentheses, square
    brackets, or single or double quotation marks.
    If any of these disallowed characters are present, raises an Exception.

    Reads the alignment from the *fastafile* and returns the aligned
    sequences as a list of 2-tuple of strings *(header, sequence)*
    where *sequence* is upper case.

    If the terminal codon is a stop codon for **all** sequences, then
    this terminal codon is trimmed. Raises an exception if the sequences
    are not aligned codon sequences that are free of stop codons (with
    the exception of a shared terminal stop) and free of ambiguous nucleotides.

    Read aligned sequences in this example:

    >>> seqs = [('seq1', 'ATGGAA'), ('seq2', 'ATGAAA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> seqs == a
    True

    Trim stop codons from all sequences in this example:

    >>> seqs = [('seq1', 'ATGTAA'), ('seq2', 'ATGTGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> [(head, seq[ : -3]) for (head, seq) in seqs] == a
    True

    Read sequences with gap:

    >>> seqs = [('seq1', 'ATG---'), ('seq2', 'ATGAGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True)
    >>> [(head, seq) for (head, seq) in seqs] == a
    True

    Premature stop codon gives error:

    >>> seqs = [('seq1', 'TGAATG'), ('seq2', 'ATGAGA')]
    >>> f = io.StringIO()
    >>> n = f.write(u'\\n'.join(['>{0}\\n{1}'.format(*tup) for tup in seqs]))
    >>> n = f.seek(0)
    >>> a = ReadCodonAlignment(f, True) # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ValueError:
    """
    codonmatch = re.compile('^[ATCG]{3}$')
    gapmatch = re.compile('^-+^')
    seqs = [(seq.description.strip(), str(seq.seq).upper()) for seq
            in Bio.SeqIO.parse(fastafile, 'fasta')]
    assert seqs, "{0} failed to specify any sequences".format(fastafile)

    seqlen = len(seqs[0][1])
    if not all([len(seq) == seqlen for (head, seq) in seqs]):
        raise ValueError(("All sequences in {0} are not of the same length; "
                "they must not be properly aligned").format(fastafile))
    if (seqlen < 3) or (seqlen % 3 != 0):
        raise ValueError(("The length of the sequences in {0} is {1} which "
                "is not divisible by 3; they are not valid codon sequences"
                ).format(fastafile, seqlen))

    terminalcodon = []
    codons_by_position = dict([(icodon, []) for icodon in range(seqlen // 3)])
    for (head, seq) in seqs:
        assert len(seq) % 3 == 0
        for icodon in range(seqlen // 3):
            codon = seq[3 * icodon : 3 * icodon + 3]
            codons_by_position[icodon].append(codon)
            if codonmatch.search(codon):
                aa = str(Bio.Seq.Seq(codon).translate())
                if aa == '*':
                    if icodon + 1 != len(seq) // 3:
                        raise ValueError(("In {0}, sequence {1}, non-terminal "
                                "codon {2} is stop codon: {3}").format(
                                fastafile, head, icodon + 1, codon))
            elif codon == '---':
                aa = '-'
            else:
                raise ValueError(("In {0}, sequence {1}, codon {2} is invalid: "
                        "{3}").format(fastafile, head, icodon + 1, codon))
        terminalcodon.append(aa)

    for (icodon, codonlist) in codons_by_position.items():
        if all([codon == '---' for codon in codonlist]):
            raise ValueError(("In {0}, all codons are gaps at position {1}"
                    ).format(fastafile, icodon + 1))

    if all([aa in ['*', '-'] for aa in terminalcodon]):
        if len(seq) == 3:
            raise ValueError(("The only codon is a terminal stop codon for "
                    "the sequences in {0}").format(fastafile))
        seqs = [(head, seq[ : -3]) for (head, seq) in seqs]
    elif any([aa == '*' for aa in terminalcodon]):
        raise ValueError(("Only some sequences in {0} have a terminal stop "
                "codon. All or none must have terminal stop.").format(fastafile))

    if any([gapmatch.search(seq) for (head, seq) in seqs]):
        raise ValueError(("In {0}, at least one sequence is entirely composed "
                "of gaps.").format(fastafile))

    if checknewickvalid:
        if len(set([head for (head, seq) in seqs])) != len(seqs):
            raise ValueError("Headers in {0} not all unique".format(fastafile))
        disallowedheader = re.compile('[\s\:\;\(\)\[\]\,\'\"]')
        for (head, seq) in seqs:
            if disallowedheader.search(head):
                raise ValueError(("Invalid character in header in {0}:"
                        "\n{2}").format(fastafile, head))

    return seqs