From fc112b85d30b705cb2435992c780aa021d6eb48f Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Thu, 4 Apr 2024 16:42:01 -0700 Subject: [PATCH 01/12] Fix FileNotFoundErrors to support windows --- biom/tests/test_cli/test_subset_table.py | 13 ++++---- biom/tests/test_cli/test_table_normalizer.py | 6 ++-- biom/tests/test_cli/test_validate_table.py | 3 +- biom/tests/test_parse.py | 31 ++++++++++---------- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/biom/tests/test_cli/test_subset_table.py b/biom/tests/test_cli/test_subset_table.py index 19080c6fd..4f08b2660 100644 --- a/biom/tests/test_cli/test_subset_table.py +++ b/biom/tests/test_cli/test_subset_table.py @@ -55,9 +55,10 @@ def test_invalid_input(self): def test_subset_samples_hdf5(self): """Correctly subsets samples in a hdf5 table""" cwd = os.getcwd() - if '/' in __file__: - os.chdir(__file__.rsplit('/', 1)[0]) - obs = _subset_table(hdf5_biom='test_data/test.biom', axis='sample', + if os.path.sep in __file__: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + obs = _subset_table(hdf5_biom=os.path.join('test_data', 'test.biom'), + axis='sample', ids=['Sample1', 'Sample2', 'Sample3'], json_table_str=None) os.chdir(cwd) @@ -71,9 +72,9 @@ def test_subset_samples_hdf5(self): def test_subset_observations_hdf5(self): """Correctly subsets samples in a hdf5 table""" cwd = os.getcwd() - if '/' in __file__: - os.chdir(__file__.rsplit('/', 1)[0]) - obs = _subset_table(hdf5_biom='test_data/test.biom', + if os.path.sep in __file__: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + obs = _subset_table(hdf5_biom=os.path.join('test_data', 'test.biom'), axis='observation', ids=['GG_OTU_1', 'GG_OTU_3', 'GG_OTU_5'], json_table_str=None) diff --git a/biom/tests/test_cli/test_table_normalizer.py b/biom/tests/test_cli/test_table_normalizer.py index 4844b6549..ae33f2e08 100755 --- a/biom/tests/test_cli/test_table_normalizer.py +++ b/biom/tests/test_cli/test_table_normalizer.py @@ -24,9 +24,9 @@ def setUp(self): self.cmd = _normalize_table cwd = os.getcwd() - if '/' in __file__: - os.chdir(__file__.rsplit('/', 1)[0]) - self.table = biom.load_table('test_data/test.json') + if os.path.sep in __file__: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + self.table = biom.load_table(os.path.join('test_data', 'test.json')) os.chdir(cwd) def test_bad_inputs(self): diff --git a/biom/tests/test_cli/test_validate_table.py b/biom/tests/test_cli/test_validate_table.py index b892577df..2a12ccdb8 100644 --- a/biom/tests/test_cli/test_validate_table.py +++ b/biom/tests/test_cli/test_validate_table.py @@ -39,7 +39,8 @@ def setUp(self): self.to_remove = [] cur_path = os.path.split(os.path.abspath(__file__))[0] - examples_path = os.path.join(cur_path.rsplit('/', 3)[0], 'examples') + examples_path = os.path.join(cur_path.rsplit(os.path.sep, 3)[0], + 'examples') self.hdf5_file_valid = os.path.join(examples_path, 'min_sparse_otu_table_hdf5.biom') self.hdf5_file_valid_md = os.path.join(examples_path, diff --git a/biom/tests/test_parse.py b/biom/tests/test_parse.py index 8fd5d0b9d..df94d003d 100644 --- a/biom/tests/test_parse.py +++ b/biom/tests/test_parse.py @@ -281,9 +281,10 @@ def test_parse_adjacency_table_no_header(self): def test_parse_biom_table_hdf5(self): """Make sure we can parse a HDF5 table through the same loader""" cwd = os.getcwd() - if '/' in __file__[1:]: - os.chdir(__file__.rsplit('/', 1)[0]) - Table.from_hdf5(h5py.File('test_data/test.biom', 'r')) + if os.path.sep in __file__[1:]: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + Table.from_hdf5(h5py.File(os.path.join('test_data', 'test.biom'), + 'r')) os.chdir(cwd) def test_save_table_filepath(self): @@ -296,23 +297,23 @@ def test_save_table_filepath(self): def test_load_table_filepath(self): cwd = os.getcwd() - if '/' in __file__[1:]: - os.chdir(__file__.rsplit('/', 1)[0]) - load_table('test_data/test.biom') + if os.path.sep in __file__[1:]: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + load_table(os.path.join('test_data', 'test.biom')) os.chdir(cwd) def test_load_table_inmemory(self): cwd = os.getcwd() - if '/' in __file__[1:]: - os.chdir(__file__.rsplit('/', 1)[0]) - load_table(h5py.File('test_data/test.biom', 'r')) + if os.path.sep in __file__[1:]: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + load_table(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) os.chdir(cwd) def test_load_table_inmemory_json(self): cwd = os.getcwd() - if '/' in __file__[1:]: - os.chdir(__file__.rsplit('/', 1)[0]) - load_table(open('test_data/test.json')) + if os.path.sep in __file__[1:]: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + load_table(open(os.path.join('test_data', 'test.json'))) os.chdir(cwd) def test_load_table_inmemory_stringio(self): @@ -350,10 +351,10 @@ def test_parse_biom_table_with_hdf5(self): """tests for parse_biom_table when we have h5py""" # We will round-trip the HDF5 file to several different formats, and # make sure we can recover the same table using parse_biom_table - if '/' in __file__[1:]: - os.chdir(__file__.rsplit('/', 1)[0]) + if os.path.sep in __file__[1:]: + os.chdir(__file__.rsplit(os.path.sep, 1)[0]) - t = parse_biom_table(h5py.File('test_data/test.biom', 'r')) + t = parse_biom_table(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) # These things are not round-trippable using the general-purpose # parse_biom_table function From e656012fb782c08f74253331e4eb42be9392b346 Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Fri, 5 Apr 2024 16:23:44 -0700 Subject: [PATCH 02/12] Use dirname over rsplit --- biom/tests/test_cli/test_subset_table.py | 4 ++-- biom/tests/test_cli/test_table_normalizer.py | 2 +- biom/tests/test_parse.py | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/biom/tests/test_cli/test_subset_table.py b/biom/tests/test_cli/test_subset_table.py index 4f08b2660..0ecedd8af 100644 --- a/biom/tests/test_cli/test_subset_table.py +++ b/biom/tests/test_cli/test_subset_table.py @@ -56,7 +56,7 @@ def test_subset_samples_hdf5(self): """Correctly subsets samples in a hdf5 table""" cwd = os.getcwd() if os.path.sep in __file__: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) obs = _subset_table(hdf5_biom=os.path.join('test_data', 'test.biom'), axis='sample', ids=['Sample1', 'Sample2', 'Sample3'], @@ -73,7 +73,7 @@ def test_subset_observations_hdf5(self): """Correctly subsets samples in a hdf5 table""" cwd = os.getcwd() if os.path.sep in __file__: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) obs = _subset_table(hdf5_biom=os.path.join('test_data', 'test.biom'), axis='observation', ids=['GG_OTU_1', 'GG_OTU_3', 'GG_OTU_5'], diff --git a/biom/tests/test_cli/test_table_normalizer.py b/biom/tests/test_cli/test_table_normalizer.py index ae33f2e08..d85ebcf99 100755 --- a/biom/tests/test_cli/test_table_normalizer.py +++ b/biom/tests/test_cli/test_table_normalizer.py @@ -25,7 +25,7 @@ def setUp(self): cwd = os.getcwd() if os.path.sep in __file__: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) self.table = biom.load_table(os.path.join('test_data', 'test.json')) os.chdir(cwd) diff --git a/biom/tests/test_parse.py b/biom/tests/test_parse.py index df94d003d..780a89af0 100644 --- a/biom/tests/test_parse.py +++ b/biom/tests/test_parse.py @@ -282,7 +282,7 @@ def test_parse_biom_table_hdf5(self): """Make sure we can parse a HDF5 table through the same loader""" cwd = os.getcwd() if os.path.sep in __file__[1:]: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) Table.from_hdf5(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) os.chdir(cwd) @@ -298,21 +298,21 @@ def test_save_table_filepath(self): def test_load_table_filepath(self): cwd = os.getcwd() if os.path.sep in __file__[1:]: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) load_table(os.path.join('test_data', 'test.biom')) os.chdir(cwd) def test_load_table_inmemory(self): cwd = os.getcwd() if os.path.sep in __file__[1:]: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) load_table(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) os.chdir(cwd) def test_load_table_inmemory_json(self): cwd = os.getcwd() if os.path.sep in __file__[1:]: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) load_table(open(os.path.join('test_data', 'test.json'))) os.chdir(cwd) @@ -352,7 +352,7 @@ def test_parse_biom_table_with_hdf5(self): # We will round-trip the HDF5 file to several different formats, and # make sure we can recover the same table using parse_biom_table if os.path.sep in __file__[1:]: - os.chdir(__file__.rsplit(os.path.sep, 1)[0]) + os.chdir(os.path.dirname(__file__)) t = parse_biom_table(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) From 8ae53dd0d48ecb4f0c283b7283c0f7c8e05505b5 Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Mon, 8 Apr 2024 11:05:36 -0700 Subject: [PATCH 03/12] Fix temp file opening issues --- biom/tests/test_cli/test_add_metadata.py | 5 ++++- biom/tests/test_cli/test_summarize_table.py | 5 ++++- biom/tests/test_cli/test_table_converter.py | 9 +++++++-- biom/tests/test_util.py | 9 +++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/biom/tests/test_cli/test_add_metadata.py b/biom/tests/test_cli/test_add_metadata.py index 16e6d4367..dc926c2eb 100644 --- a/biom/tests/test_cli/test_add_metadata.py +++ b/biom/tests/test_cli/test_add_metadata.py @@ -9,6 +9,7 @@ # ----------------------------------------------------------------------------- import tempfile +import os from unittest import TestCase, main import biom @@ -20,10 +21,12 @@ class TestAddMetadata(TestCase): def setUp(self): """Set up data for use in unit tests.""" self.cmd = _add_metadata - with tempfile.NamedTemporaryFile('w') as fh: + with tempfile.NamedTemporaryFile('w', delete=False) as fh: fh.write(biom1) fh.flush() self.biom_table1 = biom.load_table(fh.name) + fh.close() + os.unlink(fh.name) self.sample_md_lines1 = sample_md1.split('\n') self.obs_md_lines1 = obs_md1.split('\n') diff --git a/biom/tests/test_cli/test_summarize_table.py b/biom/tests/test_cli/test_summarize_table.py index a979848b2..a370faa93 100644 --- a/biom/tests/test_cli/test_summarize_table.py +++ b/biom/tests/test_cli/test_summarize_table.py @@ -12,16 +12,19 @@ from biom.parse import load_table import tempfile +import os from unittest import TestCase, main class TestSummarizeTable(TestCase): def setUp(self): - with tempfile.NamedTemporaryFile(mode='w') as fh: + with tempfile.NamedTemporaryFile(mode='w', delete=False) as fh: fh.write(biom1) fh.flush() self.biom1 = load_table(fh.name) + fh.close() + os.unlink(fh.name) def test_default(self): """ TableSummarizer functions as expected diff --git a/biom/tests/test_cli/test_table_converter.py b/biom/tests/test_cli/test_table_converter.py index af200130d..dc32e9d29 100644 --- a/biom/tests/test_cli/test_table_converter.py +++ b/biom/tests/test_cli/test_table_converter.py @@ -8,6 +8,7 @@ # The full license is in the file COPYING.txt, distributed with this software. # ----------------------------------------------------------------------------- +import os from os.path import abspath, dirname, join import tempfile @@ -28,16 +29,20 @@ def setUp(self): self.cmd = _convert self.output_filepath = tempfile.NamedTemporaryFile().name - with tempfile.NamedTemporaryFile('w') as fh: + with tempfile.NamedTemporaryFile('w', delete=False) as fh: fh.write(biom1) fh.flush() self.biom_table1 = load_table(fh.name) + fh.close() + os.unlink(fh.name) self.biom_lines1 = biom1.split('\n') - with tempfile.NamedTemporaryFile('w') as fh: + with tempfile.NamedTemporaryFile('w', delete=False) as fh: fh.write(classic1) fh.flush() self.classic_biom1 = load_table(fh.name) + fh.close() + os.unlink(fh.name) self.sample_md1 = MetadataMap.from_file(sample_md1.split('\n')) diff --git a/biom/tests/test_util.py b/biom/tests/test_util.py index b3d1806ca..f18a15012 100644 --- a/biom/tests/test_util.py +++ b/biom/tests/test_util.py @@ -246,11 +246,14 @@ def test_safe_md5(self): tmp_f = NamedTemporaryFile( mode='w', prefix='test_safe_md5', - suffix='txt') + suffix='txt', + delete=False) tmp_f.write('foo\n') tmp_f.flush() obs = safe_md5(open(tmp_f.name)) + tmp_f.close() + os.unlink(tmp_f.name) self.assertEqual(obs, exp) obs = safe_md5(['foo\n']) @@ -309,11 +312,13 @@ def test_is_hdf5_file(self): def test_load_classic(self): tab = load_table(get_data_path('test.json')) - with NamedTemporaryFile(mode='w') as fp: + with NamedTemporaryFile(mode='w', delete=False) as fp: fp.write(str(tab)) fp.flush() obs = load_table(fp.name) + fp.close() + os.unlink(fp.name) npt.assert_equal(obs.ids(), tab.ids()) npt.assert_equal(obs.ids(axis='observation'), From 4b3adc98afb570ecea62c33b061875234303271d Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Mon, 8 Apr 2024 13:09:37 -0700 Subject: [PATCH 04/12] Fix other temp file issues --- biom/tests/test_cli/test_add_metadata.py | 3 +- biom/tests/test_cli/test_summarize_table.py | 3 +- biom/tests/test_cli/test_table_converter.py | 3 +- biom/tests/test_parse.py | 3 +- biom/tests/test_table.py | 58 ++++++++++++++------- biom/tests/test_util.py | 6 +-- 6 files changed, 46 insertions(+), 30 deletions(-) diff --git a/biom/tests/test_cli/test_add_metadata.py b/biom/tests/test_cli/test_add_metadata.py index dc926c2eb..375b84bd2 100644 --- a/biom/tests/test_cli/test_add_metadata.py +++ b/biom/tests/test_cli/test_add_metadata.py @@ -25,8 +25,7 @@ def setUp(self): fh.write(biom1) fh.flush() self.biom_table1 = biom.load_table(fh.name) - fh.close() - os.unlink(fh.name) + os.unlink(fh.name) self.sample_md_lines1 = sample_md1.split('\n') self.obs_md_lines1 = obs_md1.split('\n') diff --git a/biom/tests/test_cli/test_summarize_table.py b/biom/tests/test_cli/test_summarize_table.py index a370faa93..b9b280b77 100644 --- a/biom/tests/test_cli/test_summarize_table.py +++ b/biom/tests/test_cli/test_summarize_table.py @@ -23,8 +23,7 @@ def setUp(self): fh.write(biom1) fh.flush() self.biom1 = load_table(fh.name) - fh.close() - os.unlink(fh.name) + os.unlink(fh.name) def test_default(self): """ TableSummarizer functions as expected diff --git a/biom/tests/test_cli/test_table_converter.py b/biom/tests/test_cli/test_table_converter.py index dc32e9d29..73aaae42f 100644 --- a/biom/tests/test_cli/test_table_converter.py +++ b/biom/tests/test_cli/test_table_converter.py @@ -41,8 +41,7 @@ def setUp(self): fh.write(classic1) fh.flush() self.classic_biom1 = load_table(fh.name) - fh.close() - os.unlink(fh.name) + os.unlink(fh.name) self.sample_md1 = MetadataMap.from_file(sample_md1.split('\n')) diff --git a/biom/tests/test_parse.py b/biom/tests/test_parse.py index 780a89af0..430454542 100644 --- a/biom/tests/test_parse.py +++ b/biom/tests/test_parse.py @@ -290,10 +290,11 @@ def test_parse_biom_table_hdf5(self): def test_save_table_filepath(self): t = Table(np.array([[0, 1, 2], [3, 4, 5]]), ['a', 'b'], ['c', 'd', 'e']) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: save_table(t, tmpfile.name) obs = load_table(tmpfile.name) self.assertEqual(obs, t) + os.unlink(tmpfile.name) def test_load_table_filepath(self): cwd = os.getcwd() diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 756758472..7214abd76 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1016,13 +1016,15 @@ def test_to_from_hdf5_bug_861(self): ['c', 'd', 'e']) t.add_metadata({'a': {'a / problem': 10}, 'b': {'a / problem': 20}}, axis='observation') - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() h5 = h5py.File(tmpfile.name, 'r') obs = Table.from_hdf5(h5) + h5.close() + os.unlink(tmpfile.name) self.assertEqual(obs, t) @@ -1030,7 +1032,7 @@ def test_to_from_hdf5_creation_date(self): t = Table(np.array([[0, 1, 2], [3, 4, 5]]), ['a', 'b'], ['c', 'd', 'e']) current = datetime.now() - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests', creation_date=current) h5.close() @@ -1038,6 +1040,8 @@ def test_to_from_hdf5_creation_date(self): h5 = h5py.File(tmpfile.name, 'r') obs = Table.from_hdf5(h5) self.assertEqual(obs.create_date, current) + h5.close() + os.unlink(tmpfile.name) self.assertEqual(obs, t) @@ -1045,24 +1049,27 @@ def test_to_hdf5_empty_table(self): """Successfully writes an empty OTU table in HDF5 format""" # Create an empty OTU table t = Table([], [], []) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() + os.unlink(tmpfile.name) def test_to_hdf5_empty_table_bug_619(self): """Successfully writes an empty OTU table in HDF5 format""" t = example_table.filter({}, axis='observation', inplace=False) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() + os.unlink(tmpfile.name) t = example_table.filter({}, inplace=False) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() + os.unlink(tmpfile.name) def test_to_hdf5_missing_metadata_observation(self): # exercises a vlen_list @@ -1070,10 +1077,11 @@ def test_to_hdf5_missing_metadata_observation(self): [{'taxonomy': None}, {'taxonomy': ['foo', 'baz']}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) + os.unlink(tmpfile.name) self.assertEqual(obs.metadata(axis='observation'), ({'taxonomy': None}, {'taxonomy': ['foo', 'baz']})) @@ -1084,10 +1092,11 @@ def test_to_hdf5_missing_metadata_sample(self): [{'dat': None}, {'dat': 'foo'}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) + os.unlink(tmpfile.name) self.assertEqual(obs.metadata(axis='sample'), ({'dat': ''}, {'dat': 'foo'})) @@ -1097,11 +1106,12 @@ def test_to_hdf5_inconsistent_metadata_categories_observation(self): [{'taxonomy_A': 'foo; bar'}, {'taxonomy_B': 'foo; baz'}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with h5py.File(tmpfile.name, 'w') as h5: with self.assertRaisesRegex(ValueError, 'inconsistent metadata'): t.to_hdf5(h5, 'tests') + os.unlink(tmpfile.name) def test_to_hdf5_inconsistent_metadata_categories_sample(self): t = Table(np.array([[0, 1], [2, 3]]), ['a', 'b'], ['c', 'd'], @@ -1109,21 +1119,23 @@ def test_to_hdf5_inconsistent_metadata_categories_sample(self): [{'dat_A': 'foo; bar'}, {'dat_B': 'foo; baz'}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with h5py.File(tmpfile.name, 'w') as h5: with self.assertRaisesRegex(ValueError, 'inconsistent metadata'): t.to_hdf5(h5, 'tests') + os.unlink(tmpfile.name) def test_to_hdf5_malformed_taxonomy(self): t = Table(np.array([[0, 1], [2, 3]]), ['a', 'b'], ['c', 'd'], [{'taxonomy': 'foo; bar'}, {'taxonomy': 'foo; baz'}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) + os.unlink(tmpfile.name) self.assertEqual(obs.metadata(axis='observation'), ({'taxonomy': ['foo', 'bar']}, {'taxonomy': ['foo', 'baz']})) @@ -1134,9 +1146,11 @@ def test_to_hdf5_general_fallback_to_list(self): [{'foo': ['k__a', 'p__b']}, {'foo': ['k__a', 'p__c']}], [{'barcode': 'aatt'}, {'barcode': 'ttgg'}]) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') st_rich.to_hdf5(h5, 'tests') + h5.close() + os.unlink(tmpfile.name) def test_to_hdf5_custom_formatters(self): self.st_rich = Table(self.vals, @@ -1151,7 +1165,7 @@ def bc_formatter(grp, category, md, compression): grp.create_dataset(name, shape=data.shape, dtype=H5PY_VLEN_STR, data=data, compression=compression) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') self.st_rich.to_hdf5(h5, 'tests', format_fs={'barcode': bc_formatter}) @@ -1172,10 +1186,11 @@ def bc_formatter(grp, category, md, compression): self.assertNotEqual(m1['barcode'], m2['barcode']) self.assertEqual(m1['barcode'].lower(), m2['barcode']) h5.close() + os.unlink(tmpfile.name) def test_to_hdf5(self): """Write a file""" - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') self.st_rich.to_hdf5(h5, 'tests') h5.close() @@ -1184,18 +1199,19 @@ def test_to_hdf5(self): self.assertIn('observation', h5) self.assertIn('sample', h5) self.assertEqual(sorted(h5.attrs.keys()), sorted(['id', 'type', - 'format-url', - 'format-version', - 'generated-by', - 'creation-date', - 'shape', 'nnz'])) + 'format-url', + 'format-version', + 'generated-by', + 'creation-date', + 'shape', 'nnz'])) obs = Table.from_hdf5(h5) self.assertEqual(obs, self.st_rich) h5.close() + os.unlink(tmpfile.name) # Test with a collapsed table - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') dt_rich = Table( np.array([[5, 6, 7], [8, 9, 10], [11, 12, 13]]), @@ -1238,9 +1254,10 @@ def bin_f(id_, x): [{'collapsed_ids': ['a', 'c']}, {'collapsed_ids': ['b']}]) self.assertEqual(obs, exp) + os.unlink(tmpfile.name) # Test with table having a None on taxonomy - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t = Table(self.vals, ['1', '2'], ['a', 'b'], [{'taxonomy': ['k__a', 'p__b']}, @@ -1262,6 +1279,7 @@ def bin_f(id_, x): obs = Table.from_hdf5(h5) h5.close() self.assertEqual(obs, t) + os.unlink(tmpfile.name) def test_from_tsv(self): tab1_fh = StringIO(otu_table1) diff --git a/biom/tests/test_util.py b/biom/tests/test_util.py index f18a15012..76063fdd7 100644 --- a/biom/tests/test_util.py +++ b/biom/tests/test_util.py @@ -265,9 +265,10 @@ def test_safe_md5(self): def test_biom_open_hdf5_pathlib_write(self): t = Table(np.array([[0, 1, 2], [3, 4, 5]]), ['a', 'b'], ['c', 'd', 'e']) - with NamedTemporaryFile() as tmpfile: + with NamedTemporaryFile(delete=False) as tmpfile: with biom_open(pathlib.Path(tmpfile.name), 'w') as fp: t.to_hdf5(fp, 'tests') + os.unlink(tmpfile.name) def test_biom_open_hdf5_pathlib_read(self): cwd = os.getcwd() @@ -317,8 +318,7 @@ def test_load_classic(self): fp.flush() obs = load_table(fp.name) - fp.close() - os.unlink(fp.name) + os.unlink(fp.name) npt.assert_equal(obs.ids(), tab.ids()) npt.assert_equal(obs.ids(axis='observation'), From d00f947b4e98dd05c6432c4087cec2432d0e825b Mon Sep 17 00:00:00 2001 From: Qiyun Zhu Date: Wed, 10 Apr 2024 12:35:59 -0700 Subject: [PATCH 05/12] Supporting NumPy 2.0 (#950) * added numpy-2.0 support * Update ChangeLog.md --- ChangeLog.md | 1 + biom/_filter.pyx | 1 + biom/_subsample.pyx | 2 ++ biom/_transform.pyx | 1 + 4 files changed, 5 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 8cb9d77ba..622634d5e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ biom 2.1.15-dev Performance improvements: +* Add NumPy 2.0 support. PR [#950](https://github.com/biocore/biom-format/pull/950) ensures code compatibility with NumPy 2.0. This support is yet to be added to the CI testing matrix. * Revise `Table._fast_merge` to use COO directly. For very large tables, this reduces runtime by ~50x and memory by ~5x. See PR [#913](https://github.com/biocore/biom-format/pull/933). * Drastically reduce the memory needs of subsampling when sums are large. Also adds 64-bit support. See PR [#935](https://github.com/biocore/biom-format/pull/935). * Improve handling of not-perfectly-integer inputs. See PR [#938](https://github.com/biocore/biom-format/pull/938). diff --git a/biom/_filter.pyx b/biom/_filter.pyx index 3c94ac541..879db01d0 100644 --- a/biom/_filter.pyx +++ b/biom/_filter.pyx @@ -13,6 +13,7 @@ from types import FunctionType import numpy as np cimport numpy as cnp +cnp.import_array() cdef cnp.ndarray[cnp.uint8_t, ndim=1] \ diff --git a/biom/_subsample.pyx b/biom/_subsample.pyx index 6bf212ac2..358920250 100644 --- a/biom/_subsample.pyx +++ b/biom/_subsample.pyx @@ -8,6 +8,8 @@ import numpy as np cimport numpy as cnp +cnp.import_array() + cdef _subsample_with_replacement(cnp.ndarray[cnp.float64_t, ndim=1] data, cnp.ndarray[cnp.int32_t, ndim=1] indptr, diff --git a/biom/_transform.pyx b/biom/_transform.pyx index 8a15018ff..0d5000421 100644 --- a/biom/_transform.pyx +++ b/biom/_transform.pyx @@ -9,6 +9,7 @@ import numpy as np cimport numpy as cnp +cnp.import_array() def _transform(arr, ids, metadata, function, axis): From 50b69081c6565b65f06ba54bf7b52870a35a53c7 Mon Sep 17 00:00:00 2001 From: Qiyun Zhu Date: Wed, 10 Apr 2024 13:27:06 -0700 Subject: [PATCH 06/12] removed dependency on natsort (#953) --- ci/aarch64.conda_requirements.txt | 1 - ci/conda_requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/ci/aarch64.conda_requirements.txt b/ci/aarch64.conda_requirements.txt index e0f66afe5..c07742dc4 100644 --- a/ci/aarch64.conda_requirements.txt +++ b/ci/aarch64.conda_requirements.txt @@ -1,4 +1,3 @@ -natsort >= 4.0.3 numpy >= 1.9.2 pandas >= 0.20.0 scipy >= 1.3.1 diff --git a/ci/conda_requirements.txt b/ci/conda_requirements.txt index e0f66afe5..c07742dc4 100644 --- a/ci/conda_requirements.txt +++ b/ci/conda_requirements.txt @@ -1,4 +1,3 @@ -natsort >= 4.0.3 numpy >= 1.9.2 pandas >= 0.20.0 scipy >= 1.3.1 From bdbc4db26be22561491994e805bca1fd6c7cb612 Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Thu, 11 Apr 2024 11:03:02 -0700 Subject: [PATCH 07/12] Fix linting and put unlink in tearDown --- biom/tests/test_cli/test_add_metadata.py | 5 ++++- biom/tests/test_cli/test_summarize_table.py | 5 ++++- biom/tests/test_cli/test_table_converter.py | 9 ++++++--- biom/tests/test_parse.py | 3 ++- biom/tests/test_table.py | 10 +++++----- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/biom/tests/test_cli/test_add_metadata.py b/biom/tests/test_cli/test_add_metadata.py index 375b84bd2..16010833f 100644 --- a/biom/tests/test_cli/test_add_metadata.py +++ b/biom/tests/test_cli/test_add_metadata.py @@ -25,10 +25,13 @@ def setUp(self): fh.write(biom1) fh.flush() self.biom_table1 = biom.load_table(fh.name) - os.unlink(fh.name) + self.temporary_fh_name = fh.name self.sample_md_lines1 = sample_md1.split('\n') self.obs_md_lines1 = obs_md1.split('\n') + def tearDown(self): + os.unlink(self.temporary_fh_name) + def test_add_sample_metadata_no_casting(self): """Correctly adds sample metadata without casting it.""" # Add a subset of sample metadata to a table that doesn't have any diff --git a/biom/tests/test_cli/test_summarize_table.py b/biom/tests/test_cli/test_summarize_table.py index b9b280b77..274a7d4d2 100644 --- a/biom/tests/test_cli/test_summarize_table.py +++ b/biom/tests/test_cli/test_summarize_table.py @@ -23,7 +23,10 @@ def setUp(self): fh.write(biom1) fh.flush() self.biom1 = load_table(fh.name) - os.unlink(fh.name) + self.temporary_fh_name = fh.name + + def tearDown(self): + os.unlink(self.temporary_fh_name) def test_default(self): """ TableSummarizer functions as expected diff --git a/biom/tests/test_cli/test_table_converter.py b/biom/tests/test_cli/test_table_converter.py index 73aaae42f..44ecc97e7 100644 --- a/biom/tests/test_cli/test_table_converter.py +++ b/biom/tests/test_cli/test_table_converter.py @@ -33,15 +33,14 @@ def setUp(self): fh.write(biom1) fh.flush() self.biom_table1 = load_table(fh.name) - fh.close() - os.unlink(fh.name) + self.temporary_fh_table_name = fh.name self.biom_lines1 = biom1.split('\n') with tempfile.NamedTemporaryFile('w', delete=False) as fh: fh.write(classic1) fh.flush() self.classic_biom1 = load_table(fh.name) - os.unlink(fh.name) + self.temporary_fh_classic_name = fh.name self.sample_md1 = MetadataMap.from_file(sample_md1.split('\n')) @@ -51,6 +50,10 @@ def setUp(self): self.json_collapsed_samples = join(test_data_dir, 'json_sample_collapsed.biom') + def tearDown(self): + os.unlink(self.temporary_fh_classic_name) + os.unlink(self.temporary_fh_table_name) + def test_classic_to_biom(self): """Correctly converts classic to biom.""" self.cmd(table=self.classic_biom1, diff --git a/biom/tests/test_parse.py b/biom/tests/test_parse.py index 430454542..69aebe712 100644 --- a/biom/tests/test_parse.py +++ b/biom/tests/test_parse.py @@ -355,7 +355,8 @@ def test_parse_biom_table_with_hdf5(self): if os.path.sep in __file__[1:]: os.chdir(os.path.dirname(__file__)) - t = parse_biom_table(h5py.File(os.path.join('test_data', 'test.biom'), 'r')) + t = parse_biom_table(h5py.File(os.path.join('test_data', 'test.biom'), + 'r')) # These things are not round-trippable using the general-purpose # parse_biom_table function diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 7214abd76..4167081af 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1199,11 +1199,11 @@ def test_to_hdf5(self): self.assertIn('observation', h5) self.assertIn('sample', h5) self.assertEqual(sorted(h5.attrs.keys()), sorted(['id', 'type', - 'format-url', - 'format-version', - 'generated-by', - 'creation-date', - 'shape', 'nnz'])) + 'format-url', + 'format-version', + 'generated-by', + 'creation-date', + 'shape', 'nnz'])) obs = Table.from_hdf5(h5) self.assertEqual(obs, self.st_rich) From b0654ba05e3e4e23b094f1c06523e3793a8657de Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Thu, 11 Apr 2024 15:45:09 -0700 Subject: [PATCH 08/12] Testing workflow failure of python3.7 on windows --- biom/tests/test_cli/test_add_metadata.py | 1 + 1 file changed, 1 insertion(+) diff --git a/biom/tests/test_cli/test_add_metadata.py b/biom/tests/test_cli/test_add_metadata.py index 16010833f..6adc49201 100644 --- a/biom/tests/test_cli/test_add_metadata.py +++ b/biom/tests/test_cli/test_add_metadata.py @@ -31,6 +31,7 @@ def setUp(self): def tearDown(self): os.unlink(self.temporary_fh_name) + self.test_workflow = True def test_add_sample_metadata_no_casting(self): """Correctly adds sample metadata without casting it.""" From 07236ffc4243e2e968c158c28133260a808f124b Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Thu, 11 Apr 2024 16:01:17 -0700 Subject: [PATCH 09/12] Remove test line for python3.7 --- biom/tests/test_cli/test_add_metadata.py | 1 - 1 file changed, 1 deletion(-) diff --git a/biom/tests/test_cli/test_add_metadata.py b/biom/tests/test_cli/test_add_metadata.py index 6adc49201..16010833f 100644 --- a/biom/tests/test_cli/test_add_metadata.py +++ b/biom/tests/test_cli/test_add_metadata.py @@ -31,7 +31,6 @@ def setUp(self): def tearDown(self): os.unlink(self.temporary_fh_name) - self.test_workflow = True def test_add_sample_metadata_no_casting(self): """Correctly adds sample metadata without casting it.""" From 563eda1a574bf45d529a63a1c00fdccc48bd7d5e Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Mon, 15 Apr 2024 10:13:03 -0700 Subject: [PATCH 10/12] Utilize setUp and tearDown for removing tmp files --- biom/tests/test_parse.py | 9 +++++++-- biom/tests/test_table.py | 30 +++++++++++++++--------------- biom/tests/test_util.py | 12 +++++++++--- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/biom/tests/test_parse.py b/biom/tests/test_parse.py index 69aebe712..1b310160a 100644 --- a/biom/tests/test_parse.py +++ b/biom/tests/test_parse.py @@ -46,7 +46,7 @@ def setUp(self): self.legacy_otu_table1 = legacy_otu_table1 self.otu_table1 = otu_table1 self.otu_table1_floats = otu_table1_floats - self.files_to_remove = [] + self.to_remove = [] self.biom_minimal_sparse = biom_minimal_sparse self.classic_otu_table1_w_tax = classic_otu_table1_w_tax.split('\n') @@ -54,6 +54,11 @@ def setUp(self): self.classic_table_with_complex_metadata = \ classic_table_with_complex_metadata.split('\n') + def tearDown(self): + if self.to_remove: + for f in self.to_remove: + os.remove(f) + def test_from_tsv_bug_854(self): data = StringIO('#FeatureID\tSample1') exp = Table([], [], ['Sample1']) @@ -294,7 +299,7 @@ def test_save_table_filepath(self): save_table(t, tmpfile.name) obs = load_table(tmpfile.name) self.assertEqual(obs, t) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_load_table_filepath(self): cwd = os.getcwd() diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 4167081af..372f61601 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1024,7 +1024,7 @@ def test_to_from_hdf5_bug_861(self): h5 = h5py.File(tmpfile.name, 'r') obs = Table.from_hdf5(h5) h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) self.assertEqual(obs, t) @@ -1041,7 +1041,7 @@ def test_to_from_hdf5_creation_date(self): obs = Table.from_hdf5(h5) self.assertEqual(obs.create_date, current) h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) self.assertEqual(obs, t) @@ -1053,7 +1053,7 @@ def test_to_hdf5_empty_table(self): h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5_empty_table_bug_619(self): """Successfully writes an empty OTU table in HDF5 format""" @@ -1062,14 +1062,14 @@ def test_to_hdf5_empty_table_bug_619(self): h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) t = example_table.filter({}, inplace=False) with NamedTemporaryFile(delete=False) as tmpfile: h5 = h5py.File(tmpfile.name, 'w') t.to_hdf5(h5, 'tests') h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5_missing_metadata_observation(self): # exercises a vlen_list @@ -1081,7 +1081,7 @@ def test_to_hdf5_missing_metadata_observation(self): with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) self.assertEqual(obs.metadata(axis='observation'), ({'taxonomy': None}, {'taxonomy': ['foo', 'baz']})) @@ -1096,7 +1096,7 @@ def test_to_hdf5_missing_metadata_sample(self): with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) self.assertEqual(obs.metadata(axis='sample'), ({'dat': ''}, {'dat': 'foo'})) @@ -1111,7 +1111,7 @@ def test_to_hdf5_inconsistent_metadata_categories_observation(self): with self.assertRaisesRegex(ValueError, 'inconsistent metadata'): t.to_hdf5(h5, 'tests') - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5_inconsistent_metadata_categories_sample(self): t = Table(np.array([[0, 1], [2, 3]]), ['a', 'b'], ['c', 'd'], @@ -1124,7 +1124,7 @@ def test_to_hdf5_inconsistent_metadata_categories_sample(self): with self.assertRaisesRegex(ValueError, 'inconsistent metadata'): t.to_hdf5(h5, 'tests') - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5_malformed_taxonomy(self): t = Table(np.array([[0, 1], [2, 3]]), ['a', 'b'], ['c', 'd'], @@ -1135,7 +1135,7 @@ def test_to_hdf5_malformed_taxonomy(self): with h5py.File(tmpfile.name, 'w') as h5: t.to_hdf5(h5, 'tests') obs = load_table(tmpfile.name) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) self.assertEqual(obs.metadata(axis='observation'), ({'taxonomy': ['foo', 'bar']}, {'taxonomy': ['foo', 'baz']})) @@ -1150,7 +1150,7 @@ def test_to_hdf5_general_fallback_to_list(self): h5 = h5py.File(tmpfile.name, 'w') st_rich.to_hdf5(h5, 'tests') h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5_custom_formatters(self): self.st_rich = Table(self.vals, @@ -1186,7 +1186,7 @@ def bc_formatter(grp, category, md, compression): self.assertNotEqual(m1['barcode'], m2['barcode']) self.assertEqual(m1['barcode'].lower(), m2['barcode']) h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_to_hdf5(self): """Write a file""" @@ -1208,7 +1208,7 @@ def test_to_hdf5(self): obs = Table.from_hdf5(h5) self.assertEqual(obs, self.st_rich) h5.close() - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) # Test with a collapsed table with NamedTemporaryFile(delete=False) as tmpfile: @@ -1254,7 +1254,7 @@ def bin_f(id_, x): [{'collapsed_ids': ['a', 'c']}, {'collapsed_ids': ['b']}]) self.assertEqual(obs, exp) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) # Test with table having a None on taxonomy with NamedTemporaryFile(delete=False) as tmpfile: @@ -1279,7 +1279,7 @@ def bin_f(id_, x): obs = Table.from_hdf5(h5) h5.close() self.assertEqual(obs, t) - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_from_tsv(self): tab1_fh = StringIO(otu_table1) diff --git a/biom/tests/test_util.py b/biom/tests/test_util.py index 76063fdd7..44bf2341d 100644 --- a/biom/tests/test_util.py +++ b/biom/tests/test_util.py @@ -42,6 +42,12 @@ class UtilTests(TestCase): def setUp(self): self.biom_otu_table1_w_tax = parse_biom_table(biom_otu_table1_w_tax) + self.to_remove = [] + + def tearDown(self): + if self.to_remove: + for f in self.to_remove: + os.remove(f) def test_generate_subsamples(self): table = Table(np.array([[3, 1, 1], [0, 3, 3]]), ['O1', 'O2'], @@ -253,7 +259,7 @@ def test_safe_md5(self): obs = safe_md5(open(tmp_f.name)) tmp_f.close() - os.unlink(tmp_f.name) + self.to_remove.append(tmp_f.name) self.assertEqual(obs, exp) obs = safe_md5(['foo\n']) @@ -268,7 +274,7 @@ def test_biom_open_hdf5_pathlib_write(self): with NamedTemporaryFile(delete=False) as tmpfile: with biom_open(pathlib.Path(tmpfile.name), 'w') as fp: t.to_hdf5(fp, 'tests') - os.unlink(tmpfile.name) + self.to_remove.append(tmpfile.name) def test_biom_open_hdf5_pathlib_read(self): cwd = os.getcwd() @@ -318,7 +324,7 @@ def test_load_classic(self): fp.flush() obs = load_table(fp.name) - os.unlink(fp.name) + self.to_remove.append(fp.name) npt.assert_equal(obs.ids(), tab.ids()) npt.assert_equal(obs.ids(axis='observation'), From 00a532ef3a5d87de95ee2432a07792295718a238 Mon Sep 17 00:00:00 2001 From: Matt Aton Date: Mon, 15 Apr 2024 10:40:25 -0700 Subject: [PATCH 11/12] Update changelog --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index 622634d5e..ab59f5878 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ biom 2.1.15-dev Performance improvements: +* Add Windows support. PR[#951](https://github.com/biocore/biom-format/pull/951) revises codebase to be Winows compatible and adds this support to the CI testing matrix. * Add NumPy 2.0 support. PR [#950](https://github.com/biocore/biom-format/pull/950) ensures code compatibility with NumPy 2.0. This support is yet to be added to the CI testing matrix. * Revise `Table._fast_merge` to use COO directly. For very large tables, this reduces runtime by ~50x and memory by ~5x. See PR [#913](https://github.com/biocore/biom-format/pull/933). * Drastically reduce the memory needs of subsampling when sums are large. Also adds 64-bit support. See PR [#935](https://github.com/biocore/biom-format/pull/935). From bf11bfd3c80cdf5f5e9ec6e78d4df1b31b724a3d Mon Sep 17 00:00:00 2001 From: Daniel McDonald Date: Mon, 15 Apr 2024 12:58:13 -0700 Subject: [PATCH 12/12] Update ChangeLog.md --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index ab59f5878..b2f8428b2 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,7 +6,7 @@ biom 2.1.15-dev Performance improvements: -* Add Windows support. PR[#951](https://github.com/biocore/biom-format/pull/951) revises codebase to be Winows compatible and adds this support to the CI testing matrix. +* Add Windows support. PR[#951](https://github.com/biocore/biom-format/pull/951) revises codebase to be Windows compatible and adds this support to the CI testing matrix. * Add NumPy 2.0 support. PR [#950](https://github.com/biocore/biom-format/pull/950) ensures code compatibility with NumPy 2.0. This support is yet to be added to the CI testing matrix. * Revise `Table._fast_merge` to use COO directly. For very large tables, this reduces runtime by ~50x and memory by ~5x. See PR [#913](https://github.com/biocore/biom-format/pull/933). * Drastically reduce the memory needs of subsampling when sums are large. Also adds 64-bit support. See PR [#935](https://github.com/biocore/biom-format/pull/935).