From f106360252f1223b0b521972eb247f447ef3add6 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Tue, 14 Apr 2026 16:06:26 +0100 Subject: [PATCH 01/10] Add missing ESMPy module availability test skips --- cf/test/test_RegridOperator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cf/test/test_RegridOperator.py b/cf/test/test_RegridOperator.py index 28cf79f215..7367ab1e25 100644 --- a/cf/test/test_RegridOperator.py +++ b/cf/test/test_RegridOperator.py @@ -55,6 +55,7 @@ def test_RegridOperator_attributes(self): def test_RegridOperator_copy(self): self.assertIsInstance(self.r.copy(), self.r.__class__) + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_equal_weights(self): r0 = self.r r1 = r0.copy() @@ -62,6 +63,7 @@ def test_RegridOperator_equal_weights(self): r1.weights.data += 0.1 self.assertFalse(r0.equal_weights(r1)) + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_equal_dst_mask(self): r0 = self.r.copy() r1 = r0.copy() From 2e885f2f037dd52d13f01d5c97a26f44be521a6e Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Tue, 14 Apr 2026 16:25:06 +0100 Subject: [PATCH 02/10] Add test_Domain healpix module availability test skipping --- cf/test/test_Domain.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cf/test/test_Domain.py b/cf/test/test_Domain.py index 15e492b140..2e760fed3c 100644 --- a/cf/test/test_Domain.py +++ b/cf/test/test_Domain.py @@ -1,11 +1,18 @@ import datetime import re import unittest +from importlib.util import find_spec import numpy as np import cf +healpix_available = False +# Note: here only need healpix for cf under-the-hood code, not in test +# directly, so no need to actually import healpix, just test it is there. +if find_spec("healpix"): + healpix_available = True + class DomainTest(unittest.TestCase): d = cf.example_field(1).domain @@ -494,6 +501,7 @@ def test_Domain_cyclic_iscyclic(self): d2.cyclic("X", iscyclic=False) self.assertTrue(d2.iscyclic("X")) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Domain_create_healpix(self): """Test Domain.create_healpix.""" d = cf.Domain.create_healpix(0) From 44357b8611c5a3bbbd16042840c68519c4648e20 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Tue, 14 Apr 2026 16:35:55 +0100 Subject: [PATCH 03/10] Add test_weights healpix module availability test skipping --- cf/test/test_weights.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cf/test/test_weights.py b/cf/test/test_weights.py index 5043a6b796..90e4ebd70f 100644 --- a/cf/test/test_weights.py +++ b/cf/test/test_weights.py @@ -1,10 +1,17 @@ import datetime import unittest +from importlib.util import find_spec import numpy as np import cf +healpix_available = False +# Note: here only need healpix for cf under-the-hood code, not in test +# directly, so no need to actually import healpix, just test it is there. +if find_spec("healpix"): + healpix_available = True + # A radius greater than 1. Used since weights based on the unit # sphere and non-spheres are tested separately. r = 2 @@ -152,6 +159,7 @@ def test_weights_polygon_area_geometry(self): self.assertTrue((w.array == correct_weights).all()) self.assertEqual(w.Units, cf.Units("m2")) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_weights_polygon_area_ugrid(self): f = cf.example_field(8) f = f[..., [0, 2]] @@ -345,6 +353,7 @@ def test_weights_exceptions(self): ): f.weights("area") + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_weights_healpix(self): """Test HEALPix weights.""" # HEALPix grid with Multi-Order Coverage (a combination of From b39b4023a9a44cecffe2fbccbeb4bf38837f8773 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Tue, 14 Apr 2026 16:50:25 +0100 Subject: [PATCH 04/10] Add healpix module availability test skips to field-related tests --- cf/test/test_Field.py | 12 ++++++++++++ cf/test/test_collapse.py | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index 8b60ba5b06..d399f3e18f 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -16,6 +16,12 @@ import cf +healpix_available = False +# Note: here only need healpix for cf under-the-hood code, not in test +# directly, so no need to actually import healpix, just test it is there. +if find_spec("healpix"): + healpix_available = True + n_tmpfiles = 1 tmpfiles = [ tempfile.mkstemp("_test_Field.nc", dir=os.getcwd())[1] @@ -3137,6 +3143,7 @@ def test_Field_to_units(self): with self.assertRaises(ValueError): g.to_units("degC") + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_healpix_change_indexing_scheme(self): """Test Field.healpix_change_indexing_scheme.""" # HEALPix field @@ -3236,6 +3243,7 @@ def test_Field_healpix_change_indexing_scheme(self): with self.assertRaises(ValueError): self.f0.healpix_change_indexing_scheme("ring") + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_healpix_to_ugrid(self): """Test Field.healpix_to_ugrid.""" # HEALPix field @@ -3276,6 +3284,7 @@ def test_Field_healpix_to_ugrid(self): with self.assertRaises(ValueError): self.f0.healpix_to_ugrid() + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_create_latlon_coordinates(self): """Test Field.create_latlon_coordinates.""" # ------------------------------------------------------------ @@ -3334,6 +3343,7 @@ def test_Field_create_latlon_coordinates(self): self.assertTrue(mc[:16].equals(l2.auxiliary_coordinate(c)[:16])) self.assertTrue(mc[16:].equals(l1.auxiliary_coordinate(c)[4:])) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_healpix_subspace(self): """Test Field.subspace for HEALPix grids""" f = self.f12 @@ -3369,6 +3379,7 @@ def test_Field_healpix_subspace(self): np.array_equal(g.coordinate("healpix_index"), [13, 12]) ) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_healpix_decrease_refinement_level(self): """Test Field.healpix_decrease_refinement_level.""" f = self.f12 @@ -3469,6 +3480,7 @@ def my_mean(a, axis=None): with self.assertRaises(ValueError): self.f0.healpix_decrease_refinement_level(0, "mean") + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_healpix_increase_refinement_level(self): """Test Field.healpix_increase_refinement_level.""" f = self.f12 diff --git a/cf/test/test_collapse.py b/cf/test/test_collapse.py index 2dde8679c5..ce7fc5bd52 100644 --- a/cf/test/test_collapse.py +++ b/cf/test/test_collapse.py @@ -4,6 +4,7 @@ import os import tempfile import unittest +from importlib.util import find_spec import numpy as np @@ -11,6 +12,12 @@ import cf +healpix_available = False +# Note: here only need healpix for cf under-the-hood code, not in test +# directly, so no need to actually import healpix, just test it is there. +if find_spec("healpix"): + healpix_available = True + n_tmpfiles = 1 tmpfiles = [ tempfile.mkstemp("_test_collapse.nc", dir=os.getcwd())[1] @@ -825,6 +832,7 @@ def test_Field_collapse_ugrid(self): # Check the collpsed fields writes cf.write(f, tmpfile) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_Field_collapse_HEALPix(self): """Test HEALPix collapses.""" f0 = cf.example_field(12) From d77c4243ebdbbb624e6551baf160efaf2884501b Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Tue, 14 Apr 2026 17:27:17 +0100 Subject: [PATCH 05/10] Add module availability module-level test skip for test_HEALPix_utils --- .flake8 | 3 ++ cf/test/test_HEALPix_utils.py | 52 +++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/.flake8 b/.flake8 index 75d2e1a211..0ebbb3f85c 100644 --- a/.flake8 +++ b/.flake8 @@ -30,3 +30,6 @@ per-file-ignores = # https://stackoverflow.com/questions/59167405/) __init__.py: F401 */__init__.py: F401 + # Whole-module test skip means exit before reach undefined vars where need + # given (healpix) module for defining those. Is cleanest way but needs: + cf/test/test_HEALPix_utils.py: F821 diff --git a/cf/test/test_HEALPix_utils.py b/cf/test/test_HEALPix_utils.py index bec272cda6..514b9f7014 100644 --- a/cf/test/test_HEALPix_utils.py +++ b/cf/test/test_HEALPix_utils.py @@ -1,33 +1,49 @@ import datetime import unittest -import healpix import numpy as np import cf -# Create matching lists of selected nested, ring, nuniq and zuniq -# indices for every refinement level. -indices = [ - (r, i, healpix.nest2ring(healpix.order2nside(r), i)) - for r in range(30) - for i in (0, 7, (12 * 4**r) - 1) -] -refinement_levels, nested_indices, ring_indices = map(list, zip(*indices)) - -nuniq_indices = [ - i + 4 ** (1 + r) for r, i in zip(refinement_levels, nested_indices) -] - -zuniq_indices = [ - (2 * i + 1) * 4 ** (29 - r) - for r, i in zip(refinement_levels, nested_indices) -] +healpix_imported = True +try: + import healpix # noqa: F401 +except ImportError: + healpix_imported = False class DataTest(unittest.TestCase): """Unit tests for HEALPix utilities.""" + def setUp(self): + """Preparations called immediately before each test method.""" + # Skip all if healpix module not available! + if not healpix_imported: + self.skipTest( + "Test module requires 'healpix' package. Install it to run all." + ) + else: + # Create matching lists of selected nested, ring, nuniq and zuniq + # indices for every refinement level. + indices = [ + (r, i, healpix.nest2ring(healpix.order2nside(r), i)) + for r in range(30) + for i in (0, 7, (12 * 4**r) - 1) + ] + refinement_levels, nested_indices, ring_indices = map( + list, zip(*indices) + ) + + nuniq_indices = [ # noqa: F841 + i + 4 ** (1 + r) + for r, i in zip(refinement_levels, nested_indices) + ] + + zuniq_indices = [ # noqa: F841 + (2 * i + 1) * 4 ** (29 - r) + for r, i in zip(refinement_levels, nested_indices) + ] + def test_HEALPix_uniq2zuniq(self): """Test _uniq2zuniq""" from cf.data.dask_utils_healpix import _uniq2zuniq From cad2ba47e5ace86e486e202eb20fd757d29a16cd Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Wed, 15 Apr 2026 16:28:34 +0100 Subject: [PATCH 06/10] Add subtle esmpy availability test_regrid skip to avoid false positive --- cf/test/test_regrid.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cf/test/test_regrid.py b/cf/test/test_regrid.py index f5d4675ca4..3d6bf98e04 100644 --- a/cf/test/test_regrid.py +++ b/cf/test/test_regrid.py @@ -11,6 +11,12 @@ import cf +esmpy_imported = True +try: + import esmpy # noqa: F401 +except ImportError: + esmpy_imported = False + n_tmpfiles = 1 tmpfiles = [ tempfile.mkstemp("_test_regrid.nc", dir=os.getcwd())[1] @@ -30,14 +36,6 @@ def _remove_tmpfiles(): atexit.register(_remove_tmpfiles) - -esmpy_imported = True -try: - import esmpy # noqa: F401 -except ImportError: - esmpy_imported = False - - all_methods = ( "linear", "conservative", @@ -405,6 +403,7 @@ def test_Field_regridc_2d_coords(self): d1 = src.regridc(r) self.assertTrue(d1.data.equals(d0.data, atol=atol, rtol=rtol)) + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_bad_dst(self): """Disallowed destination grid types raise an exception.""" self.assertFalse(cf.regrid_logging()) From 45dbe80d12509370003f2436535deb43c94aa96e Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Wed, 15 Apr 2026 16:37:53 +0100 Subject: [PATCH 07/10] Convert test_regrid esmpy availability test skips to module-level --- .flake8 | 5 +++-- cf/test/test_regrid.py | 43 +++++++++++++++++++----------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/.flake8 b/.flake8 index 0ebbb3f85c..e1881075ff 100644 --- a/.flake8 +++ b/.flake8 @@ -30,6 +30,7 @@ per-file-ignores = # https://stackoverflow.com/questions/59167405/) __init__.py: F401 */__init__.py: F401 - # Whole-module test skip means exit before reach undefined vars where need - # given (healpix) module for defining those. Is cleanest way but needs: + # Whole-module test skips (via setUp method) for these cases means exit + # before reach undefined vars where need given module for defining those. + # This is the cleanest way but sets off F821 so skip those: cf/test/test_HEALPix_utils.py: F821 diff --git a/cf/test/test_regrid.py b/cf/test/test_regrid.py index 3d6bf98e04..b523f25191 100644 --- a/cf/test/test_regrid.py +++ b/cf/test/test_regrid.py @@ -138,19 +138,27 @@ def esmpy_regrid_Nd(coord_sys, method, src, dst, **kwargs): class RegridTest(unittest.TestCase): - # Get the test source and destination fields - filename = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "regrid.nc" - ) - dst_src = cf.read(filename) - dst = dst_src[0] - src = dst_src[1] - filename_xyz = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "regrid_xyz.nc" - ) + def setUp(self): + """Preparations called immediately before each test method.""" + # Skip all if healpix module not available! + if not esmpy_imported: + self.skipTest( + "Test module requires 'esmpy' package. Install it to run all." + ) + else: + # Get the test source and destination fields + filename = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "regrid.nc" + ) + dst_src = cf.read(filename) + dst = dst_src[0] # noqa: F841 + src = dst_src[1] # noqa: F841 + + filename_xyz = os.path.join( # noqa: F841 + os.path.dirname(os.path.abspath(__file__)), "regrid_xyz.nc" + ) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrid_2d_field(self): """2-d regridding with Field destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -302,7 +310,6 @@ def test_Field_regrid_2d_field(self): with self.assertRaises(ValueError): src.regrids(dst, method=method).array - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_coords(self): """Spherical regridding with coords destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -372,7 +379,6 @@ def test_Field_regrids_coords(self): d1 = src.regrids(r) self.assertTrue(d1.data.equals(d0.data, atol=atol, rtol=rtol)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regridc_2d_coords(self): """2-d Cartesian regridding with coords destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -403,7 +409,6 @@ def test_Field_regridc_2d_coords(self): d1 = src.regridc(r) self.assertTrue(d1.data.equals(d0.data, atol=atol, rtol=rtol)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_bad_dst(self): """Disallowed destination grid types raise an exception.""" self.assertFalse(cf.regrid_logging()) @@ -417,7 +422,6 @@ def test_Field_regrids_bad_dst(self): with self.assertRaises(ValueError): self.src.regrids("foobar", method="conservative") - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_domain(self): """Spherical regridding with Domain destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -441,7 +445,6 @@ def test_Field_regrids_domain(self): d1 = src.regrids(r) self.assertTrue(d1.equals(d0, atol=atol, rtol=rtol)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regridc_domain(self): """Spherical regridding with Domain destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -467,7 +470,6 @@ def test_Field_regridc_domain(self): d1 = src.regridc(r) self.assertTrue(d1.equals(d0, atol=atol, rtol=rtol)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_field_operator(self): """Spherical regridding with operator destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -505,7 +507,6 @@ def test_Field_regrids_field_operator(self): with self.assertRaises(ValueError): dst.regrids(r) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrids_non_coordinates(self): """Check setting of non-coordinate metadata.""" self.assertFalse(cf.regrid_logging()) @@ -554,7 +555,6 @@ def test_Field_regrids_non_coordinates(self): # Cell measures self.assertFalse(d1.cell_measures()) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regridc_3d_field(self): """3-d Cartesian regridding with Field destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -658,7 +658,6 @@ def test_Field_regridc_3d_field(self): with self.assertRaises(ValueError): src.regridc(dst, method=method, axes=axes) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regridc_1d_field(self): """1-d Cartesian regridding with Field destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -749,7 +748,6 @@ def test_Field_regridc_1d_field(self): with self.assertRaises(ValueError): src.regridc(dst, method=method, axes=axes) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regridc_1d_coordinates_z(self): """1-d Z Cartesian regridding with coordinates destination grid.""" self.assertFalse(cf.regrid_logging()) @@ -762,7 +760,6 @@ def test_Field_regridc_1d_coordinates_z(self): z = d.dimension_coordinate("Z") self.assertTrue(z.data.equals(dst.data)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrid_chunks(self): """Regridding of chunked axes""" self.assertFalse(cf.regrid_logging()) @@ -779,7 +776,6 @@ def test_Field_regrid_chunks(self): d0 = src.regrids(dst, method="linear") self.assertEqual(d0.data.numblocks, (1, 1, 1)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_Field_regrid_weights_file(self): """Regridding creation/use of weights file""" self.assertFalse(cf.regrid_logging()) @@ -815,7 +811,6 @@ def test_Field_regrid_weights_file(self): src.regrids(r1, method="linear", weights_file=tmpfile) ) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_return_esmpy_regrid_operator(self): """esmpy regrid operator returns esmpy.Regrid in regrids and regridc""" self.assertFalse(cf.regrid_logging()) From 7567ac761acd6ebad14f5aee8255c08a1cc738c9 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Wed, 15 Apr 2026 16:59:25 +0100 Subject: [PATCH 08/10] Add last required healpix availability skip from full test suite run --- cf/test/test_functions.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cf/test/test_functions.py b/cf/test/test_functions.py index 98d186d02a..eb1c984d44 100644 --- a/cf/test/test_functions.py +++ b/cf/test/test_functions.py @@ -4,6 +4,7 @@ import platform import sys import unittest +from importlib.util import find_spec import dask.array as da import numpy as np @@ -12,6 +13,12 @@ import cf +healpix_available = False +# Note: here only need healpix for cf under-the-hood code, not in test +# directly, so no need to actually import healpix, just test it is there. +if find_spec("healpix"): + healpix_available = True + class functionTest(unittest.TestCase): def setUp(self): @@ -487,6 +494,7 @@ def test_normalize_slice(self): with self.assertRaises(IndexError): cf.normalize_slice(index, 8, cyclic=True) + @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") def test_locate(self): """Test cf.locate""" # HEALPix From f674572880d271e8e49b7c57980f47586a0ee16e Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Wed, 15 Apr 2026 17:12:21 +0100 Subject: [PATCH 09/10] Tidy test skip on lone methods to avoid use of flag --- .flake8 | 2 +- cf/test/test_Domain.py | 10 +++------- cf/test/test_collapse.py | 10 +++------- cf/test/test_functions.py | 10 +++------- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/.flake8 b/.flake8 index e1881075ff..fc7bb54da2 100644 --- a/.flake8 +++ b/.flake8 @@ -32,5 +32,5 @@ per-file-ignores = */__init__.py: F401 # Whole-module test skips (via setUp method) for these cases means exit # before reach undefined vars where need given module for defining those. - # This is the cleanest way but sets off F821 so skip those: + # This is the cleanest way but sets off F821 code en-masse so ignore that. cf/test/test_HEALPix_utils.py: F821 diff --git a/cf/test/test_Domain.py b/cf/test/test_Domain.py index 2e760fed3c..59c69c0f22 100644 --- a/cf/test/test_Domain.py +++ b/cf/test/test_Domain.py @@ -7,12 +7,6 @@ import cf -healpix_available = False -# Note: here only need healpix for cf under-the-hood code, not in test -# directly, so no need to actually import healpix, just test it is there. -if find_spec("healpix"): - healpix_available = True - class DomainTest(unittest.TestCase): d = cf.example_field(1).domain @@ -501,7 +495,9 @@ def test_Domain_cyclic_iscyclic(self): d2.cyclic("X", iscyclic=False) self.assertTrue(d2.iscyclic("X")) - @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") + # Note: here only need healpix for cf under-the-hood code, not in test + # directly, so no need to actually import healpix, just test it is there. + @unittest.skipUnless(find_spec("healpix"), "Requires 'healpix' package.") def test_Domain_create_healpix(self): """Test Domain.create_healpix.""" d = cf.Domain.create_healpix(0) diff --git a/cf/test/test_collapse.py b/cf/test/test_collapse.py index ce7fc5bd52..b3f73f4524 100644 --- a/cf/test/test_collapse.py +++ b/cf/test/test_collapse.py @@ -12,12 +12,6 @@ import cf -healpix_available = False -# Note: here only need healpix for cf under-the-hood code, not in test -# directly, so no need to actually import healpix, just test it is there. -if find_spec("healpix"): - healpix_available = True - n_tmpfiles = 1 tmpfiles = [ tempfile.mkstemp("_test_collapse.nc", dir=os.getcwd())[1] @@ -832,7 +826,9 @@ def test_Field_collapse_ugrid(self): # Check the collpsed fields writes cf.write(f, tmpfile) - @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") + # Note: here only need healpix for cf under-the-hood code, not in test + # directly, so no need to actually import healpix, just test it is there. + @unittest.skipUnless(find_spec("healpix"), "Requires 'healpix' package.") def test_Field_collapse_HEALPix(self): """Test HEALPix collapses.""" f0 = cf.example_field(12) diff --git a/cf/test/test_functions.py b/cf/test/test_functions.py index eb1c984d44..b686a98b40 100644 --- a/cf/test/test_functions.py +++ b/cf/test/test_functions.py @@ -13,12 +13,6 @@ import cf -healpix_available = False -# Note: here only need healpix for cf under-the-hood code, not in test -# directly, so no need to actually import healpix, just test it is there. -if find_spec("healpix"): - healpix_available = True - class functionTest(unittest.TestCase): def setUp(self): @@ -494,7 +488,9 @@ def test_normalize_slice(self): with self.assertRaises(IndexError): cf.normalize_slice(index, 8, cyclic=True) - @unittest.skipUnless(healpix_available, "Requires 'healpix' package.") + # Note: here only need healpix for cf under-the-hood code, not in test + # directly, so no need to actually import healpix, just test it is there. + @unittest.skipUnless(find_spec("healpix"), "Requires 'healpix' package.") def test_locate(self): """Test cf.locate""" # HEALPix From 2088b7e9851bd6abd6ff4cb1d2f0b1eeef1c744f Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Wed, 15 Apr 2026 17:14:39 +0100 Subject: [PATCH 10/10] Convert test skipping to module-level for test_RegridOperator --- cf/test/test_RegridOperator.py | 16 +++++++++------- cf/test/test_regrid.py | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cf/test/test_RegridOperator.py b/cf/test/test_RegridOperator.py index 7367ab1e25..0f062300ce 100644 --- a/cf/test/test_RegridOperator.py +++ b/cf/test/test_RegridOperator.py @@ -19,11 +19,16 @@ class RegridOperatorTest(unittest.TestCase): def setUp(self): - src = cf.example_field(0) - dst = cf.example_field(1) - self.r = src.regrids(dst, "linear", return_operator=True) + # Skip all if espmy module not available! + if not esmpy_imported: + self.skipTest( + "Test module requires 'esmpy' package. Install it to run all." + ) + else: + src = cf.example_field(0) + dst = cf.example_field(1) + self.r = src.regrids(dst, "linear", return_operator=True) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_attributes(self): self.assertEqual(self.r.coord_sys, "spherical") self.assertEqual(self.r.method, "linear") @@ -51,11 +56,9 @@ def test_RegridOperator_attributes(self): self.assertIsNone(self.r.dst_z) self.assertFalse(self.r.ln_z) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_copy(self): self.assertIsInstance(self.r.copy(), self.r.__class__) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_equal_weights(self): r0 = self.r r1 = r0.copy() @@ -63,7 +66,6 @@ def test_RegridOperator_equal_weights(self): r1.weights.data += 0.1 self.assertFalse(r0.equal_weights(r1)) - @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") def test_RegridOperator_equal_dst_mask(self): r0 = self.r.copy() r1 = r0.copy() diff --git a/cf/test/test_regrid.py b/cf/test/test_regrid.py index b523f25191..236d852414 100644 --- a/cf/test/test_regrid.py +++ b/cf/test/test_regrid.py @@ -141,7 +141,7 @@ class RegridTest(unittest.TestCase): def setUp(self): """Preparations called immediately before each test method.""" - # Skip all if healpix module not available! + # Skip all if esmpy module not available! if not esmpy_imported: self.skipTest( "Test module requires 'esmpy' package. Install it to run all."