Source code for poolparty.region

"""Region and OrfRegion classes for poolparty - represent registered regions with their properties."""

from dataclasses import dataclass, field

from poolparty.types import Optional

# Valid frame values for ORF regions
VALID_FRAMES = {-3, -2, -1, 1, 2, 3}


[docs] @dataclass(frozen=True) class Region: """ Represents a registered region in a poolparty Party. Regions identify sections of sequences for later modification. Each region has a name and a seq_length that specifies the expected length of content within the region tags. Attributes ---------- name : str The region name (used in XML tags like <name>...</name>). seq_length : Optional[int] The expected length of content within the region: - None: Variable-length region (content length not fixed) - 0: Zero-length region (insertion point, <name/>) - >0: Fixed-length region (content must be this length) _id : int Unique identifier assigned by the Party upon registration. """ name: str seq_length: Optional[int] # None for variable-length, 0 for zero-length, >0 for fixed _id: int = field(default=-1, repr=False)
[docs] def __post_init__(self): """Validate region attributes.""" if not self.name: raise ValueError("Region name cannot be empty") if not self.name.isidentifier(): raise ValueError( f"Region name '{self.name}' is not a valid identifier. " "Use only letters, numbers, and underscores, starting with a letter." ) if self.seq_length is not None and self.seq_length < 0: raise ValueError(f"seq_length must be None or >= 0, got {self.seq_length}")
@property def is_variable_length(self) -> bool: """True if this region has variable length (seq_length is None).""" return self.seq_length is None @property def is_zero_length(self) -> bool: """True if this region is a zero-length insertion point.""" return self.seq_length == 0
[docs] def __hash__(self): """Hash based on name (regions with same name should be the same).""" return hash(self.name)
[docs] def __eq__(self, other): """Equality based on name.""" if isinstance(other, Region): return self.name == other.name return False
@dataclass(frozen=True) class OrfRegion(Region): """ Represents an ORF (Open Reading Frame) region with associated reading frame. Extends Region with frame information for ORF-aware operations like stylize_orf() and mutagenize_orf(). Attributes ---------- frame : int Reading frame and orientation. Valid values: +1, +2, +3, -1, -2, -3. Positive values indicate forward orientation (5'->3'), negative values indicate reverse orientation (3'->5'). The absolute value indicates the frame offset (1-indexed). """ frame: int = 1 def __post_init__(self): """Validate OrfRegion attributes.""" # Call parent validation super().__post_init__() # Validate frame if self.frame not in VALID_FRAMES: raise ValueError(f"frame must be one of {sorted(VALID_FRAMES)}, got {self.frame}")