Skip to content

Commit

Permalink
refactor: move check south
Browse files Browse the repository at this point in the history
  • Loading branch information
msto committed Sep 19, 2024
1 parent edb963b commit e91b176
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
33 changes: 22 additions & 11 deletions prymer/primer3/primer3.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,22 +730,33 @@ def _create_design_region(
ValueError: If the target region is too large to be padded.
"""
# Pad the target region on both sides by the maximum amplicon length (minus the length of
# the target). This ensures that the design region covers the complete window of potentially
# valid primer pairs.
padding: int = max_amplicon_length - target_region.length
contig_length: int = self._dict[target_region.refname].length

design_region: Span = replace(
target_region,
start=max(1, target_region.start - padding),
end=min(target_region.end + padding, contig_length),
)

left_design_window: int = target_region.start - design_region.start
right_design_window: int = design_region.end - target_region.end
# Apply the padding, ensuring that we don't run out-of-bounds on the target contig.
contig_length: int = self._dict[target_region.refname].length
design_start: int = max(1, target_region.start - padding)
design_end: int = min(target_region.end + padding, contig_length)

# Validate that our design window includes sufficient space for a primer to be designed on
# each side of the target region.
left_design_window: int = target_region.start - design_start
right_design_window: int = design_end - target_region.end
if left_design_window < min_primer_length or right_design_window < min_primer_length:
raise ValueError(
f"Target region {target_region} is too large to design an amplicon of maximum "
f"length {max_amplicon_length}."
f"Target region {target_region} exceeds the maximum size compatible with a "
f"maximum amplicon length of {max_amplicon_length} and a minimum primer length of "
f"{min_primer_length}. The maximum amplicon length should exceed the length of "
"the target region by at least twice the minimum primer length."
)

# Return the validated design region.
design_region: Span = replace(
target_region,
start=design_start,
end=design_end,
)

return design_region
30 changes: 26 additions & 4 deletions tests/primer3/test_primer3.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ def test_primer3_result_as_primer_pair_result_exception(


@pytest.mark.parametrize("max_amplicon_length", [100, 101])
def test_pad_target_region(max_amplicon_length: int, genome_ref: Path) -> None:
def test_create_design_region(max_amplicon_length: int, genome_ref: Path) -> None:
"""If the target region is shorter than the max amplicon length, it should be padded to fit."""
target_region = Span(refname="chr1", start=201, end=250, strand=Strand.POSITIVE)

Expand All @@ -572,12 +572,34 @@ def test_pad_target_region(max_amplicon_length: int, genome_ref: Path) -> None:
assert design_region.length == 2 * max_amplicon_length - target_region.length


def test_pad_target_region_doesnt_pad(genome_ref: Path) -> None:
"""If the target region is larger than the max amplicon length, no padding should occur."""
def test_create_design_region_raises_when_target_region_exceeds_max_amplicon_length(
genome_ref: Path,
) -> None:
"""
`_create_design_region()` should raise a ValueError when the target region is larger than the
max amplicon length.
"""
target_region = Span(refname="chr1", start=201, end=250, strand=Strand.POSITIVE)

with Primer3(genome_fasta=genome_ref) as designer:
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="exceeds the maximum size"):
designer._create_design_region(
target_region=target_region, max_amplicon_length=10, min_primer_length=10
)


def test_create_design_region_raises_when_primers_would_not_fit_in_design_region(
genome_ref: Path,
) -> None:
"""
`_create_design_region()` should raise a ValueError when the design region does not include
sufficient space flanking the target for a primer to be designed. (i.e. when this space is less
than the specified minimum primer length.)
"""
target_region = Span(refname="chr1", start=201, end=250, strand=Strand.POSITIVE)

with Primer3(genome_fasta=genome_ref) as designer:
with pytest.raises(ValueError, match="exceeds the maximum size"):
designer._create_design_region(
target_region=target_region, max_amplicon_length=55, min_primer_length=10
)

0 comments on commit e91b176

Please sign in to comment.