Source code for pen_assemble.codon

"""
Codon optimisation utilities for human expression.

Implements rule-based codon optimisation using the Kazusa Homo sapiens
high-expression preferred-codon table. Provides restriction-site scanning
and ORF assembly helpers.

Note
----
This is a rule-based optimiser (one preferred codon per amino acid). For
synthesis orders, verify the codon-adaptation index (CAI) with a commercial
tool (IDT CodonOpt, Twist, GeneArt) before submitting.
"""

from __future__ import annotations

__all__ = [
    "CODON_TABLE_HUMAN",
    "RESTRICTION_SITES",
    "codon_optimise",
    "gc_content",
    "check_restriction_sites",
    "build_expression_orf",
]

# ---------------------------------------------------------------------------
# Kazusa Homo sapiens preferred-codon table
# (most frequent codon per amino acid in human high-expression genes)
# ---------------------------------------------------------------------------
CODON_TABLE_HUMAN: dict[str, str] = {
    "A": "GCC",  # Ala
    "R": "AGG",  # Arg
    "N": "AAC",  # Asn
    "D": "GAC",  # Asp
    "C": "TGC",  # Cys
    "Q": "CAG",  # Gln
    "E": "GAG",  # Glu
    "G": "GGC",  # Gly
    "H": "CAC",  # His
    "I": "ATC",  # Ile
    "L": "CTG",  # Leu
    "K": "AAG",  # Lys
    "M": "ATG",  # Met
    "F": "TTC",  # Phe
    "P": "CCC",  # Pro
    "S": "AGC",  # Ser
    "T": "ACC",  # Thr
    "W": "TGG",  # Trp
    "Y": "TAC",  # Tyr
    "V": "GTG",  # Val
    "*": "TGA",  # Stop (preferred)
}

# ---------------------------------------------------------------------------
# Common restriction enzyme recognition sites
# ---------------------------------------------------------------------------
RESTRICTION_SITES: dict[str, str] = {
    "EcoRI": "GAATTC",
    "BamHI": "GGATCC",
    "HindIII": "AAGCTT",
    "NotI": "GCGGCCGC",
    "XhoI": "CTCGAG",
    "NheI": "GCTAGC",
    "XbaI": "TCTAGA",
    "SalI": "GTCGAC",
}


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


[docs] def codon_optimise(aa_sequence: str) -> str: """Translate an amino acid sequence to human-preferred-codon DNA. Unknown amino acid symbols are replaced with ``NNN``. Parameters ---------- aa_sequence: Single-letter amino acid sequence (case-insensitive). Stop codons (``*``) are included if present. Returns ------- str DNA sequence (uppercase, no spaces). Examples -------- >>> from pen_assemble.codon import codon_optimise >>> codon_optimise("MA") 'ATGGCC' >>> codon_optimise("M*") 'ATGTGA' """ return "".join(CODON_TABLE_HUMAN.get(aa.upper(), "NNN") for aa in aa_sequence)
[docs] def gc_content(dna: str) -> float: """Return the GC fraction of a DNA string. Parameters ---------- dna: DNA sequence (any case). Returns ------- float GC fraction in [0, 1], or 0.0 for empty strings. Examples -------- >>> from pen_assemble.codon import gc_content >>> round(gc_content("GCGCAT"), 4) 0.6667 """ if not dna: return 0.0 uc = dna.upper() return sum(1 for b in uc if b in "GC") / len(uc)
[docs] def check_restriction_sites(dna: str) -> list[str]: """Return names of restriction enzymes whose sites appear in *dna*. Checks both strands (forward only; palindromic sites are self-complementary so forward-strand search is sufficient for all sites in :data:`RESTRICTION_SITES`). Parameters ---------- dna: DNA sequence (any case). Returns ------- list[str] Sorted list of enzyme names with sites found in *dna*. Empty if none. Examples -------- >>> from pen_assemble.codon import check_restriction_sites >>> check_restriction_sites("AAAGAATTCAAA") ['EcoRI'] >>> check_restriction_sites("ACGTACGT") [] """ uc = dna.upper() return sorted(name for name, site in RESTRICTION_SITES.items() if site in uc)
[docs] def build_expression_orf( aa_sequence: str, kozak: bool = True, stop: bool = True, ) -> str: """Build a full expression-ready ORF from an amino acid sequence. Applies codon optimisation then optionally prepends a Kozak consensus (``GCCACC``) and appends a preferred stop codon (``TGA``). Parameters ---------- aa_sequence: Amino acid sequence (case-insensitive). Must begin with ``M`` (Met). kozak: If ``True`` (default), prepend Kozak context ``GCCACC``. stop: If ``True`` (default), append stop codon ``TGA``. Returns ------- str DNA sequence ready for gene synthesis. Raises ------ ValueError If *aa_sequence* is empty or does not start with Met. Examples -------- >>> from pen_assemble.codon import build_expression_orf >>> build_expression_orf("MA", kozak=False, stop=False) 'ATGGCC' >>> build_expression_orf("MA", kozak=True, stop=True) 'GCCACCATGGCCTGA' """ if not aa_sequence: raise ValueError("aa_sequence must not be empty") if aa_sequence[0].upper() != "M": raise ValueError(f"aa_sequence must start with Met (M); got {aa_sequence[0]!r}") orf = codon_optimise(aa_sequence) if kozak: orf = "GCCACC" + orf if stop: orf = orf + "TGA" return orf