Skip to content

Commit

Permalink
refactor: revert thermo-related attributes, params, weights; revert d…
Browse files Browse the repository at this point in the history
…oc-tests and unit-tests
  • Loading branch information
emmcauley committed Oct 2, 2024
1 parent e3870f3 commit 932c823
Show file tree
Hide file tree
Showing 13 changed files with 38 additions and 271 deletions.
4 changes: 2 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The `prymer` Python library is intended to be used for three main purposes:

1. [Clustering targets](#clustering-targets) into larger amplicons prior to designing primers.
2. [Designing oligos](#designing-primers) using Primer3 for each target from (1).
2. [Designing](#designing-primers) primers (single and paired) and internal hybridization probes using Primer3 for each target from (1).
3. [Build and Picking a set of primer pairs](#build-and-picking-primer-pairs) from the design candidates produced in (2).

## Clustering Targets
Expand All @@ -18,7 +18,7 @@ amplicons prior to primer design.
Designing primers (left or right) or primer pairs using Primer3 is primarily performed using the
[`Primer3`][prymer.primer3.primer3.Primer3] class, which wraps the
[`primer3` command line tool](https://github.com/primer3-org/primer3). The
[`design_oligos()`][prymer.primer3.primer3.Primer3.design_oligos] facilitates the design of primers (single and paired) and internal hybridization probes
[`design()`][prymer.primer3.primer3.Primer3.design] method facilitates the design of primers (single and paired) and internal hybridization probes
for a single target. The `Primer3` instance is intended to be re-used to design primers across multiple targets, or
re-design (after changing parameters) for the same target, or both!

Expand Down
17 changes: 1 addition & 16 deletions prymer/api/oligo.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
# Oligo Class and Methods
This module contains a class and class methods to represent an oligo designed by Primer3.
This module contains a class and class methods to represent an oligo (e.g., designed by Primer3).
Oligos can represent single primer and/or internal probe designs.
Expand Down Expand Up @@ -88,20 +88,10 @@ class Oligo(OligoLike, Metric["Oligo"]):
The penalty for a primer is set by the combination of `PrimerAndAmpliconParameters` and
`PrimerWeights`, whereas a probe penalty is set by `ProbeParameters` and `ProbeWeights`.
The values for `self_any`, `self_any_th`, `self_end`, `self_end_th`, and `hairpin_th`
are emitted by Primer3 as part of oligo design. These attributes are optional to maintain
flexibility for reading in and writing `Oligo` objects, espeically when design settings are
inconsistent.
Attributes:
tm: the calculated melting temperature of the oligo
penalty: the penalty or score for the oligo
span: the mapping of the primer to the genome
self_any: probe self-complementarity, expressed as local alignment score
self_any_th: probe self-complementarity, expressed as melting temperature
self_end: 3' end complementarity, expressed as local alignment score
self_end_th: 3' end complementarity, expressed as melting temperature
hairpin_th: hairpin formation thermodynamics of the oligo as calculated by Primer3
bases: the base sequence of the oligo (excluding any tail)
tail: an optional tail sequence to put on the 5' end of the primer
name: an optional name to use for the primer
Expand All @@ -111,11 +101,6 @@ class Oligo(OligoLike, Metric["Oligo"]):
tm: float
penalty: float
span: Span
self_any: Optional[float] = None
self_any_th: Optional[float] = None
self_end: Optional[float] = None
self_end_th: Optional[float] = None
hairpin_th: Optional[float] = None
bases: Optional[str] = None
tail: Optional[str] = None

Expand Down
5 changes: 0 additions & 5 deletions prymer/api/oligo_like.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from abc import abstractmethod
from dataclasses import dataclass
from typing import Optional
from typing import TypeVar
from typing import assert_never

from fgpyo.sequence import gc_content
Expand Down Expand Up @@ -124,7 +123,3 @@ def _strand_to_location_string(self) -> str:
case _: # pragma: no cover
# Not calculating coverage on this line as it should be impossible to reach
assert_never(f"Encountered unhandled Strand value: {self.span.strand}")


OligoLikeType = TypeVar("OligoLikeType", bound=OligoLike)
"""Type variable for classes generic over `OligoLike` types."""
70 changes: 7 additions & 63 deletions prymer/api/primer.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,4 @@
"""
# Primer Class and Methods
This module contains a class and class methods to represent a primer (e.g. designed by Primer3)
Class attributes include the primer sequence, melting temperature, and the score of the primer. The
mapping of the primer to the genome is also stored.
Optional attributes include naming information and a tail sequence to attach to the 5' end of the
primer (if applicable).
## Examples of interacting with the `Primer` class
```python
>>> from prymer.api.span import Span, Strand
>>> primer_span = Span(refname="chr1", start=1, end=20)
>>> primer = Oligo(tm=70.0, penalty=-123.0, span=primer_span)
>>> primer.longest_hp_length()
0
>>> primer.length
20
>>> primer.name is None
True
>>> primer = Oligo(tm=70.0, penalty=-123.0, span=primer_span)
>>> primer.longest_hp_length()
0
>>> primer.untailed_length()
20
>>> primer.tailed_length()
20
>>> primer = primer.with_tail(tail="GATTACA")
>>> primer.untailed_length()
20
>>> primer.tailed_length()
27
>>> primer = primer.with_name(name="foobar")
>>> primer.name
'foobar'
```
Primers may also be written to a file and subsequently read back in, as the `Primer` class is an
`fgpyo` `Metric` class:
```python
>>> from pathlib import Path
>>> left_span = Span(refname="chr1", start=1, end=20)
>>> left = Oligo(tm=70.0, penalty=-123.0, span=left_span)
>>> right_span = Span(refname="chr1", start=101, end=120)
>>> right = Oligo(tm=70.0, penalty=-123.0, span=right_span)
>>> path = Path("/tmp/path/to/primers.txt")
>>> Oligo.write(path, left, right) # doctest: +SKIP
>>> primers = Oligo.read(path) # doctest: +SKIP
>>> list(primers) # doctest: +SKIP
[
Primer(tm=70.0, penalty=-123.0, span=amplicon_span, bases="G"*20),
Primer(tm=70.0, penalty=-123.0, span=amplicon_span, bases="T"*20)
]
```
"""
"""This module is deprecated - see prymer/api/oligo.py"""

import warnings
from dataclasses import dataclass
Expand All @@ -68,8 +8,12 @@

@dataclass(frozen=True, init=True, slots=True)
class Primer(Oligo):
"""A deprecated alias for `Oligo` intended to maintain backwards
compatibility with earlier releases of `prymer`."""
"""
A deprecated alias for `Oligo`.
This class exists to maintain backwards compatibility with earlier releases of `prymer`
and may be removed in a future version.
"""

warnings.warn(
"The Primer class was deprecated, use Oligo instead",
Expand Down
2 changes: 1 addition & 1 deletion prymer/api/primer_pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class methods to represent a primer pair. The primer pair is comprised of a lef
Span(refname='chr1', start=21, end=100, strand=<Strand.POSITIVE: '+'>)
>>> list(primer_pair)
[Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=1, end=20, strand=<Strand.POSITIVE: '+'>), self_any=None, self_any_th=None, self_end=None, self_end_th=None, hairpin_th=None, bases='GGGGGGGGGGGGGGGGGGGG', tail=None), Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=101, end=120, strand=<Strand.NEGATIVE: '-'>), self_any=None, self_any_th=None, self_end=None, self_end_th=None, hairpin_th=None, bases='TTTTTTTTTTTTTTTTTTTT', tail=None)]
[Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=1, end=20, strand=<Strand.POSITIVE: '+'>), bases='GGGGGGGGGGGGGGGGGGGG', tail=None), Oligo(name=None, tm=70.0, penalty=-123.0, span=Span(refname='chr1', start=101, end=120, strand=<Strand.NEGATIVE: '-'>), bases='TTTTTTTTTTTTTTTTTTTT', tail=None)]
```
""" # noqa: E501
Expand Down
17 changes: 7 additions & 10 deletions prymer/primer3/primer3.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
```
The `design_oligos()` method on `Primer3` is used to design the primers given a
The `design()` method on `Primer3` is used to design the primers given a
[`Primer3Input`][prymer.primer3.primer3_input.Primer3Input]. The latter includes all the
parameters and target region.
Expand All @@ -63,13 +63,13 @@
primer_and_amplicon_params=params, \
task=DesignLeftPrimersTask(), \
)
>>> left_result = designer.design_oligos(design_input=design_input)
>>> left_result = designer.design(design_input=design_input)
```
The `left_result` returns the [`Primer3Result`][prymer.primer3.primer3.Primer3Result]
container class. It contains two attributes:
1. `filtered_designs`: filtered and ordered (by objective function score) list of primer pairs or
1. `designs`: filtered and ordered (by objective function score) list of primer pairs or
single primers that were returned by Primer3.
2. `failures`: ordered list of [`Primer3Failures`][prymer.primer3.primer3.Primer3Failure]
detailing design failure reasons and corresponding count.
Expand All @@ -84,7 +84,7 @@
```
While`filtered_designs` attribute on `Primer3Result` may be used to access the list of primers or
While the `designs` attribute on `Primer3Result` may be used to access the list of primers or
primer pairs, it is more convenient to use the `primers()` and `primer_pairs()` methods
to return the designed primers or primer pairs (use the method corresponding to the input task) so
that the proper type is returned (i.e. [`Primer`][prymer.api.primer.Primer] or
Expand Down Expand Up @@ -343,7 +343,7 @@ def _screen_pair_results(
valid_primer_pair_designs.append(primer_pair)
return valid_primer_pair_designs, dinuc_pair_failures

def design_oligos(self, design_input: Primer3Input) -> Primer3Result: # noqa: C901
def design(self, design_input: Primer3Input) -> Primer3Result: # noqa: C901
"""Designs primers, primer pairs, and/or internal probes given a target region.
Args:
Expand Down Expand Up @@ -491,7 +491,7 @@ def _build_oligos(
Args:
design_input: the target region, design task, specifications, and scoring penalties
design_results: design results emitted by Primer3 and captured by design_oligos()
design_results: design results emitted by Primer3 and captured by design()
design_region: the padded design region
design_task: the design task
unmasked_design_seq: the reference sequence corresponding to the target region
Expand Down Expand Up @@ -537,9 +537,6 @@ def _build_oligos(
tm=float(design_results[f"PRIMER_{design_task.task_type}_{idx}_TM"]),
penalty=float(design_results[f"PRIMER_{design_task.task_type}_{idx}_PENALTY"]),
span=span,
self_any_th=float(design_results[f"{key}_SELF_ANY_TH"]),
self_end_th=float(design_results[f"{key}_SELF_END_TH"]),
hairpin_th=float(design_results[f"{key}_HAIRPIN_TH"]),
)
)
return primers
Expand Down Expand Up @@ -581,7 +578,7 @@ def _build_primer_pairs(
Args:
design_input: the target region, design task, specifications, and scoring penalties
design_results: design results emitted by Primer3 and captured by design_oligos()
design_results: design results emitted by Primer3 and captured by design()
design_region: the padded design region
unmasked_design_seq: the reference sequence corresponding to the target region
Expand Down
8 changes: 0 additions & 8 deletions prymer/primer3/primer3_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,6 @@
PRIMER_MAX_NS_ACCEPTED -> 1
PRIMER_LOWERCASE_MASKING -> 1
PRIMER_NUM_RETURN -> 5
PRIMER_MAX_SELF_ANY -> 8.0
PRIMER_MAX_SELF_ANY_TH -> 53.0
PRIMER_MAX_SELF_END -> 3.0
PRIMER_MAX_SELF_END_TH -> 53.0
PRIMER_MAX_HAIRPIN_TH -> 53.0
PRIMER_PAIR_WT_PRODUCT_SIZE_LT -> 1
PRIMER_PAIR_WT_PRODUCT_SIZE_GT -> 1
PRIMER_PAIR_WT_PRODUCT_TM_LT -> 0.0
Expand All @@ -90,9 +85,6 @@
PRIMER_WT_SIZE_GT -> 0.1
PRIMER_WT_TM_LT -> 1.0
PRIMER_WT_TM_GT -> 1.0
PRIMER_WT_SELF_ANY_TH -> 0.0
PRIMER_WT_SELF_END_TH -> 0.0
PRIMER_WT_HAIRPIN_TH -> 0.0
"""

from dataclasses import MISSING
Expand Down
Loading

0 comments on commit 932c823

Please sign in to comment.