Skip to content

Commit

Permalink
feat: added post init checks for max_gap_opens and max_gap_extends
Browse files Browse the repository at this point in the history
  • Loading branch information
ameynert committed Jan 10, 2025
1 parent cdca0bf commit c9075ac
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
21 changes: 15 additions & 6 deletions prymer/offtarget/offtarget_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class OffTargetDetector(AbstractContextManager):
alignments does not exceed the specified maximum number of primer pair hits.
"""

def __init__(
def __init__( # noqa: C901
self,
ref: Path,
max_primer_hits: int,
Expand Down Expand Up @@ -211,15 +211,13 @@ def __init__(
`three_prime_region_length`. Must be between 0 and `three_prime_region_length`,
inclusive.
max_mismatches: the maximum number of mismatches allowed in the full length primer
(including any in the three prime region)
(including any in the three prime region). Must be greater than or equal to 0.
max_gap_opens: the maximum number of gaps (insertions or deletions) allowable in an
alignment of a oligo to the reference
alignment of a oligo to the reference. Must be greater than or equal to 0.
max_gap_extends: the maximum number of gap extensions allowed; extending a gap
beyond a single base costs 1 gap extension. Can be set to -1 to allow
unlimited extensions up to max diffs (aka max mismatches), while disallowing
"long gaps".
max_amplicon_size: the maximum amplicon size to consider amplifiable
(including any in the three prime region). Must be greater than or equal to 0.
"long gaps". Must be greater than or equal to -1.
max_amplicon_size: the maximum amplicon size to consider amplifiable. Must be greater
than 0.
cache_results: if True, cache results for faster re-querying
Expand All @@ -238,6 +236,8 @@ def __init__(
ValueError: If `max_mismatches_in_three_prime_region` is outside the range 0 to
`three_prime_region_length`, inclusive.
ValueError: If `max_mismatches` is not greater than or equal to 0.
ValueError: If `max_gap_opens` is not greater than or equal to 0.
ValueError: If `max_gap_extends` is not -1 or greater than or equal to 0.
"""
errors: list[str] = []
if max_amplicon_size < 1:
Expand Down Expand Up @@ -274,6 +274,15 @@ def __init__(
errors.append(
f"'max_mismatches' must be greater than or equal to 0. Saw {max_mismatches}"
)
if max_gap_opens < 0:
errors.append(
f"'max_gap_opens' must be greater than or equal to 0. Saw {max_gap_opens}"
)
if max_gap_extends < -1:
errors.append(
"'max_gap_extends' must be -1 (for unlimited extensions up to 'max_mismatches'="
f"{max_mismatches}) or greater than or equal to 0. Saw {max_gap_extends}"
)

if len(errors) > 0:
raise ValueError("\n".join(errors))
Expand Down
26 changes: 17 additions & 9 deletions tests/offtarget/test_offtarget.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from dataclasses import dataclass
from pathlib import Path

Expand Down Expand Up @@ -355,17 +356,20 @@ class CustomPrimer(Oligo):
@pytest.mark.parametrize(
(
"max_primer_hits,max_primer_pair_hits,min_primer_pair_hits,three_prime_region_length,"
"max_mismatches_in_three_prime_region,max_mismatches,max_amplicon_size,expected_error"
"max_mismatches_in_three_prime_region,max_mismatches,max_amplicon_size,"
"max_gap_opens,max_gap_extends,expected_error"
),
[
(-1, 1, 1, 20, 0, 0, 1, "'max_primer_hits' must be greater than or equal to 0. Saw -1"),
(1, -1, 1, 20, 0, 0, 1, "'max_primer_pair_hits' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, 1, -1, 20, 0, 0, 1, "'min_primer_pair_hits' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, 1, 1, 0, 0, 0, 1, "'three_prime_region_length' must be greater than 0. Saw 0"),
(1, 1, 1, 20, -1, 0, 1, "'max_mismatches_in_three_prime_region' must be between 0 and 'three_prime_region_length'=20 inclusive. Saw -1"), # noqa: E501
(1, 1, 1, 20, 21, 0, 1, "'max_mismatches_in_three_prime_region' must be between 0 and 'three_prime_region_length'=20 inclusive. Saw 21"), # noqa: E501
(1, 1, 1, 20, 0, -1, 1, "'max_mismatches' must be greater than or equal to 0. Saw -1"),
(1, 1, 1, 20, 0, 0, 0, "'max_amplicon_size' must be greater than 0. Saw 0"),
(-1, 1, 1, 20, 0, 0, 1, 0, 0, "'max_primer_hits' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, -1, 1, 20, 0, 0, 1, 0, 0, "'max_primer_pair_hits' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, 1, -1, 20, 0, 0, 1, 0, 0, "'min_primer_pair_hits' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, 1, 1, 0, 0, 0, 1, 0, 0, "'three_prime_region_length' must be greater than 0. Saw 0"),
(1, 1, 1, 20, -1, 0, 1, 0, 0, "'max_mismatches_in_three_prime_region' must be between 0 and 'three_prime_region_length'=20 inclusive. Saw -1"), # noqa: E501
(1, 1, 1, 20, 21, 0, 1, 0, 0, "'max_mismatches_in_three_prime_region' must be between 0 and 'three_prime_region_length'=20 inclusive. Saw 21"), # noqa: E501
(1, 1, 1, 20, 0, -1, 1, 0, 0, "'max_mismatches' must be greater than or equal to 0. Saw -1"), # noqa: E501
(1, 1, 1, 20, 0, 0, 0, 0, 0, "'max_amplicon_size' must be greater than 0. Saw 0"),
(1, 1, 1, 20, 0, 0, 1, -1, 0, "'max_gap_opens' must be greater than or equal to 0. Saw -1"),
(1, 1, 1, 20, 0, 5, 1, 0, -2, re.escape("'max_gap_extends' must be -1 (for unlimited extensions up to 'max_mismatches'=5) or greater than or equal to 0. Saw -2")), #noqa: E501
],
)
# fmt: on
Expand All @@ -378,6 +382,8 @@ def test_init(
max_mismatches_in_three_prime_region: int,
max_mismatches: int,
max_amplicon_size: int,
max_gap_opens: int,
max_gap_extends: int,
expected_error: str,
) -> None:
with pytest.raises(ValueError, match=expected_error):
Expand All @@ -390,4 +396,6 @@ def test_init(
max_mismatches_in_three_prime_region=max_mismatches_in_three_prime_region,
max_mismatches=max_mismatches,
max_amplicon_size=max_amplicon_size,
max_gap_opens=max_gap_opens,
max_gap_extends=max_gap_extends,
)

0 comments on commit c9075ac

Please sign in to comment.