bio-primer-design-primer-basics — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited bio-primer-design-primer-basics (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Reference examples tested with: BioPython 1.83+, pandas 2.2+, primer3-py 2.0+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
"Design primers for this sequence" → Given a template sequence and constraints (product size, Tm, GC%), find ranked primer pairs that amplify the target region.
primer3.design_primers() (primer3-py)primer3_core (Primer3)Design PCR primers using primer3-py, the Python binding for Primer3.
import primer3
from primer3 import p3helpers
from Bio import SeqIO
from Bio.Seq import Seq# Sanitize sequence (uppercase, remove whitespace)
raw_seq = ' atgc gatc GATC '
clean_seq = p3helpers.sanitize_sequence(raw_seq)
print(f'Cleaned: {clean_seq}') # 'ATGCGATCGATC'
# Reverse complement for designing reverse primers
seq = 'ATGCGATCGATC'
rc_seq = p3helpers.reverse_complement(seq)
print(f'Reverse complement: {rc_seq}') # 'GATCGATCGCAT'
# Ensure valid DNA sequence (ACGT only, uppercase)
valid_seq = p3helpers.ensure_acgt_uppercase('atgcNNgatc') # Raises error if invalidsequence = 'ATGCGTACGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCG'
result = primer3.design_primers(
seq_args={'SEQUENCE_TEMPLATE': sequence},
global_args={
'PRIMER_PRODUCT_SIZE_RANGE': [[100, 300]],
'PRIMER_MIN_TM': 57.0,
'PRIMER_OPT_TM': 60.0,
'PRIMER_MAX_TM': 63.0,
'PRIMER_MIN_GC': 40.0,
'PRIMER_MAX_GC': 60.0,
}
)num_returned = result['PRIMER_PAIR_NUM_RETURNED']
print(f'Found {num_returned} primer pairs')
for i in range(num_returned):
left = result[f'PRIMER_LEFT_{i}_SEQUENCE']
right = result[f'PRIMER_RIGHT_{i}_SEQUENCE']
left_tm = result[f'PRIMER_LEFT_{i}_TM']
right_tm = result[f'PRIMER_RIGHT_{i}_TM']
product_size = result[f'PRIMER_PAIR_{i}_PRODUCT_SIZE']
print(f'Pair {i}: {left} / {right}')
print(f' Tm: {left_tm:.1f}C / {right_tm:.1f}C, Product: {product_size}bp')# Target a specific region: [start, length]
result = primer3.design_primers(
seq_args={
'SEQUENCE_TEMPLATE': sequence,
'SEQUENCE_TARGET': [100, 50], # Target region at position 100, length 50
},
global_args={
'PRIMER_PRODUCT_SIZE_RANGE': [[150, 300]],
'PRIMER_OPT_TM': 60.0,
}
)# Primers must span this region (e.g., exon junction)
result = primer3.design_primers(
seq_args={
'SEQUENCE_TEMPLATE': sequence,
'SEQUENCE_INCLUDED_REGION': [50, 200], # Primers within this region
},
global_args={'PRIMER_PRODUCT_SIZE_RANGE': [[100, 250]]}
)# Exclude regions (e.g., SNP positions, repeats)
result = primer3.design_primers(
seq_args={
'SEQUENCE_TEMPLATE': sequence,
'SEQUENCE_EXCLUDED_REGION': [[150, 20], [300, 15]], # Regions to avoid
},
global_args={'PRIMER_PRODUCT_SIZE_RANGE': [[100, 300]]}
)# Force primer to overlap a specific position
result = primer3.design_primers(
seq_args={
'SEQUENCE_TEMPLATE': sequence,
'SEQUENCE_FORCE_LEFT_START': 50, # Left primer must start here
'SEQUENCE_FORCE_RIGHT_START': 250, # Right primer must start here
},
global_args={'PRIMER_PRODUCT_SIZE_RANGE': [[150, 250]]}
)# Single primer for sequencing
result = primer3.design_primers(
seq_args={'SEQUENCE_TEMPLATE': sequence},
global_args={
'PRIMER_PICK_LEFT_PRIMER': 1,
'PRIMER_PICK_RIGHT_PRIMER': 0, # Only design left primer
'PRIMER_PICK_INTERNAL_OLIGO': 0,
'PRIMER_OPT_SIZE': 20,
'PRIMER_MIN_SIZE': 18,
'PRIMER_MAX_SIZE': 25,
}
)result = primer3.design_primers(
seq_args={
'SEQUENCE_TEMPLATE': sequence,
'SEQUENCE_TARGET': [200, 50],
},
global_args={
'PRIMER_PRODUCT_SIZE_RANGE': [[150, 300], [300, 500]], # Multiple ranges
'PRIMER_NUM_RETURN': 5,
'PRIMER_MIN_SIZE': 18,
'PRIMER_OPT_SIZE': 20,
'PRIMER_MAX_SIZE': 25,
'PRIMER_MIN_TM': 57.0,
'PRIMER_OPT_TM': 60.0,
'PRIMER_MAX_TM': 63.0,
'PRIMER_MIN_GC': 40.0,
'PRIMER_OPT_GC_PERCENT': 50.0,
'PRIMER_MAX_GC': 60.0,
'PRIMER_MAX_POLY_X': 4, # Max consecutive identical bases
'PRIMER_MAX_NS_ACCEPTED': 0, # No ambiguous bases
'PRIMER_MAX_SELF_ANY': 8, # Self-complementarity
'PRIMER_MAX_SELF_END': 3, # 3' self-complementarity
'PRIMER_PAIR_MAX_COMPL_ANY': 8, # Pair complementarity
'PRIMER_PAIR_MAX_COMPL_END': 3, # Pair 3' complementarity
'PRIMER_MAX_END_STABILITY': 9.0, # Max 3' end stability (delta G)
}
)from Bio import SeqIO
record = SeqIO.read('gene.fasta', 'fasta')
sequence = str(record.seq)
result = primer3.design_primers(
seq_args={'SEQUENCE_TEMPLATE': sequence, 'SEQUENCE_ID': record.id},
global_args={'PRIMER_PRODUCT_SIZE_RANGE': [[100, 300]], 'PRIMER_OPT_TM': 60.0}
)# Calculate Tm for an existing primer
tm = primer3.calc_tm('ATGCGATCGATCGATCGATC')
print(f'Tm: {tm:.1f}C')
# With custom salt/DNA concentrations
tm = primer3.calc_tm('ATGCGATCGATCGATCGATC', mv_conc=50.0, dv_conc=1.5, dntp_conc=0.2, dna_conc=50.0)| Parameter | Default | Description |
|---|---|---|
| mv_conc | 50.0 mM | Monovalent cations (Na+, K+) |
| dv_conc | 0.0 mM | Divalent cations (Mg2+) |
| dntp_conc | 0.0 mM | dNTP concentration |
| dna_conc | 50.0 nM | DNA oligo concentration |
# Hairpin Tm
hairpin = primer3.calc_hairpin('ATGCGATCGATCGATCGATC')
print(f'Hairpin Tm: {hairpin.tm:.1f}C, dG: {hairpin.dg:.1f}')
# Homodimer Tm
homodimer = primer3.calc_homodimer('ATGCGATCGATCGATCGATC')
print(f'Homodimer Tm: {homodimer.tm:.1f}C, dG: {homodimer.dg:.1f}')
# Heterodimer Tm (between two different primers)
heterodimer = primer3.calc_heterodimer('ATGCGATCGATCGATCGATC', 'GCTAGCTAGCTAGCTAGCTA')
print(f'Heterodimer Tm: {heterodimer.tm:.1f}C, dG: {heterodimer.dg:.1f}')Goal: Convert primer3 results into a tabular format for comparison, filtering, or export.
Approach: Loop over returned pairs, extract sequence/Tm/GC/size/penalty for each, and build a DataFrame.
Reference (pandas 2.2+):
import pandas as pd
def primers_to_dataframe(result):
rows = []
for i in range(result['PRIMER_PAIR_NUM_RETURNED']):
rows.append({
'pair': i,
'left_seq': result[f'PRIMER_LEFT_{i}_SEQUENCE'],
'right_seq': result[f'PRIMER_RIGHT_{i}_SEQUENCE'],
'left_tm': result[f'PRIMER_LEFT_{i}_TM'],
'right_tm': result[f'PRIMER_RIGHT_{i}_TM'],
'left_gc': result[f'PRIMER_LEFT_{i}_GC_PERCENT'],
'right_gc': result[f'PRIMER_RIGHT_{i}_GC_PERCENT'],
'product_size': result[f'PRIMER_PAIR_{i}_PRODUCT_SIZE'],
'penalty': result[f'PRIMER_PAIR_{i}_PENALTY'],
})
return pd.DataFrame(rows)
df = primers_to_dataframe(result)
print(df)| Parameter | Description | Default |
|---|---|---|
| PRIMER_PRODUCT_SIZE_RANGE | Allowed product sizes | [[100,300]] |
| PRIMER_NUM_RETURN | Number of primer pairs | 5 |
| PRIMER_MIN/OPT/MAX_SIZE | Primer length | 18/20/27 |
| PRIMER_MIN/OPT/MAX_TM | Melting temperature | 57/60/63 |
| PRIMER_MIN/MAX_GC | GC content percent | 20/80 |
| PRIMER_MAX_POLY_X | Max poly-X run | 5 |
| PRIMER_MAX_SELF_ANY | Self complementarity | 8 |
| PRIMER_MAX_SELF_END | 3' self complementarity | 3 |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.