def construct_codons_dict(alphabet_file = None):
    """Generate the sub_codons_right dictionary of codon suffixes.

    syntax of custom alphabet_files:
        
    char: list,of,amino,acids,or,codons,separated,by,commas
    
    
    Parameters
    ----------
    alphabet_file : str
        File name for a custom alphabet definition. If no file is provided, the 
        default alphabet is used, i.e. standard amino acids, undetermined amino 
        acids (B, J, X, and Z), and single codon symbols.

    Returns
    -------
    codons_dict : dict
        Dictionary, keyed by the allowed 'amino acid' symbols with the values 
        being lists of codons corresponding to the symbol.
    
    """
    
    #Some symbols can't be used in the CDR3 sequences in order to allow for
    #regular expression parsing and general manipulation.
    protected_symbols = [' ', '\t', '\n', '\x0b', '\x0c', '\r', ':', ',', ';', '[', ']', '{', '}', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    
    
    #construct list of all 64 codons
    codons = [i + j + k for i in 'ACGT' for j in 'ACGT' for k in 'ACGT']
    
    codons_dict = {}
    #add standard amino acids symbols to the dict (i.e. 'ACDEFGHIKLMNPQRSTVWY*').
    #these symbols CANNOT be overwritten by custom alphabet files
    for codon in codons:
        codons_dict[nt2aa(codon)] = codons_dict.get(nt2aa(codon), []) + [codon]
                 
    #add single codon symbols to allow for inframe ntseq pgen computation
    #'\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf'
    #these symbols CANNOT be overwritten by custom alphabet files
    for codon in codons:
        codons_dict[nt2codon_rep(codon)] = [codon]  
    
    #Check to see if custom alphabet file is supplied, else use default alphabet
    
    #Include standard ambigious amino acids.
    #these symbols CAN be overwritten by custom alphabet files
    expanded_alphabet = {}
    expanded_alphabet['B'] = ['D','N']
    expanded_alphabet['J'] = ['I', 'L']
    expanded_alphabet['X'] = ['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y']
    expanded_alphabet['Z'] = ['E', 'Q']
    if alphabet_file is not None: #Use custom alphabet file definitions
        alphabet_f = open(alphabet_file, 'r')
        for line in alphabet_f:
            #assumed syntax is of a line is:
            #s: a1, a2, a3, a4, a5, ..., aN
            #where s is a single character symbol that isn't reserved, and all 
            #of the a's are either amino acid symbols or codons. Whitespaces
            #will be stripped as will brackets if the a's are presented as a
            #list.
            c_symbol = line.split(':', 1)[0].strip(''.join(protected_symbols))
            #Note there shouldn't be any additional colons -- this is a protected symbol.
            c_aa_codon_list_str = line.split(':', 1)[1]
            expanded_alphabet[c_symbol] = [x.strip(''.join(protected_symbols)) for x in c_aa_codon_list_str.split(',')]
        
        alphabet_f.close()
    
    
    for symbol in expanded_alphabet.keys():
        #Double check that the symbol isn't already used (important particularly for the single codon representation)
        if symbol in codons_dict.keys():
            print symbol + " is already used as an 'amino acid' symbol for codons: "
            print codons_dict[symbol]
            continue
        elif not len(symbol) == 1: #Check that the custom symbol is a single character
            print "Can't use " + symbol + " as a custom 'amino acid' definitions as such symbols must be single characters."
            continue
        elif symbol in protected_symbols: #This elif shouldn't trigger due to the stripping of protected symbols.
            print symbol + " is a protected character"
        current_codon_collection = set()
        for x in expanded_alphabet[symbol]:
            if x in codons_dict.keys(): #Check if reference to an amino acid or other amino acid symbol
                current_codon_collection = current_codon_collection.union(codons_dict[x]) #If so, add those codons to the new collection
            elif x.upper() in codons: #Check if specifying a single codon
                current_codon_collection.add(x.upper()) #If so, add the codon to the new collection
            elif len(x) == 0: #fully stripped away
                continue
            else: #If not, don't recognize the addition and continue.
                print 'Unfamiliar amino acid symbol or codon: ' + x
                continue
        codons_dict[symbol] = list(current_codon_collection)
    
                
    return codons_dict