Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Release Notes
================================================================================

Version 0.7.8dev: Unreleased
-------------------------------------------------------------------------------


Version 0.7.7: January 19, 2026
-------------------------------------------------------------------------------

Expand Down
6 changes: 5 additions & 1 deletion presto/IO.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions presto/Version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Binary file added tests/data/test_presto_R1.fastq.gz
Binary file not shown.
Binary file added tests/data/test_presto_R2.fastq.gz
Binary file not shown.
59 changes: 59 additions & 0 deletions tests/test_IO.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()