From 5795692d804ec6acfd22c51e366f3d35ea9c3558 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Thu, 15 Feb 2024 11:17:09 -0500 Subject: [PATCH] Fix `_ThermoAnalysis._set_globals_and_seq_args` for improper checks on `misprime_lib` and `mishyb_lib` leading to incorrect initialization of `mp_lib` and `mh_lib` Test added in `test_primerdesign.py` to verify the timing of the fix. --- CHANGES | 5 +++++ primer3/__init__.py | 4 ++-- primer3/src/libprimer3/libprimer3flex.c | 3 +++ primer3/thermoanalysis.pyx | 29 +++++++++++++------------ tests/test_primerdesign.py | 27 +++++++++++++++++++++-- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index 85a059e..4eba3f0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ # Changelog +## Version 2.0.3 (February 15, 2024) + +- Fix `_ThermoAnalysis._set_globals_and_seq_args` for improper checks on `misprime_lib` and +`mishyb_lib` leading to incorrect initialization of `mp_lib` and `mh_lib`. See issue #133 + ## Version 2.0.2 (February 9, 2024) - Support for python 3.12 (build, test, docs) diff --git a/primer3/__init__.py b/primer3/__init__.py index f2b1456..8bc27b4 100644 --- a/primer3/__init__.py +++ b/primer3/__init__.py @@ -26,10 +26,10 @@ from typing import List # Per PEP-440 https://peps.python.org/pep-0440/#public-version-identifiers -__version__ = '2.0.2' +__version__ = '2.0.3' __author__ = 'Ben Pruitt, Nick Conway' __copyright__ = ( - 'Copyright 2014-2023, Ben Pruitt & Nick Conway; 2014-2018 Wyss Institute' + 'Copyright 2014-2024, Ben Pruitt & Nick Conway; 2014-2018 Wyss Institute' ) __license__ = 'GPLv2' DESCRIPTION = 'Python bindings for Primer3' diff --git a/primer3/src/libprimer3/libprimer3flex.c b/primer3/src/libprimer3/libprimer3flex.c index dc26e1c..2fe5fed 100644 --- a/primer3/src/libprimer3/libprimer3flex.c +++ b/primer3/src/libprimer3/libprimer3flex.c @@ -300,6 +300,9 @@ pr_set_default_global_args_1( memset(a, 0, sizeof(p3_global_settings)); /* Arguments for primers ================================= */ + a->p_args.repeat_lib = NULL; + a->o_args.repeat_lib = NULL; + a->p_args.opt_size = 20; a->p_args.min_size = 18; a->p_args.max_size = 27; diff --git a/primer3/thermoanalysis.pyx b/primer3/thermoanalysis.pyx index e773761..c3d1668 100644 --- a/primer3/thermoanalysis.pyx +++ b/primer3/thermoanalysis.pyx @@ -1213,23 +1213,24 @@ cdef class _ThermoAnalysis: err_msg = '' try: global_settings_data = self.global_settings_data - if misprime_lib != None: - mp_lib = pdh_create_seq_lib(misprime_lib) - if mp_lib == NULL: - err_msg = f'Issue creating misprime_lib {misprime_lib}' - raise ValueError( - f'Issue creating misprime_lib {misprime_lib}' - ) - global_settings_data[0].p_args.repeat_lib = mp_lib + if misprime_lib is None: + misprime_lib = {} + + mp_lib = pdh_create_seq_lib(misprime_lib) + + global_settings_data[0].p_args.repeat_lib = mp_lib + + if mishyb_lib is None: + mishyb_lib = {} + + mh_lib = pdh_create_seq_lib(mishyb_lib) + + global_settings_data[0].o_args.repeat_lib = mh_lib - if mishyb_lib != None: - mh_lib = pdh_create_seq_lib(mishyb_lib) - if mh_lib == NULL: - err_msg = f'Issue creating mishyb_lib: {mishyb_lib}' - raise ValueError(err_msg) - global_settings_data[0].o_args.repeat_lib = mh_lib except (OSError, TypeError) as exc: + # Caught the exception, now raise a new one from the original + # post cleanup p3_destroy_global_settings( self.global_settings_data ) diff --git a/tests/test_primerdesign.py b/tests/test_primerdesign.py index 98aaf76..ad9d5e5 100644 --- a/tests/test_primerdesign.py +++ b/tests/test_primerdesign.py @@ -27,8 +27,8 @@ import os import random import sys +import time import unittest -from time import sleep from typing import ( Any, Dict, @@ -431,7 +431,7 @@ def test_memory_leaks(self): ], }, ) - sleep(0.1) # Pause for any GC + time.sleep(0.1) # Pause for any GC em = _get_mem_usage() print( f'\n\tMemory usage before {run_count} runs of design_primers: {sm}', @@ -555,6 +555,29 @@ def test_misprime_lib_mishyb_lib(self): self.assertEqual(result['PRIMER_INTERNAL'][0]['COORDS'], [69, 24]) self.assertEqual(len(result['PRIMER_INTERNAL']), 5) + # Test timing of this function of empty dictionaries compared to None + t1 = time.time() + bindings.design_primers( + seq_args=seq_args, + global_args=global_args, + misprime_lib={}, + mishyb_lib={}, + ) + dt1 = time.time() - t1 + + t2 = time.time() + bindings.design_primers( + seq_args=seq_args, + global_args=global_args, + misprime_lib=None, + mishyb_lib=None, + ) + dt2 = time.time() - t2 + # If the difference is less than 1 second, we are good + # Should be way less than 1 second but we are being conservative for + # the test in the cloud + assert abs(dt1 - dt2) < 1.0 + def test_PRIMER_SECONDARY_STRUCTURE_ALIGNMENT(self): '''Ensure all result pointers are initialized to NULL. '''