Fix fasta preview rendering for sequences larger than 100 KB

Sequence.display_data set the text/plain content-type only on the
non-truncated preview branch. Sequences larger than the 100 KB peek
limit were returned without a content-type, so the browser fell back to
text/html: newlines collapsed and the header line ran into the sequence
(the "horrible" fasta preview). Serve text/plain for both branches.

Also stop dropping the final byte of small previews -- the read-ahead
byte that chunk[:-1] removes is only present when the content is
truncated.

Fixes #22719
This commit is contained in:
mvdbeek
2026-07-23 14:55:27 +02:00
parent 7ba6f9f751
commit 0ce09ceb66
2 changed files with 22 additions and 5 deletions
+6 -5
View File
@@ -336,12 +336,13 @@ class Sequence(data.Text):
chunk = fh.read(max_peek_size + 1)
except UnicodeDecodeError:
raise InvalidFileFormatError("Dataset appears to contain binary data, cannot display.")
# Always serve as text/plain so the browser preserves whitespace/newlines
# and does not interpret the content as HTML.
self._clean_and_set_mime_type(trans, "text/plain", headers)
if len(chunk) <= max_peek_size:
mime = "text/plain"
self._clean_and_set_mime_type(trans, mime, headers)
return chunk[:-1], headers
headers["x-content-truncated"] = max_peek_size
return util.unicodify(chunk[:-1]), headers
return chunk, headers
headers["x-content-truncated"] = str(max_peek_size)
return util.unicodify(chunk[:max_peek_size]), headers
else:
return super().display_data(trans, dataset, preview, filename, to_ext, **kwd)
+16
View File
@@ -355,6 +355,22 @@ class TestDatasetsApi(ApiTestCase):
assert content_type.startswith("text/plain"), content_type
assert display_response.text == contents
def test_display_preview_large_fasta_uses_text_plain(self, history_id):
# Regression test for https://github.com/galaxyproject/galaxy/issues/22719
# A fasta larger than the 100 KB preview limit must still be served as
# text/plain so the browser preserves newlines; otherwise the header line
# runs into the sequence and the content is rendered as HTML.
header = ">seq0 large fasta regression test\n"
contents = header + "".join(f"{'ACGT' * 20}\n" for _ in range(2000))
hda1 = self.dataset_populator.new_dataset(history_id, content=contents, file_type="fasta", wait=True)
display_response = self._get(f"histories/{history_id}/contents/{hda1['id']}/display", {"preview": "True"})
self._assert_status_code_is(display_response, 200)
content_type = display_response.headers.get("content-type", "")
assert content_type.startswith("text/plain"), content_type
assert display_response.headers.get("x-content-truncated") == "100000"
assert display_response.text.startswith(header)
assert len(display_response.text) <= 100000
def test_display_extra_paths(self, history_id: str):
test_data_resolver = TestDataResolver()
with open(test_data_resolver.get_filename("1.fasta")) as fh: