Spaces:
Sleeping
Sleeping
| """ | |
| Sequence-liability motif scanning for mRNA constructs. | |
| Scans the functional regions of an mRNA for short sequence motifs that are | |
| known to compromise expression, stability, or processing: | |
| - **uORF start (upstream AUG)** in the 5'UTR β can initiate an upstream | |
| open reading frame and reduce translation of the main CDS. | |
| - **Premature polyadenylation signal** (AAUAAA / AUUAAA) inside the CDS β | |
| can cause truncated transcripts. | |
| - **AU-rich element (ARE, AUUUA)** in the 3'UTR β recruits decay machinery | |
| and shortens mRNA half-life. | |
| - **Cryptic 5' splice-donor consensus** (GT[AG]AGT) anywhere β risk of | |
| aberrant splicing when transcribed from a DNA template. | |
| All scanning is done on DNA alphabet (T, not U); inputs are normalised. | |
| Severities are plain strings ("critical" / "warning" / "info") so this module | |
| stays free of any dependency on the liability aggregator. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| from dataclasses import dataclass | |
| from typing import List, Optional | |
| # Severity labels (kept as bare strings to avoid import cycles) | |
| CRITICAL = "critical" | |
| WARNING = "warning" | |
| INFO = "info" | |
| # Cap per-motif hits so a pathological sequence can't produce thousands of rows | |
| _MAX_HITS_PER_MOTIF = 50 | |
| class MotifHit: | |
| """A single sequence-liability motif occurrence.""" | |
| name: str # machine name, e.g. "uorf" | |
| label: str # human label, e.g. "Upstream AUG (uORF)" | |
| region: str # "5'UTR" | "CDS" | "3'UTR" | "full" | |
| start: int # 0-based start within the scanned region | |
| end: int # exclusive end within the region | |
| match: str # the matched subsequence (DNA alphabet) | |
| severity: str # CRITICAL | WARNING | INFO | |
| description: str # why it matters | |
| recommendation: str # what to do about it | |
| def __repr__(self) -> str: | |
| return f"MotifHit({self.name} {self.region}@{self.start} {self.match!r})" | |
| def _norm(seq: Optional[str]) -> str: | |
| return (seq or "").upper().replace("U", "T") | |
| def _find_all(pattern: str, seq: str) -> List[re.Match]: | |
| """Find non-overlapping regex matches, capped.""" | |
| return list(re.finditer(pattern, seq))[:_MAX_HITS_PER_MOTIF] | |
| def scan_motifs( | |
| five_prime_utr: Optional[str] = None, | |
| cds: Optional[str] = None, | |
| three_prime_utr: Optional[str] = None, | |
| full_seq: Optional[str] = None, | |
| ) -> List[MotifHit]: | |
| """ | |
| Scan an mRNA's regions for liability motifs. | |
| Pass the individual components when available. ``full_seq`` is used for | |
| region-agnostic scans (splice donor) and as a fallback when components | |
| are not provided (e.g. a monolithic ``full_mrna`` record). | |
| """ | |
| hits: List[MotifHit] = [] | |
| utr5 = _norm(five_prime_utr) | |
| cds_s = _norm(cds) | |
| utr3 = _norm(three_prime_utr) | |
| full = _norm(full_seq) or (utr5 + cds_s + utr3) | |
| # ββ uORF: any ATG in the 5'UTR ββββββββββββββββββββββββββββββββββββββββββββ | |
| for m in _find_all("ATG", utr5): | |
| hits.append(MotifHit( | |
| name="uorf", | |
| label="Upstream AUG (uORF)", | |
| region="5'UTR", | |
| start=m.start(), end=m.end(), match=m.group(), | |
| severity=WARNING, | |
| description="An AUG in the 5'UTR can start an upstream ORF and " | |
| "reduce ribosome loading on the main CDS.", | |
| recommendation="Remove or silently mutate upstream AUGs in the 5'UTR.", | |
| )) | |
| # ββ Premature polyadenylation signal inside the CDS βββββββββββββββββββββββ | |
| for m in _find_all("AATAAA|ATTAAA", cds_s): | |
| hits.append(MotifHit( | |
| name="premature_polya", | |
| label="Premature polyA signal", | |
| region="CDS", | |
| start=m.start(), end=m.end(), match=m.group(), | |
| severity=CRITICAL, | |
| description="A canonical polyadenylation signal inside the CDS can " | |
| "cause premature cleavage and a truncated protein.", | |
| recommendation="Codon-optimise to remove the internal AAUAAA/AUUAAA signal.", | |
| )) | |
| # ββ AU-rich element (ARE) in the 3'UTR ββββββββββββββββββββββββββββββββββββ | |
| for m in _find_all("ATTTA", utr3): | |
| hits.append(MotifHit( | |
| name="are", | |
| label="AU-rich element (ARE)", | |
| region="3'UTR", | |
| start=m.start(), end=m.end(), match=m.group(), | |
| severity=WARNING, | |
| description="AUUUA pentamers recruit mRNA-decay machinery and shorten " | |
| "transcript half-life.", | |
| recommendation="Remove ARE motifs from the 3'UTR to improve stability.", | |
| )) | |
| # ββ Cryptic 5' splice-donor consensus (region-agnostic) βββββββββββββββββββ | |
| for m in _find_all("GT[AG]AGT", full): | |
| hits.append(MotifHit( | |
| name="splice_donor", | |
| label="Cryptic splice donor", | |
| region="full", | |
| start=m.start(), end=m.end(), match=m.group(), | |
| severity=INFO, | |
| description="Matches the 5' splice-donor consensus; may cause aberrant " | |
| "splicing if transcribed from a DNA template in cells.", | |
| recommendation="Consider disrupting the GU[A/G]AGU consensus if splicing is a concern.", | |
| )) | |
| return hits | |