From ad2f5e6e6788fe6d73895aff5c310e834d4b9a7f Mon Sep 17 00:00:00 2001 From: Susanna Marquez Date: Sun, 25 Jan 2026 15:01:21 +0100 Subject: [PATCH 1/3] change to dev version --- NEWS.rst | 4 ++++ presto/Version.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 055ce31..9d1cdf8 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,6 +1,10 @@ Release Notes ================================================================================ +Version 0.7.8dev: Unreleased +------------------------------------------------------------------------------- + + Version 0.7.7: January 19, 2026 ------------------------------------------------------------------------------- diff --git a/presto/Version.py b/presto/Version.py index 3f3402b..e25a673 100644 --- a/presto/Version.py +++ b/presto/Version.py @@ -5,5 +5,5 @@ __author__ = 'Jason Anthony Vander Heiden' __copyright__ = 'Copyright 2026 Kleinstein Lab, Yale University. All rights reserved.' __license__ = 'GNU Affero General Public License 3 (AGPL-3)' -__version__ = '0.7.7' -__date__ = '2026.01.19' +__version__ = '0.7.8dev' +__date__ = '2026.01.25' From d0c921f63a089345fd1a4487a663cd340a4fdb4d Mon Sep 17 00:00:00 2001 From: Susanna Marquez Date: Sun, 25 Jan 2026 15:03:02 +0100 Subject: [PATCH 2/3] suggested fix for PairSeq mismatch gz index=True ignoring key_func --- presto/IO.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/presto/IO.py b/presto/IO.py index a913782..67c4d71 100644 --- a/presto/IO.py +++ b/presto/IO.py @@ -124,7 +124,11 @@ def readSeqFile(seq_file, index=False, key_func=None): # For gzip files, we can't use SeqIO.index directly, so we'll read into memory. # SeqIO.index() cannot index regular .gz files, only BGZF format. with openFile(seq_file, 'r') as handle: - seq_records = {rec.id: rec for rec in SeqIO.parse(handle, seq_type)} + # Use key_func if possible for consistent behavior with non-.gz indexing + if key_func is None: + seq_records = {rec.id: rec for rec in SeqIO.parse(handle, seq_type)} + else: + seq_records = {key_func(rec.id): rec for rec in SeqIO.parse(handle, seq_type)} else: seq_records = SeqIO.index(seq_file, seq_type, key_function=key_func) else: From 6c1523b382dd724fa2f5d2a0da1293b8725c9b7e Mon Sep 17 00:00:00 2001 From: Susanna Marquez Date: Sun, 25 Jan 2026 16:24:45 +0100 Subject: [PATCH 3/3] added test --- tests/data/test_presto_R1.fastq.gz | Bin 0 -> 150 bytes tests/data/test_presto_R2.fastq.gz | Bin 0 -> 155 bytes tests/test_IO.py | 59 +++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/data/test_presto_R1.fastq.gz create mode 100644 tests/data/test_presto_R2.fastq.gz diff --git a/tests/data/test_presto_R1.fastq.gz b/tests/data/test_presto_R1.fastq.gz new file mode 100644 index 0000000000000000000000000000000000000000..7de3f8ee551582cb9ee0f15d8ba15e5218302af1 GIT binary patch literal 150 zcmV;H0BQdpiwFpbF?MMH19W9`bYF0CWpi|IUs5qHW?^%5aR74&b`1=5^>cPLFfgof z_V){R_7C+7u{Ej*2=erG4YKu2cZ@gHEhx&&O)Ub7xdpif+8XK^a5;uJyJN##+FYJE z0G9*8E+Ztnj1hLZW7uV&XKBji?i}KX4HIdZF_LA52+Mr2dMv~dJH%%f0MY|#)I|XR E0KA|*j{pDw literal 0 HcmV?d00001 diff --git a/tests/data/test_presto_R2.fastq.gz b/tests/data/test_presto_R2.fastq.gz new file mode 100644 index 0000000000000000000000000000000000000000..da627b36910436fd3179bd4b1ec29fa7d9a943e3 GIT binary patch literal 155 zcmV;M0A&9kiwFp!F?MMH19W9`bYF0CWpi|IUs5tIW?^%5aR74&b`1=5^>cPLFfgof z_V){R_7C+7u{Ej*2=erG4YKu2cZ@gIEhx&&O)Ub7xdpif+8XK^a5=j>hJ-kRNL0+F z&E<&$a5*6CGDfn?5Mh@uR=XTSutA(QA=_hwWREez9(N3T4D>8bK^D0?W5IYl1puC5 Jtt3DJ008BPJ*WTx literal 0 HcmV?d00001 diff --git a/tests/test_IO.py b/tests/test_IO.py index 71e1cb0..09a92f3 100644 --- a/tests/test_IO.py +++ b/tests/test_IO.py @@ -140,6 +140,65 @@ def test_gzip_output_functionality(self): self.assertEqual(len(records), 1) print(f'Successfully read back record: {records[0].id}') + #@unittest.skip('-> test_readSeqFile_gzipped_with_key_func() skipped\n') + def test_readSeqFile_gzipped_with_key_func(self): + """ + Test for issue #108: readSeqFile with .gz + index=True should respect key_func + + When using .gz FASTQ with index=True, readSeqFile should use key_func + to create keys instead of using rec.id directly. This is essential for + PairSeq --coord presto to work with gzipped consensus files. + + Issue: https://github.com/immcantation/presto/issues/108 + """ + # Test files with presto annotations (ID|CONSCOUNT=X|...) + presto_r1 = os.path.join(data_path, 'test_presto_R1.fastq.gz') + presto_r2 = os.path.join(data_path, 'test_presto_R2.fastq.gz') + + if not os.path.exists(presto_r1) or not os.path.exists(presto_r2): + self.skipTest(f"Test files not found: {presto_r1} or {presto_r2}") + + # Import getCoordKey to test with presto coordinate extraction + from presto.Annotation import getCoordKey + + # Define key function that extracts presto ID (strips annotations after |) + def presto_key_func(header): + return getCoordKey(header, coord_type='presto') + + # Read R1 file with index=True and key_func + r1_dict = readSeqFile(presto_r1, index=True, key_func=presto_key_func) + print(f'R1 sequences indexed: {len(r1_dict)}') + print(f'R1 keys: {list(r1_dict.keys())}') + + # Read R2 file with index=True and key_func + r2_dict = readSeqFile(presto_r2, index=True, key_func=presto_key_func) + print(f'R2 sequences indexed: {len(r2_dict)}') + print(f'R2 keys: {list(r2_dict.keys())}') + + # Verify that keys are clean IDs without annotations + # (not full headers like "SEQUENCE001|CONSCOUNT=2|...") + for key in r1_dict.keys(): + self.assertNotIn('|', key, + f"Key should not contain annotations: {key}") + self.assertNotIn('CONSCOUNT', key, + f"Key should not contain CONSCOUNT: {key}") + + # Verify that both R1 and R2 have the same keys (pairable sequences) + self.assertEqual(set(r1_dict.keys()), set(r2_dict.keys()), + "R1 and R2 should have matching keys when using presto key_func") + + # Expected keys based on our test data + expected_keys = {'SEQUENCE001', 'SEQUENCE002', 'SEQUENCE003'} + self.assertEqual(set(r1_dict.keys()), expected_keys, + f"Keys should be clean sequence IDs: {expected_keys}") + + # Verify the original full headers are preserved in the records + for key, record in r1_dict.items(): + self.assertIn('|', record.id, + f"Original record.id should contain full header with annotations") + self.assertTrue(record.id.startswith(key), + f"Record ID should start with the key: {record.id} vs {key}") + if __name__ == '__main__': unittest.main() \ No newline at end of file