docs: def make_codon_list(protein_seq, template_dna=None, include_stop=True):
      """
      Return a list of codons that would be translated to the given protein 
      sequence.  Codons are picked first to minimize the mutations relative to a 
      template DNA sequence and second to prefer "optimal" codons.
      """
  
      codon_list = []
  
      if template_dna is None:
          template_dna = []
  
      # Reverse translate each codon, preferring (in order):
      # 1. The codon with the most similarity to the template codon.
      # 2. The codon with the highest natural usage.
  
      for i, res in enumerate(protein_seq.upper()):
          try: template_codon = template_dna[3*i:3*i+3]
          except IndexError: template_codon = '---'
  
          # Already sorted by natural codon usage
          possible_codons = dna.ecoli_reverse_translate[res]
  
          # Sort by similarity.  Note that this is a stable sort.
          possible_codons.sort(
                  key=lambda x: dna.num_mutations(x, template_codon))
  
          # Pick the best codon.
          codon_list.append(possible_codons[0])
  
      # Make sure the sequence ends with a stop codon.
      last_codon = codon_list[-1]
      stop_codons = dna.ecoli_reverse_translate['.']
  
      if include_stop and last_codon not in stop_codons:
          codon_list.append(stop_codons[0])
  
      return codon_list