Skip to content

primer3_task

Primer3Task Class and Methods

This module contains a Primer3Task class to provide design-specific parameters to Primer3. The classes are primarily used in Primer3Input.

Primer3 can design single primers ("left" and "right") as well as primer pairs. The design task "type" dictates which type of primers to pick and informs the design region. These parameters are aligned to the correct Primer3 settings and fed directly into Primer3.

Four types of tasks are available:

  1. DesignPrimerPairsTask -- task for designing primer pairs.
  2. DesignLeftPrimersTask -- task for designing primers to the left (5') of the design region on the top/positive strand.
  3. DesignRightPrimersTask -- task for designing primers to the right (3') of the design region on the bottom/negative strand.
  4. PickHybProbeOnly -- task for designing an internal probe for hybridization-based technologies

The main purpose of these classes are to generate the Primer3InputTagss required by Primer3 for specifying how to design the primers, returned by the to_input_tags() method. The target and design region are provided to this method, where the target region is wholly contained within design region. This leaves a left and right primer region respectively, that are the two regions that remain after removing the wholly contained (inner) target regions.

Therefore, the left primers shall be designed from the start of the design region to the start of the target region, while right primers shall be designed from the end of the target region through to the end of the design region.

In addition to the to_input_tags() method, each Primer3TaskType provides a task_type and count_tag class property. The former is a TaskType enumeration that represents the type of design task, while the latter is the tag returned by Primer3 that provides the number of primers returned.

Examples

Suppose we have the following design and target regions:

>>> from prymer.api import Strand
>>> design_region = Span(refname="chr1", start=1, end=500, strand=Strand.POSITIVE)
>>> target = Span(refname="chr1", start=200, end=300, strand=Strand.POSITIVE)
>>> design_region.contains(target)
True

The task for designing primer pairs:

>>> task = DesignPrimerPairsTask()
>>> task.task_type.value
'PAIR'
>>> task.count_tag
'PRIMER_PAIR_NUM_RETURNED'
>>> [(tag.value, value) for tag, value in task.to_input_tags(target=target, design_region=design_region).items()]
[('PRIMER_TASK', 'generic'), ('PRIMER_PICK_LEFT_PRIMER', 1), ('PRIMER_PICK_RIGHT_PRIMER', 1), ('PRIMER_PICK_INTERNAL_OLIGO', 0), ('SEQUENCE_TARGET', '200,101')]

The tasks for designing left primers:

>>> task = DesignLeftPrimersTask()
>>> task.task_type.value
'LEFT'
>>> task.count_tag
'PRIMER_LEFT_NUM_RETURNED'
>>> [(tag.value, value) for tag, value in task.to_input_tags(target=target, design_region=design_region).items()]
[('PRIMER_TASK', 'pick_primer_list'), ('PRIMER_PICK_LEFT_PRIMER', 1), ('PRIMER_PICK_RIGHT_PRIMER', 0), ('PRIMER_PICK_INTERNAL_OLIGO', 0), ('SEQUENCE_INCLUDED_REGION', '1,199')]

The tasks for designing left primers:

>>> task = DesignRightPrimersTask()
>>> task.task_type.value
'RIGHT'
>>> task.count_tag
'PRIMER_RIGHT_NUM_RETURNED'
>>> [(tag.value, value) for tag, value in task.to_input_tags(target=target, design_region=design_region).items()]
[('PRIMER_TASK', 'pick_primer_list'), ('PRIMER_PICK_LEFT_PRIMER', 0), ('PRIMER_PICK_RIGHT_PRIMER', 1), ('PRIMER_PICK_INTERNAL_OLIGO', 0), ('SEQUENCE_INCLUDED_REGION', '300,200')]

Attributes

Primer3TaskType module-attribute

Primer3TaskType: TypeAlias = Union[
    "DesignPrimerPairsTask",
    "DesignLeftPrimersTask",
    "DesignRightPrimersTask",
    "PickHybProbeOnly",
]

Type alias for all Primer3Tasks, to enable exhaustiveness checking.

Classes

DesignLeftPrimersTask

Bases: Primer3Task

Stores task-specific characteristics for designing left primers.

Source code in prymer/primer3/primer3_task.py
class DesignLeftPrimersTask(Primer3Task, task_type=TaskType.LEFT):
    """Stores task-specific characteristics for designing left primers."""

    @classmethod
    def _to_input_tags(cls, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        return {
            Primer3InputTag.PRIMER_TASK: "pick_primer_list",
            Primer3InputTag.PRIMER_PICK_LEFT_PRIMER: 1,
            Primer3InputTag.PRIMER_PICK_RIGHT_PRIMER: 0,
            Primer3InputTag.PRIMER_PICK_INTERNAL_OLIGO: 0,
            Primer3InputTag.SEQUENCE_INCLUDED_REGION: f"1,{target.start - design_region.start}",
        }

    @property
    def requires_primer_amplicon_params(self) -> bool:
        return True

    @property
    def requires_probe_params(self) -> bool:
        return False

DesignPrimerPairsTask

Bases: Primer3Task

Stores task-specific Primer3 settings for designing primer pairs

Source code in prymer/primer3/primer3_task.py
class DesignPrimerPairsTask(Primer3Task, task_type=TaskType.PAIR):
    """Stores task-specific Primer3 settings for designing primer pairs"""

    @classmethod
    def _to_input_tags(cls, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        return {
            Primer3InputTag.PRIMER_TASK: "generic",
            Primer3InputTag.PRIMER_PICK_LEFT_PRIMER: 1,
            Primer3InputTag.PRIMER_PICK_RIGHT_PRIMER: 1,
            Primer3InputTag.PRIMER_PICK_INTERNAL_OLIGO: 0,
            Primer3InputTag.SEQUENCE_TARGET: f"{target.start - design_region.start + 1},"
            f"{target.length}",
        }

    @property
    def requires_primer_amplicon_params(self) -> bool:
        return True

    @property
    def requires_probe_params(self) -> bool:
        return False

DesignRightPrimersTask

Bases: Primer3Task

Stores task-specific characteristics for designing right primers

Source code in prymer/primer3/primer3_task.py
class DesignRightPrimersTask(Primer3Task, task_type=TaskType.RIGHT):
    """Stores task-specific characteristics for designing right primers"""

    @classmethod
    def _to_input_tags(cls, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        start = target.end - design_region.start + 1
        length = design_region.end - target.end
        return {
            Primer3InputTag.PRIMER_TASK: "pick_primer_list",
            Primer3InputTag.PRIMER_PICK_LEFT_PRIMER: 0,
            Primer3InputTag.PRIMER_PICK_RIGHT_PRIMER: 1,
            Primer3InputTag.PRIMER_PICK_INTERNAL_OLIGO: 0,
            Primer3InputTag.SEQUENCE_INCLUDED_REGION: f"{start},{length}",
        }

    @property
    def requires_primer_amplicon_params(self) -> bool:
        return True

    @property
    def requires_probe_params(self) -> bool:
        return False

PickHybProbeOnly

Bases: Primer3Task

Stores task-specific characteristics for designing an internal hybridization probe.

Source code in prymer/primer3/primer3_task.py
class PickHybProbeOnly(Primer3Task, task_type=TaskType.INTERNAL):
    """Stores task-specific characteristics for designing an internal hybridization probe."""

    @classmethod
    def _to_input_tags(cls, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        return {
            Primer3InputTag.PRIMER_TASK: "generic",
            Primer3InputTag.PRIMER_PICK_LEFT_PRIMER: 0,
            Primer3InputTag.PRIMER_PICK_RIGHT_PRIMER: 0,
            Primer3InputTag.PRIMER_PICK_INTERNAL_OLIGO: 1,
        }

    @property
    def requires_primer_amplicon_params(self) -> bool:
        return False

    @property
    def requires_probe_params(self) -> bool:
        return True

Primer3Task

Bases: ABC

Abstract base class from which the other classes derive.

Source code in prymer/primer3/primer3_task.py
class Primer3Task(ABC):
    """Abstract base class from which the other classes derive."""

    @final
    def to_input_tags(self, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        """Returns the input tags specific to this type of task for Primer3.

        Ensures target region is wholly contained within design region. Subclass specific
        implementation aligns the set of input parameters specific to primer pair or
        single primer design.

        This implementation mimics the Template method, a Behavioral Design Pattern in which the
        abstract base class contains a rough skeleton of methods and the derived subclasses
        implement the details of those methods. In this case, each of the derived subclasses
        will first use the base class `to_input_tags()` method to check that the target region is
        wholly contained within the design region. If so, they will implement task-specific logic
        for the `to_input_tags()` method.

        Args:
            target: the target region (to be amplified)
            design_region: the design region, which wholly contains the target region, in which
                primers are to be designed


        Raises:
            ValueError: if the target region is not contained within the design region

        Returns:
            The input tags for Primer3
        """
        if not design_region.contains(target):
            raise ValueError(
                "Target not contained within design region: "
                f"target:{target.__str__()},"
                f"design_region: {design_region.__str__()}"
            )

        return self._to_input_tags(target=target, design_region=design_region)

    task_type: ClassVar[TaskType] = NotImplemented
    """Tracks task type for downstream analysis"""

    count_tag: ClassVar[str] = NotImplemented
    """The tag returned by Primer3 that provides the number of primers returned"""

    @classmethod
    @abstractmethod
    def _to_input_tags(cls, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
        """Aligns the set of input parameters specific to primer pair or single primer design"""

    @classmethod
    def __init_subclass__(cls, task_type: TaskType, **kwargs: Any) -> None:
        # See: https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__
        super().__init_subclass__(**kwargs)

        cls.task_type = task_type
        cls.count_tag = f"PRIMER_{task_type}_NUM_RETURNED"

Attributes

count_tag class-attribute
count_tag: str = NotImplemented

The tag returned by Primer3 that provides the number of primers returned

task_type class-attribute
task_type: TaskType = NotImplemented

Tracks task type for downstream analysis

Functions

to_input_tags
to_input_tags(
    target: Span, design_region: Span
) -> dict[Primer3InputTag, Any]

Returns the input tags specific to this type of task for Primer3.

Ensures target region is wholly contained within design region. Subclass specific implementation aligns the set of input parameters specific to primer pair or single primer design.

This implementation mimics the Template method, a Behavioral Design Pattern in which the abstract base class contains a rough skeleton of methods and the derived subclasses implement the details of those methods. In this case, each of the derived subclasses will first use the base class to_input_tags() method to check that the target region is wholly contained within the design region. If so, they will implement task-specific logic for the to_input_tags() method.

Parameters:

Name Type Description Default
target Span

the target region (to be amplified)

required
design_region Span

the design region, which wholly contains the target region, in which primers are to be designed

required

Raises:

Type Description
ValueError

if the target region is not contained within the design region

Returns:

Type Description
dict[Primer3InputTag, Any]

The input tags for Primer3

Source code in prymer/primer3/primer3_task.py
@final
def to_input_tags(self, target: Span, design_region: Span) -> dict[Primer3InputTag, Any]:
    """Returns the input tags specific to this type of task for Primer3.

    Ensures target region is wholly contained within design region. Subclass specific
    implementation aligns the set of input parameters specific to primer pair or
    single primer design.

    This implementation mimics the Template method, a Behavioral Design Pattern in which the
    abstract base class contains a rough skeleton of methods and the derived subclasses
    implement the details of those methods. In this case, each of the derived subclasses
    will first use the base class `to_input_tags()` method to check that the target region is
    wholly contained within the design region. If so, they will implement task-specific logic
    for the `to_input_tags()` method.

    Args:
        target: the target region (to be amplified)
        design_region: the design region, which wholly contains the target region, in which
            primers are to be designed


    Raises:
        ValueError: if the target region is not contained within the design region

    Returns:
        The input tags for Primer3
    """
    if not design_region.contains(target):
        raise ValueError(
            "Target not contained within design region: "
            f"target:{target.__str__()},"
            f"design_region: {design_region.__str__()}"
        )

    return self._to_input_tags(target=target, design_region=design_region)

TaskType

Bases: UppercaseStrEnum

Represents the type of design task: design primer pairs, individual primers (left or right), or an internal hybridization probe.

Source code in prymer/primer3/primer3_task.py
@unique
class TaskType(UppercaseStrEnum):
    """Represents the type of design task: design primer pairs, individual primers
    (left or right), or an internal hybridization probe."""

    # Developer Note: the names of this enum are important, as they are used as-is for the
    # count_tag in `Primer3Task`.

    PAIR = auto()
    LEFT = auto()
    RIGHT = auto()
    INTERNAL = auto()