From 0702747277608257d722ad60afdda5f4010d1d16 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 11:29:46 +0200 Subject: [PATCH 1/8] extend tests to py2.7 --- .github/workflows/qiita-plugin-ci.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/qiita-plugin-ci.yml b/.github/workflows/qiita-plugin-ci.yml index b88f397..693b780 100644 --- a/.github/workflows/qiita-plugin-ci.yml +++ b/.github/workflows/qiita-plugin-ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.5", "3.6", "3.9", "3.10", "3.11", "3.12"] + python-version: ["2.7", "3.5", "3.6", "3.9", "3.10", "3.11", "3.12"] steps: # Downloads a copy of the code in your repository before running CI tests - name: Check out repository code diff --git a/setup.py b/setup.py index 2580547..cd49f1b 100644 --- a/setup.py +++ b/setup.py @@ -18,6 +18,7 @@ Topic :: Software Development :: Libraries :: Application Frameworks Topic :: Software Development :: Libraries :: Python Modules Programming Language :: Python + Programming Language :: Python :: 2.7 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.9 From e7d27a8b3c095c29c91ed655a90da8c1ab4e365f Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 11:34:16 +0200 Subject: [PATCH 2/8] also remove --cov for old py27 --- .github/workflows/qiita-plugin-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qiita-plugin-ci.yml b/.github/workflows/qiita-plugin-ci.yml index 693b780..952def6 100644 --- a/.github/workflows/qiita-plugin-ci.yml +++ b/.github/workflows/qiita-plugin-ci.yml @@ -35,7 +35,7 @@ jobs: shell: bash -l {0} run: | conda activate qiita-files - pytest --doctest-modules --cov=qiita_files `if (( $(echo "${{ matrix.python-version }} > 3.6" | bc -l) )); then echo "--cov-report=lcov"; else echo ""; fi` + pytest --doctest-modules `if (( $(echo "${{ matrix.python-version }} > 3.6" | bc -l) )); then echo "--cov-report=lcov --cov=qiita_files"; else echo ""; fi` lint: runs-on: ubuntu-latest From a8c35aa6765275dbbcbac4335aa23fad91f249cc Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 11:39:21 +0200 Subject: [PATCH 3/8] fix import for py27 --- qiita_files/parse/fastq.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qiita_files/parse/fastq.py b/qiita_files/parse/fastq.py index 3626da4..15b2d0d 100644 --- a/qiita_files/parse/fastq.py +++ b/qiita_files/parse/fastq.py @@ -8,7 +8,10 @@ import numpy as np from qiita_files.util import open_file -from itertools import zip_longest +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest def _ascii_to_phred(s, offset): From c99471e21d5dbf2f852a585c45376422ebb60142 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 11:55:55 +0200 Subject: [PATCH 4/8] try to fix syntax for py27 --- qiita_files/parse/tests/test_fastq.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qiita_files/parse/tests/test_fastq.py b/qiita_files/parse/tests/test_fastq.py index a331260..2e9ef40 100644 --- a/qiita_files/parse/tests/test_fastq.py +++ b/qiita_files/parse/tests/test_fastq.py @@ -155,7 +155,7 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): } -FASTQ_EXAMPLE = rb"""@GAPC_0015:6:1:1259:10413#0/1 +FASTQ_EXAMPLE = str(r"""@GAPC_0015:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @@ -194,10 +194,10 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): @GAPC_0015:6:1:1317:3403#0/1 TTGTTTCCACTTGGTTGATTTCACCCCTGAGTTTG +GAPC_0015:6:1:1317:3403#0/1 -\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""" +\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""") -FASTQ_EXAMPLE_2 = rb"""@GAPC_0017:6:1:1259:10413#0/1 +FASTQ_EXAMPLE_2 = str(r"""@GAPC_0017:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @@ -205,15 +205,15 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): TATGTATATATAACATATACATATATACATACATA +GAPC_0015:6:1:1283:11957#0/1 ]KZ[PY]_[YY^```ac^\\`bT``c`\aT``bbb -""" +""") -FASTQ_EXAMPLE_3 = rb"""@GAPC_0017:6:1:1259:10413#0/1 +FASTQ_EXAMPLE_3 = str(r"""@GAPC_0017:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @GAPC_0015:6:1:1283:11957#0/1 -""" +""") if __name__ == "__main__": From 195cdb16b0e2216488d9bcd61637b9fa9f544287 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 16:07:01 +0200 Subject: [PATCH 5/8] some additional work to function with old python 2.7 and also slightly more modern python 3.x --- qiita_files/parse/fastq.py | 2 +- qiita_files/parse/tests/test_fastq.py | 37 +++++++++++++++------------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/qiita_files/parse/fastq.py b/qiita_files/parse/fastq.py index 15b2d0d..1b4d2a9 100644 --- a/qiita_files/parse/fastq.py +++ b/qiita_files/parse/fastq.py @@ -11,7 +11,7 @@ try: from itertools import zip_longest except ImportError: - from itertools import izip_longest + from itertools import izip_longest as zip_longest def _ascii_to_phred(s, offset): diff --git a/qiita_files/parse/tests/test_fastq.py b/qiita_files/parse/tests/test_fastq.py index 2e9ef40..887b792 100644 --- a/qiita_files/parse/tests/test_fastq.py +++ b/qiita_files/parse/tests/test_fastq.py @@ -17,9 +17,9 @@ class IterableData(object): def setUp(self): """ Initialize variables to be used by the tests as lists of strings""" - self.FASTQ_EXAMPLE = FASTQ_EXAMPLE.split(b'\n') - self.FASTQ_EXAMPLE_2 = FASTQ_EXAMPLE_2.split(b'\n') - self.FASTQ_EXAMPLE_3 = FASTQ_EXAMPLE_3.split(b'\n') + self.FASTQ_EXAMPLE = FASTQ_EXAMPLE.split('\n') + self.FASTQ_EXAMPLE_2 = FASTQ_EXAMPLE_2.split('\n') + self.FASTQ_EXAMPLE_3 = FASTQ_EXAMPLE_3.split('\n') class FileData(object): @@ -30,7 +30,7 @@ def setUp(self): ('FASTQ_EXAMPLE_2', FASTQ_EXAMPLE_2), ('FASTQ_EXAMPLE_3', FASTQ_EXAMPLE_3)]: tmp_file = tempfile.NamedTemporaryFile('wb') - tmp_file.write(val) + tmp_file.write(val.encode("utf-8")) tmp_file.flush() tmp_file.seek(0) setattr(self, attr, tmp_file.name) @@ -44,7 +44,7 @@ def tearDown(self): class ParseFastqTests(object): def test_parse(self): - for label, seq, qual in parse_fastq(self.FASTQ_EXAMPLE, + for label, seq, qual in parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), phred_offset=64): self.assertTrue(label in DATA) self.assertEqual(seq, DATA[label]["seq"]) @@ -52,7 +52,7 @@ def test_parse(self): # Make sure that enforce_qual_range set to False allows qual scores # to fall outside the typically acceptable range of 0-62 - for label, seq, qual in parse_fastq(self.FASTQ_EXAMPLE_2, + for label, seq, qual in parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_2), phred_offset=33, enforce_qual_range=False): self.assertTrue(label in DATA_2) @@ -63,18 +63,18 @@ def test_parse(self): # intended to be interpreted with an offset of 64, and using 33 will # make the qual score fall outside the acceptable range of 0-62. with self.assertRaises(ValueError): - list(parse_fastq(self.FASTQ_EXAMPLE, phred_offset=33)) + list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), phred_offset=33)) def test_parse_error(self): with self.assertRaises(ValueError): - list(parse_fastq(self.FASTQ_EXAMPLE_2, strict=True)) + list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_2), strict=True)) with self.assertRaises(ValueError): - list(parse_fastq(self.FASTQ_EXAMPLE_3, phred_offset=64)) + list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_3), phred_offset=64)) def test_invalid_phred_offset(self): with self.assertRaises(ValueError): - list(parse_fastq(self.FASTQ_EXAMPLE, phred_offset=42)) + list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), phred_offset=42)) class ParseFastqTestsInputIsIterable(IterableData, ParseFastqTests, TestCase): @@ -154,8 +154,13 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): 64, 51, 63, 63, 65, 65, 65])) } +def list_str2bin(inp): + if isinstance(inp, list): + return list(map(lambda x: x.encode("utf-8"), inp)) + return inp -FASTQ_EXAMPLE = str(r"""@GAPC_0015:6:1:1259:10413#0/1 + +FASTQ_EXAMPLE = r"""@GAPC_0015:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @@ -194,10 +199,10 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): @GAPC_0015:6:1:1317:3403#0/1 TTGTTTCCACTTGGTTGATTTCACCCCTGAGTTTG +GAPC_0015:6:1:1317:3403#0/1 -\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""") +\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""" -FASTQ_EXAMPLE_2 = str(r"""@GAPC_0017:6:1:1259:10413#0/1 +FASTQ_EXAMPLE_2 = r"""@GAPC_0017:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @@ -205,15 +210,15 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): TATGTATATATAACATATACATATATACATACATA +GAPC_0015:6:1:1283:11957#0/1 ]KZ[PY]_[YY^```ac^\\`bT``c`\aT``bbb -""") +""" -FASTQ_EXAMPLE_3 = str(r"""@GAPC_0017:6:1:1259:10413#0/1 +FASTQ_EXAMPLE_3 = r"""@GAPC_0017:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @GAPC_0015:6:1:1283:11957#0/1 -""") +""" if __name__ == "__main__": From 581dd3791a4e807c7de0005baa702cb308f6a9f4 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 16:24:33 +0200 Subject: [PATCH 6/8] more elegant conversion --- qiita_files/parse/tests/test_fastq.py | 31 ++++++++---------- qiita_files/parse/tests/test_iterator.py | 41 ++++++++++++------------ 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/qiita_files/parse/tests/test_fastq.py b/qiita_files/parse/tests/test_fastq.py index 887b792..57c1849 100644 --- a/qiita_files/parse/tests/test_fastq.py +++ b/qiita_files/parse/tests/test_fastq.py @@ -17,9 +17,9 @@ class IterableData(object): def setUp(self): """ Initialize variables to be used by the tests as lists of strings""" - self.FASTQ_EXAMPLE = FASTQ_EXAMPLE.split('\n') - self.FASTQ_EXAMPLE_2 = FASTQ_EXAMPLE_2.split('\n') - self.FASTQ_EXAMPLE_3 = FASTQ_EXAMPLE_3.split('\n') + self.FASTQ_EXAMPLE = FASTQ_EXAMPLE.split(b'\n') + self.FASTQ_EXAMPLE_2 = FASTQ_EXAMPLE_2.split(b'\n') + self.FASTQ_EXAMPLE_3 = FASTQ_EXAMPLE_3.split(b'\n') class FileData(object): @@ -30,7 +30,7 @@ def setUp(self): ('FASTQ_EXAMPLE_2', FASTQ_EXAMPLE_2), ('FASTQ_EXAMPLE_3', FASTQ_EXAMPLE_3)]: tmp_file = tempfile.NamedTemporaryFile('wb') - tmp_file.write(val.encode("utf-8")) + tmp_file.write(val) tmp_file.flush() tmp_file.seek(0) setattr(self, attr, tmp_file.name) @@ -44,7 +44,7 @@ def tearDown(self): class ParseFastqTests(object): def test_parse(self): - for label, seq, qual in parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), + for label, seq, qual in parse_fastq(self.FASTQ_EXAMPLE, phred_offset=64): self.assertTrue(label in DATA) self.assertEqual(seq, DATA[label]["seq"]) @@ -52,7 +52,7 @@ def test_parse(self): # Make sure that enforce_qual_range set to False allows qual scores # to fall outside the typically acceptable range of 0-62 - for label, seq, qual in parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_2), + for label, seq, qual in parse_fastq(self.FASTQ_EXAMPLE_2, phred_offset=33, enforce_qual_range=False): self.assertTrue(label in DATA_2) @@ -63,18 +63,18 @@ def test_parse(self): # intended to be interpreted with an offset of 64, and using 33 will # make the qual score fall outside the acceptable range of 0-62. with self.assertRaises(ValueError): - list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), phred_offset=33)) + list(parse_fastq(self.FASTQ_EXAMPLE, phred_offset=33)) def test_parse_error(self): with self.assertRaises(ValueError): - list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_2), strict=True)) + list(parse_fastq(self.FASTQ_EXAMPLE_2, strict=True)) with self.assertRaises(ValueError): - list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE_3), phred_offset=64)) + list(parse_fastq(self.FASTQ_EXAMPLE_3, phred_offset=64)) def test_invalid_phred_offset(self): with self.assertRaises(ValueError): - list(parse_fastq(list_str2bin(self.FASTQ_EXAMPLE), phred_offset=42)) + list(parse_fastq(self.FASTQ_EXAMPLE, phred_offset=42)) class ParseFastqTestsInputIsIterable(IterableData, ParseFastqTests, TestCase): @@ -154,11 +154,6 @@ class ParseFastqTestsInputIsFileNames(FileData, ParseFastqTests, TestCase): 64, 51, 63, 63, 65, 65, 65])) } -def list_str2bin(inp): - if isinstance(inp, list): - return list(map(lambda x: x.encode("utf-8"), inp)) - return inp - FASTQ_EXAMPLE = r"""@GAPC_0015:6:1:1259:10413#0/1 AACACCAAACTTCTCCACCACGTGAGCTACAAAAG @@ -199,7 +194,7 @@ def list_str2bin(inp): @GAPC_0015:6:1:1317:3403#0/1 TTGTTTCCACTTGGTTGATTTCACCCCTGAGTTTG +GAPC_0015:6:1:1317:3403#0/1 -\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""" +\\\ZTYTSaLbb``\_UZ_bbcc`cc^[ac\a\Tc""".encode("utf-8") FASTQ_EXAMPLE_2 = r"""@GAPC_0017:6:1:1259:10413#0/1 @@ -210,7 +205,7 @@ def list_str2bin(inp): TATGTATATATAACATATACATATATACATACATA +GAPC_0015:6:1:1283:11957#0/1 ]KZ[PY]_[YY^```ac^\\`bT``c`\aT``bbb -""" +""".encode("utf-8") FASTQ_EXAMPLE_3 = r"""@GAPC_0017:6:1:1259:10413#0/1 @@ -218,7 +213,7 @@ def list_str2bin(inp): +GAPC_0015:6:1:1259:10413#0/1 ````Y^T]`]c^cabcacc`^Lb^ccYT\T\Y\WF @GAPC_0015:6:1:1283:11957#0/1 -""" +""".encode("utf-8") if __name__ == "__main__": diff --git a/qiita_files/parse/tests/test_iterator.py b/qiita_files/parse/tests/test_iterator.py index 6795fe8..cd11fce 100644 --- a/qiita_files/parse/tests/test_iterator.py +++ b/qiita_files/parse/tests/test_iterator.py @@ -15,6 +15,7 @@ from qiita_files.parse.iterator import (SequenceIterator, FastaIterator, FastqIterator) +from test_fastq import list_str2bin class SeqIterTests(TestCase): @@ -276,47 +277,47 @@ def test_fastq_gen(self): self.assertEqual(obs3, exp3) -fasta1 = rb""">1 +fasta1 = r""">1 aattggcc >2 aattaatt -""" +""".encode("utf-8") -fasta2 = rb""">3 +fasta2 = r""">3 atat -""" +""".encode("utf-8") -fasta3 = rb""">4 +fasta3 = r""">4 attatt >5 ggccc -""" +""".encode("utf-8") -qual1 = rb""">1 +qual1 = r""">1 1 2 3 4 5 6 7 8 >2 8 7 6 5 4 3 2 1 -""" +""".encode("utf-8") -qual2 = rb""">3 +qual2 = r""">3 1 2 3 4 -""" +""".encode("utf-8") -qual3 = rb""">4 +qual3 = r""">4 1 2 3 4 5 6 >5 1 2 3 4 5 -""" +""".encode("utf-8") -qual_bad_val = rb""">3 +qual_bad_val = r""">3 1 2 -""" +""".encode("utf-8") -qual_bad_id = rb""">asdasd +qual_bad_id = r""">asdasd 1 2 3 4 -""" +""".encode("utf-8") -fastq1 = rb"""@1 +fastq1 = r"""@1 atat + ABCD @@ -324,13 +325,13 @@ def test_fastq_gen(self): atgc + BCDE -""" +""".encode("utf-8") -fastq2 = rb"""@3 +fastq2 = r"""@3 taa + EFG -""" +""".encode("utf-8") if __name__ == '__main__': From 80b2a1b9a00d24a7222362f803a9dd05cc3e39e1 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 16:27:14 +0200 Subject: [PATCH 7/8] remove leftover --- qiita_files/parse/tests/test_iterator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiita_files/parse/tests/test_iterator.py b/qiita_files/parse/tests/test_iterator.py index cd11fce..3dda430 100644 --- a/qiita_files/parse/tests/test_iterator.py +++ b/qiita_files/parse/tests/test_iterator.py @@ -15,7 +15,6 @@ from qiita_files.parse.iterator import (SequenceIterator, FastaIterator, FastqIterator) -from test_fastq import list_str2bin class SeqIterTests(TestCase): From 13b88783ff2e65c3743604f11dd7dd09bce98d08 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Thu, 28 Aug 2025 16:37:06 +0200 Subject: [PATCH 8/8] make import StringIO compatible --- qiita_files/tests/test_util.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qiita_files/tests/test_util.py b/qiita_files/tests/test_util.py index daf256f..4a600f2 100644 --- a/qiita_files/tests/test_util.py +++ b/qiita_files/tests/test_util.py @@ -8,11 +8,16 @@ # ----------------------------------------------------------------------------- import os +import sys from unittest import TestCase, main import tempfile import h5py -from io import StringIO, BytesIO +if sys.version_info[0] == 2: + from StringIO import StringIO +else: + from io import StringIO +from io import BytesIO from qiita_files.util import (open_file, _is_string_or_bytes) @@ -25,7 +30,7 @@ class TestFilePathOpening(TestCase): """Tests adapted from scikit-bio's skbio.io.util tests""" def test_is_string_or_bytes(self): self.assertTrue(_is_string_or_bytes('foo')) - self.assertTrue(_is_string_or_bytes(u'foo')) + self.assertTrue(_is_string_or_bytes(u'foo'.encode("utf-8"))) self.assertTrue(_is_string_or_bytes(b'foo')) self.assertFalse(_is_string_or_bytes(StringIO('bar'))) self.assertFalse(_is_string_or_bytes([1]))