Repository navigation
Conversation
SELEX, Stockholm, CLUSTAL and PIR files written by writeMSA or MSAFile could not be read back, or came back with different labels or sequences: - parseSelex read every line at the column where the first sequence started, so labels of different lengths or longer than the padding were spliced into the sequences, and lines longer than the fixed buffer failed; it now splits each line into label and sequence, reads whole lines, joins blocks and checks that each block repeats the first one - writeSelex always leaves a space between label and sequence, no longer overflows its line buffer, and releases the label only after writing it - writeClustal and writePIR failed on Python 3 (range of a float) - MSAFile reads multi-block SELEX/Stockholm, CLUSTAL and PIR, and writes CLUSTAL and PIR; a Stockholm writer on a stream ends with // - parseClustal handles blocks, conservation lines, residue counts, other aligners' headers and labels with spaces; the CLUSTAL header is one Biopython reads - parsePIR takes the line after >P1; as the description and drops the terminal * - splitSeqLabel splits only a trailing /start-end from labels with / - labels are decoded as UTF-8 so the C and Python readers agree
satyaorz
force-pushed
the
fix/msa-write-parse-roundtrip
branch
from
October 10, 2026 15:21
c90dc0d to
fac8c8c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Disclosing up front: this was put together with help from Claude (Anthropic's coding agent). I reproduced and verified everything below before opening the PR.
writeMSAwrites SELEX, Stockholm, CLUSTAL and PIR files thatparseMSAcannot read back, or reads back with different labels or sequences. On Python 3 the CLUSTAL and PIR writers do not work at all. Before → after, writing a smallMSAand parsing it back:.alnTypeError: 'float' object cannot be interpreted as an integer(range(n/60)).aliTypeError.slxwith a label longer than 31 characters.sthwith a 500-character labelparseSelexfor some label sets.slxwith 12000 columnsIOError: failed to parse SELEX/Stockholm file at line 2(line longer than the fixed buffer)SELEX / Stockholm (
msaio.c)parseSelex. Previously it took the column where the first sequence starts and read every line at that column. That broke for labels of different lengths, labels longer than the padding, and lines longer thanSELEXLINELEN. Now each line is split into a label (everything before the last whitespace-separated item) and a sequence. Lines are read whole, growing the buffer as needed. Alignments split into blocks separated by blank lines are joined, and a block whose labels or row count differ from the first block's raisesIOError. Markup lines (#) are skipped, and nothing after//is read.writeSelex. It now always leaves at least one space between a label and its sequence. It also no longer writes labels longer than the padding into a fixed-size buffer, and the label's bytes are released only after they are written (they werePy_DECREFed before use).replace, so the C path andMSAFileagree on non-ASCII labels.Python readers and writers (
msafile.py)MSAFile. It now reads multi-block SELEX/Stockholm the same way as the C parser, and also reads CLUSTAL and PIR. It can write CLUSTAL (written onclose(), since CLUSTAL interleaves sequences) and PIR. A Stockholm writer on a stream now writes the closing//too.parseClustal.writeClustal. Pads labels to the longest one instead of a fixed 16 characters, and writes aCLUSTAL Wheader that Biopython reads.parsePIR. Takes the line after each>P1;line as the description, whatever its first field, and no longer keeps the terminal*as a residue.writePIR. Writes the full label by default, so it can be read back.splitSeqLabel. In a label containing/, only a trailing/start-endis split off, so labels likesp/inner/4-11orx/1/2keep their full identifier.Tests
prody/tests/sequence/test_msa_formats.py. It has round trips for all four formats throughwriteMSA/parseMSAand throughMSAFile, on plain and.gzfiles and on streams. It also reads fixtures written the way HMMER/Pfam, clustalw 1/2 and MODELLER write them, and checks that mismatched blocks raiseIOError. Onmasterthese tests fail, and the run segfaults inparseSelexpartway through.prody/tests/datafiles/msa_formats_*, which the existingdatafiles/msa*package-data pattern already covers.prody/tests/sequence: 254 passed. The one failure istestBuildMSAclustalw, which needs theclustalwbinary I don't have installed locally.AlignIOreads the Stockholm, CLUSTAL and PIR files written here, and ProDy reads Biopython's.