From 118b8999ced58adeb69b9aa4e1d94190b959654e Mon Sep 17 00:00:00 2001 From: baugstein <87702063+ucapbba@users.noreply.github.com> Date: Sat, 8 Mar 2025 13:38:42 +0000 Subject: [PATCH 01/10] #247 Update io.py raise KeyError to allow to work with ChainMap --- heracles/io.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/heracles/io.py b/heracles/io.py index 90044a8f..802b967e 100644 --- a/heracles/io.py +++ b/heracles/io.py @@ -618,7 +618,10 @@ def __getitem__(self, key): data = self._cache.get(ext) if data is None: with self.fits as fits: - data = self.reader(fits[ext]) + try: + data = self.reader(fits[ext]) + except Exception: + raise KeyError(ext) self._cache[ext] = data return data From 9c5e4d9bdfdc4041cf7a1ee9f526fbb715d8bad6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 8 Mar 2025 13:41:26 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- heracles/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/heracles/io.py b/heracles/io.py index 802b967e..c1c9bb4c 100644 --- a/heracles/io.py +++ b/heracles/io.py @@ -619,9 +619,9 @@ def __getitem__(self, key): if data is None: with self.fits as fits: try: - data = self.reader(fits[ext]) + data = self.reader(fits[ext]) except Exception: - raise KeyError(ext) + raise KeyError(ext) self._cache[ext] = data return data From acebccc060e4fe47ccafbe738a4bd41c5a17e208 Mon Sep 17 00:00:00 2001 From: Bradley Augstein Date: Sun, 9 Mar 2025 15:42:29 +0000 Subject: [PATCH 03/10] #247 Test case for chaining almfits --- tests/test_io.py | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/tests/test_io.py b/tests/test_io.py index 239ab56d..675b526b 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,19 +7,26 @@ def zbins(): return {0: (0.0, 0.8), 1: (1.0, 1.2)} +@pytest.fixture +def zbins2(): + return {2: (0.0, 0.8), 3: (1.0, 1.2)} @pytest.fixture def mock_alms(rng, zbins): - import numpy as np + return generate_mock_alms(rng, zbins) - lmax = 32 +@pytest.fixture +def mock_alms2(rng, zbins2): + return generate_mock_alms(rng, zbins2) +def generate_mock_alms(rng, zbins): + import numpy as np + lmax = 32 Nlm = (lmax + 1) * (lmax + 2) // 2 - # names and spins fields = {"P": 0, "G": 2} - alms = {} + for n, s in fields.items(): shape = (Nlm, 2) if s == 0 else (2, Nlm, 2) for i in zbins: @@ -29,7 +36,6 @@ def mock_alms(rng, zbins): return alms - @pytest.fixture def mock_cls(rng): import numpy as np @@ -258,16 +264,42 @@ def test_write_read_alms(mock_alms, tmp_path): import numpy as np path = tmp_path / "alms.fits" - heracles.write_alms(path, mock_alms) assert path.exists() - alms = heracles.read_alms(path) + alms = heracles.read_alms(path) assert alms.keys() == mock_alms.keys() + for key in mock_alms: np.testing.assert_array_equal(mock_alms[key], alms[key]) assert mock_alms[key].dtype.metadata == alms[key].dtype.metadata +def test_chain_almfits(mock_alms,mock_alms2, tmp_path): + from heracles.io import AlmFits + from collections import ChainMap + import numpy as np + path1 = tmp_path / "alms1.fits" + path2 = tmp_path / "alms2.fits" + + heracles.write_alms(path1, mock_alms) + heracles.write_alms(path2, mock_alms2) + + assert path1.exists() + assert path2.exists() + + alms1 = AlmFits(path1, clobber=False) + alms2 = AlmFits(path2, clobber=False) + chained_alms = ChainMap() + + chained_alms.maps.append(alms1) + chained_alms.maps.append(alms2) + + for key in mock_alms: + np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) + assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata + for key in mock_alms2: + np.testing.assert_array_equal(mock_alms2[key], chained_alms[key]) + assert mock_alms2[key].dtype.metadata == chained_alms[key].dtype.metadata def test_write_read_cls(mock_cls, tmp_path): import numpy as np From 4c02ae8386e0b0a213dc1589d33009834a5b4293 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 9 Mar 2025 15:42:41 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_io.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/test_io.py b/tests/test_io.py index 675b526b..4d36d357 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,20 +7,25 @@ def zbins(): return {0: (0.0, 0.8), 1: (1.0, 1.2)} + @pytest.fixture def zbins2(): return {2: (0.0, 0.8), 3: (1.0, 1.2)} + @pytest.fixture def mock_alms(rng, zbins): return generate_mock_alms(rng, zbins) + @pytest.fixture def mock_alms2(rng, zbins2): return generate_mock_alms(rng, zbins2) + def generate_mock_alms(rng, zbins): import numpy as np + lmax = 32 Nlm = (lmax + 1) * (lmax + 2) // 2 @@ -36,6 +41,7 @@ def generate_mock_alms(rng, zbins): return alms + @pytest.fixture def mock_cls(rng): import numpy as np @@ -274,33 +280,36 @@ def test_write_read_alms(mock_alms, tmp_path): np.testing.assert_array_equal(mock_alms[key], alms[key]) assert mock_alms[key].dtype.metadata == alms[key].dtype.metadata -def test_chain_almfits(mock_alms,mock_alms2, tmp_path): + +def test_chain_almfits(mock_alms, mock_alms2, tmp_path): from heracles.io import AlmFits from collections import ChainMap import numpy as np + path1 = tmp_path / "alms1.fits" path2 = tmp_path / "alms2.fits" heracles.write_alms(path1, mock_alms) heracles.write_alms(path2, mock_alms2) - + assert path1.exists() assert path2.exists() alms1 = AlmFits(path1, clobber=False) alms2 = AlmFits(path2, clobber=False) chained_alms = ChainMap() - + chained_alms.maps.append(alms1) chained_alms.maps.append(alms2) - + for key in mock_alms: - np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) - assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata + np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) + assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata for key in mock_alms2: np.testing.assert_array_equal(mock_alms2[key], chained_alms[key]) assert mock_alms2[key].dtype.metadata == chained_alms[key].dtype.metadata + def test_write_read_cls(mock_cls, tmp_path): import numpy as np From 9fa3f0dc6c2a958bf2cbbc4eec360cb78095a153 Mon Sep 17 00:00:00 2001 From: Bradley Augstein Date: Tue, 11 Mar 2025 17:05:22 +0000 Subject: [PATCH 05/10] #247 explicit throw KeyError on bad key in FitsDict and dedicated test --- heracles/io.py | 8 ++++---- tests/test_io.py | 38 ++++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/heracles/io.py b/heracles/io.py index c1c9bb4c..dd4a6c31 100644 --- a/heracles/io.py +++ b/heracles/io.py @@ -618,10 +618,10 @@ def __getitem__(self, key): data = self._cache.get(ext) if data is None: with self.fits as fits: - try: - data = self.reader(fits[ext]) - except Exception: - raise KeyError(ext) + if ext in fits: + data = self.reader(fits[ext]) + else: + raise KeyError(ext) self._cache[ext] = data return data diff --git a/tests/test_io.py b/tests/test_io.py index 4d36d357..eef3caf1 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,25 +7,20 @@ def zbins(): return {0: (0.0, 0.8), 1: (1.0, 1.2)} - @pytest.fixture def zbins2(): return {2: (0.0, 0.8), 3: (1.0, 1.2)} - @pytest.fixture def mock_alms(rng, zbins): return generate_mock_alms(rng, zbins) - @pytest.fixture def mock_alms2(rng, zbins2): return generate_mock_alms(rng, zbins2) - def generate_mock_alms(rng, zbins): import numpy as np - lmax = 32 Nlm = (lmax + 1) * (lmax + 2) // 2 @@ -41,7 +36,6 @@ def generate_mock_alms(rng, zbins): return alms - @pytest.fixture def mock_cls(rng): import numpy as np @@ -280,36 +274,52 @@ def test_write_read_alms(mock_alms, tmp_path): np.testing.assert_array_equal(mock_alms[key], alms[key]) assert mock_alms[key].dtype.metadata == alms[key].dtype.metadata +def test_fitsDictError(mock_alms,tmp_path): + from heracles.io import FitsDict + from pathlib import Path + tmp_path = Path(tmp_path) + path1 = tmp_path / "alms1.fits" -def test_chain_almfits(mock_alms, mock_alms2, tmp_path): + heracles.write_alms(path1, mock_alms) + + assert path1.exists() + + alms1 = FitsDict(path1, clobber=False) + + # Ensure bad key raises a key error + with pytest.raises(KeyError): + _ = alms1['badkey'] + + # Ensure an existing key does NOT raise an error + _ = alms1[('P',0)] + +def test_chain_almfits(mock_alms,mock_alms2, tmp_path): from heracles.io import AlmFits from collections import ChainMap import numpy as np - path1 = tmp_path / "alms1.fits" path2 = tmp_path / "alms2.fits" heracles.write_alms(path1, mock_alms) heracles.write_alms(path2, mock_alms2) - + assert path1.exists() assert path2.exists() alms1 = AlmFits(path1, clobber=False) alms2 = AlmFits(path2, clobber=False) chained_alms = ChainMap() - + chained_alms.maps.append(alms1) chained_alms.maps.append(alms2) - + for key in mock_alms: - np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) - assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata + np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) + assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata for key in mock_alms2: np.testing.assert_array_equal(mock_alms2[key], chained_alms[key]) assert mock_alms2[key].dtype.metadata == chained_alms[key].dtype.metadata - def test_write_read_cls(mock_cls, tmp_path): import numpy as np From bc2ebb1d952e3872e4b91f749eda6f11580fbdc9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:07:02 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- heracles/io.py | 8 ++++---- tests/test_io.py | 37 ++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/heracles/io.py b/heracles/io.py index dd4a6c31..cdce85bc 100644 --- a/heracles/io.py +++ b/heracles/io.py @@ -618,10 +618,10 @@ def __getitem__(self, key): data = self._cache.get(ext) if data is None: with self.fits as fits: - if ext in fits: - data = self.reader(fits[ext]) - else: - raise KeyError(ext) + if ext in fits: + data = self.reader(fits[ext]) + else: + raise KeyError(ext) self._cache[ext] = data return data diff --git a/tests/test_io.py b/tests/test_io.py index eef3caf1..0a328c72 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,20 +7,25 @@ def zbins(): return {0: (0.0, 0.8), 1: (1.0, 1.2)} + @pytest.fixture def zbins2(): return {2: (0.0, 0.8), 3: (1.0, 1.2)} + @pytest.fixture def mock_alms(rng, zbins): return generate_mock_alms(rng, zbins) + @pytest.fixture def mock_alms2(rng, zbins2): return generate_mock_alms(rng, zbins2) + def generate_mock_alms(rng, zbins): import numpy as np + lmax = 32 Nlm = (lmax + 1) * (lmax + 2) // 2 @@ -36,6 +41,7 @@ def generate_mock_alms(rng, zbins): return alms + @pytest.fixture def mock_cls(rng): import numpy as np @@ -274,52 +280,57 @@ def test_write_read_alms(mock_alms, tmp_path): np.testing.assert_array_equal(mock_alms[key], alms[key]) assert mock_alms[key].dtype.metadata == alms[key].dtype.metadata -def test_fitsDictError(mock_alms,tmp_path): + +def test_fitsDictError(mock_alms, tmp_path): from heracles.io import FitsDict from pathlib import Path - tmp_path = Path(tmp_path) + + tmp_path = Path(tmp_path) path1 = tmp_path / "alms1.fits" heracles.write_alms(path1, mock_alms) - + assert path1.exists() - + alms1 = FitsDict(path1, clobber=False) - + # Ensure bad key raises a key error with pytest.raises(KeyError): - _ = alms1['badkey'] + _ = alms1["badkey"] # Ensure an existing key does NOT raise an error - _ = alms1[('P',0)] + _ = alms1[("P", 0)] -def test_chain_almfits(mock_alms,mock_alms2, tmp_path): + +def test_chain_almfits(mock_alms, mock_alms2, tmp_path): from heracles.io import AlmFits from collections import ChainMap import numpy as np + path1 = tmp_path / "alms1.fits" path2 = tmp_path / "alms2.fits" heracles.write_alms(path1, mock_alms) heracles.write_alms(path2, mock_alms2) - + assert path1.exists() assert path2.exists() alms1 = AlmFits(path1, clobber=False) alms2 = AlmFits(path2, clobber=False) chained_alms = ChainMap() - + chained_alms.maps.append(alms1) chained_alms.maps.append(alms2) - + for key in mock_alms: - np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) - assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata + np.testing.assert_array_equal(mock_alms[key], chained_alms[key]) + assert mock_alms[key].dtype.metadata == chained_alms[key].dtype.metadata for key in mock_alms2: np.testing.assert_array_equal(mock_alms2[key], chained_alms[key]) assert mock_alms2[key].dtype.metadata == chained_alms[key].dtype.metadata + def test_write_read_cls(mock_cls, tmp_path): import numpy as np From fabb640b737dbcd2ae791431e27c6d445968604d Mon Sep 17 00:00:00 2001 From: baugstein <87702063+ucapbba@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:48:04 +0000 Subject: [PATCH 07/10] Update test_result.py random number dependent error fixed --- tests/test_result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_result.py b/tests/test_result.py index 3b1935fe..921536a9 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -88,7 +88,7 @@ def test_result_2d(rng): @pytest.mark.parametrize("weight", [None, "l(l+1)", "2l+1", ""]) @pytest.mark.parametrize("ndim,axis", [(1, 0), (2, 0), (3, 1)]) def test_binned(ndim, axis, weight, rng): - shape = rng.integers(0, 100, ndim) + shape = rng.integers(1, 100, ndim) lmax = shape[axis] - 1 data = heracles.Result(rng.standard_normal(shape), axis=axis) From e86af0964cffa59582ea03c762eaa741964255bb Mon Sep 17 00:00:00 2001 From: baugstein <87702063+ucapbba@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:08:39 +0000 Subject: [PATCH 08/10] Update tests/test_io.py as recommended Co-authored-by: Nicolas Tessore --- tests/test_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_io.py b/tests/test_io.py index 0a328c72..27dc1882 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -281,7 +281,7 @@ def test_write_read_alms(mock_alms, tmp_path): assert mock_alms[key].dtype.metadata == alms[key].dtype.metadata -def test_fitsDictError(mock_alms, tmp_path): +def test_fits_dict_keyerror(mock_alms, tmp_path): from heracles.io import FitsDict from pathlib import Path From ea8641f66f801505fda7a9cd7612113f7ac71030 Mon Sep 17 00:00:00 2001 From: baugstein <87702063+ucapbba@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:11:21 +0000 Subject: [PATCH 09/10] Update heracles/io.py as requested Co-authored-by: Nicolas Tessore --- heracles/io.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/heracles/io.py b/heracles/io.py index cdce85bc..03eed048 100644 --- a/heracles/io.py +++ b/heracles/io.py @@ -618,10 +618,9 @@ def __getitem__(self, key): data = self._cache.get(ext) if data is None: with self.fits as fits: - if ext in fits: - data = self.reader(fits[ext]) - else: + if ext not in fits: raise KeyError(ext) + data = self.reader(fits[ext]) self._cache[ext] = data return data From 42e8be723aac837d2ce732a63cde0b03e0ee8365 Mon Sep 17 00:00:00 2001 From: Nicolas Tessore Date: Thu, 13 Mar 2025 09:54:56 +0000 Subject: [PATCH 10/10] fix coveralls parallel build --- .github/workflows/tests.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d270069b..220d50d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -49,8 +49,22 @@ jobs: cache-dependency-path: "pyproject.toml" - run: sudo apt-get install -y libbz2-dev # required to build fitsio - run: pip install -c .github/test-constraints.txt '.[test]' - - run: pytest --cov=heracles --cov-report=lcov + - run: pytest --cov=heracles --cov-report=xml - uses: coverallsapp/github-action@v2 + with: + parallel: true + flag-name: run-${{ matrix.python-version }} + + finish: + name: Finish + runs-on: ubuntu-latest + needs: test + if: always() + steps: + - uses: coverallsapp/github-action@v2 + with: + parallel-finished: true + carryforward: "run-3.9,run-3.10,run-3.11,run-3.12,run-3.13" build: name: Build