From 73da6b664953246a3bd7b514218caca7c4cee46f Mon Sep 17 00:00:00 2001 From: clement laplace Date: Fri, 5 Jul 2024 10:53:52 +0000 Subject: [PATCH 1/9] test: parralelize the tests with the pytest-xdist library --- .github/workflows/ci.yaml | 2 +- continuous_integration/environment.yaml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ea685ddb7b..7393b69822 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -111,7 +111,7 @@ jobs: shell: bash -l {0} run: | export LD_PRELOAD=${{ env.LD_PRELOAD }}; - pytest --cov=satpy satpy/tests --cov-report=xml --cov-report= + pytest -n 6 --cov=satpy satpy/tests --cov-report=xml --cov-report= - name: Upload unittest coverage to Codecov uses: codecov/codecov-action@v4 diff --git a/continuous_integration/environment.yaml b/continuous_integration/environment.yaml index 6479ce6b53..3c88c030a1 100644 --- a/continuous_integration/environment.yaml +++ b/continuous_integration/environment.yaml @@ -57,9 +57,10 @@ dependencies: - pint-xarray - ephem - bokeh + - pytest-xdist - pip: - - pytest-lazy-fixtures - - trollsift - - trollimage>=1.23 - - pyspectral - - pyorbital + - pytest-lazy-fixtures + - trollsift + - trollimage>=1.23 + - pyspectral + - pyorbital From 186d3f815f64a500fe71fd15f67a4b2e7b6c32e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <49512274+ludwigVonKoopa@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:03:15 +0000 Subject: [PATCH 2/9] adding example in documentation --- AUTHORS.md | 1 + satpy/resample.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 733e9d572d..31eaafa223 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -94,3 +94,4 @@ The following people have made contributions to this project: - [Will Sharpe (wjsharpe)](https://github.com/wjsharpe) - [Sara Hörnquist (shornqui)](https://github.com/shornqui) - [Antonio Valentino](https://github.com/avalentino) +- [Clément (ludwigvonkoopa)](https://github.com/ludwigVonKoopa) diff --git a/satpy/resample.py b/satpy/resample.py index 50de0723f4..4d643228e5 100644 --- a/satpy/resample.py +++ b/satpy/resample.py @@ -109,6 +109,14 @@ >>> my_area = AreaDefinition(...) >>> local_scene = scn.resample(my_area) +Resize area definition in pixels +-------------------------------- + +Sometimes you may want to create a small image with fixed size in pixels. +For example, to create an image of (x, y) pixels : + + >>> small_scn = scn.resample(scn.finest_area().copy(height=y, width=x), ...) + Create dynamic area definition ------------------------------ From 1c9878ee86a3bf38d7d47139b2d33d0bb69e4758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <49512274+ludwigVonKoopa@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:26:11 +0000 Subject: [PATCH 3/9] adding mention of the resampler --- satpy/resample.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/satpy/resample.py b/satpy/resample.py index 4d643228e5..f069ae77c9 100644 --- a/satpy/resample.py +++ b/satpy/resample.py @@ -113,9 +113,16 @@ -------------------------------- Sometimes you may want to create a small image with fixed size in pixels. -For example, to create an image of (x, y) pixels : +For example, to create an image of (y, x) pixels : + + >>> small_scn = scn.resample(scn.finest_area().copy(height=y, width=x), resampler="nearest") + + +.. warning:: + + Resizing with the `nearest` resampler will accept all sizes. + Be aware that resizing with natine resampling only works if size is a factor of the original size. - >>> small_scn = scn.resample(scn.finest_area().copy(height=y, width=x), ...) Create dynamic area definition ------------------------------ From 40b8ebe283a06d72d85df70192adca58cd6179d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <49512274+ludwigVonKoopa@users.noreply.github.com> Date: Mon, 15 Jul 2024 07:06:28 +0000 Subject: [PATCH 4/9] delete one line from documentation --- satpy/resample.py | 1 - 1 file changed, 1 deletion(-) diff --git a/satpy/resample.py b/satpy/resample.py index f069ae77c9..168ea6e7ed 100644 --- a/satpy/resample.py +++ b/satpy/resample.py @@ -120,7 +120,6 @@ .. warning:: - Resizing with the `nearest` resampler will accept all sizes. Be aware that resizing with natine resampling only works if size is a factor of the original size. From f73d4089c60a4979673bf698b1130b390793e794 Mon Sep 17 00:00:00 2001 From: David Hoese Date: Mon, 15 Jul 2024 12:56:02 -0500 Subject: [PATCH 5/9] Clarify native resampling in satpy/resample.py docstring --- satpy/resample.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/satpy/resample.py b/satpy/resample.py index 168ea6e7ed..5737294838 100644 --- a/satpy/resample.py +++ b/satpy/resample.py @@ -120,7 +120,10 @@ .. warning:: - Be aware that resizing with natine resampling only works if size is a factor of the original size. + Be aware that resizing with native resampling (``resampler="native"``) only + works if the new size is an integer factor of the original input size. For example, + multiplying the size by 2 or dividing the size by 2. Multiplying by 1.5 would + not be allowed. Create dynamic area definition From feb9c749270a465351d02460ac41551eb1d5ade0 Mon Sep 17 00:00:00 2001 From: David Hoese Date: Tue, 16 Jul 2024 12:47:13 -0500 Subject: [PATCH 6/9] Refactor enhancer tests to be more parallel friendly --- satpy/tests/test_utils.py | 3 +- satpy/tests/test_writers.py | 151 +++++++++++++++++++----------------- 2 files changed, 82 insertions(+), 72 deletions(-) diff --git a/satpy/tests/test_utils.py b/satpy/tests/test_utils.py index a334d3133a..cf6ecc041a 100644 --- a/satpy/tests/test_utils.py +++ b/satpy/tests/test_utils.py @@ -19,7 +19,6 @@ import datetime import logging import typing -import unittest import warnings from math import sqrt from unittest import mock @@ -266,7 +265,7 @@ def test_make_fake_scene(): assert sc["nine"].attrs.keys() >= {"please", "answer", "bad words", "area"} -class TestCheckSatpy(unittest.TestCase): +class TestCheckSatpy: """Test the 'check_satpy' function.""" def test_basic_check_satpy(self): diff --git a/satpy/tests/test_writers.py b/satpy/tests/test_writers.py index 701347cdbe..be9dc87ff1 100644 --- a/satpy/tests/test_writers.py +++ b/satpy/tests/test_writers.py @@ -18,11 +18,14 @@ from __future__ import annotations +import contextlib import datetime as dt import os +import pathlib import shutil import unittest import warnings +from typing import Iterator from unittest import mock import dask.array as da @@ -31,6 +34,8 @@ import xarray as xr from trollimage.colormap import greys +from satpy.writers import ImageWriter + class TestWritersModule(unittest.TestCase): """Test the writers module.""" @@ -118,41 +123,47 @@ def test_init_nonexistent_enh_file(self): Enhancer(enhancement_config_file="is_not_a_valid_filename_?.yaml") +class _CustomImageWriter(ImageWriter): + def __init__(self, **kwargs): + super().__init__(name="test", config_files=[], **kwargs) + self.img = None + + def save_image(self, img, **kwargs): + self.img = img + + +@contextlib.contextmanager +def _create_test_configs(base_dir: pathlib.Path, test_configs: dict[str, str]) -> Iterator[None]: + for fn, content in test_configs.items(): + config_rel_dir = os.path.dirname(fn) + if config_rel_dir: + os.makedirs(base_dir / config_rel_dir, exist_ok=True) + with open(base_dir / fn, "w") as f: + f.write(content) + yield + # teardown: Is this needed if we use a tmp_path? + for fn, _content in test_configs.items(): + config_base_dir = base_dir / os.path.dirname(fn) + if config_base_dir != base_dir and os.path.isdir(config_base_dir): + shutil.rmtree(config_base_dir) + elif os.path.isfile(base_dir / fn): + os.remove(base_dir / fn) + + class _BaseCustomEnhancementConfigTests: TEST_CONFIGS: dict[str, str] = {} - @classmethod - def setup_class(cls): - """Create fake user configurations.""" - for fn, content in cls.TEST_CONFIGS.items(): - base_dir = os.path.dirname(fn) - if base_dir: - os.makedirs(base_dir, exist_ok=True) - with open(fn, "w") as f: - f.write(content) - - # create fake test image writer - from satpy.writers import ImageWriter - - class CustomImageWriter(ImageWriter): - def __init__(self, **kwargs): - super(CustomImageWriter, self).__init__(name="test", config_files=[], **kwargs) - self.img = None - - def save_image(self, img, **kwargs): - self.img = img - cls.CustomImageWriter = CustomImageWriter - - @classmethod - def teardown_class(cls): - """Remove fake user configurations.""" - for fn, _content in cls.TEST_CONFIGS.items(): - base_dir = os.path.dirname(fn) - if base_dir not in [".", ""] and os.path.isdir(base_dir): - shutil.rmtree(base_dir) - elif os.path.isfile(fn): - os.remove(fn) + @pytest.fixture(scope="class", autouse=True) + def test_configs_path(self, tmp_path_factory): + tmp_path = tmp_path_factory.mktemp("config") + with _create_test_configs(tmp_path, self.TEST_CONFIGS): + prev_cwd = pathlib.Path.cwd() + os.chdir(tmp_path) + try: + yield tmp_path + finally: + os.chdir(prev_cwd) class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): @@ -197,7 +208,7 @@ class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): """, } - def test_multisensor_choice(self): + def test_multisensor_choice(self, test_configs_path): """Test that a DataArray with two sensors works.""" from xarray import DataArray @@ -213,14 +224,14 @@ def test_multisensor_choice(self): assert e.enhancement_tree is not None img = get_enhanced_image(ds, enhance=e) # make sure that both sensor configs were loaded - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN), - os.path.abspath(self.ENH_FN2)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN, + test_configs_path / self.ENH_FN2}) # test_sensor1 config should have been used because it is # alphabetically first np.testing.assert_allclose(img.data.values[0], ds.data / 200.0) - def test_multisensor_exact(self): + def test_multisensor_exact(self, test_configs_path): """Test that a DataArray with two sensors can match exactly.""" from xarray import DataArray @@ -236,9 +247,9 @@ def test_multisensor_exact(self): assert e.enhancement_tree is not None img = get_enhanced_image(ds, enhance=e) # make sure that both sensor configs were loaded - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN), - os.path.abspath(self.ENH_FN2)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN, + test_configs_path / self.ENH_FN2}) # test_sensor1 config should have been used because it is # alphabetically first np.testing.assert_allclose(img.data.values[0], ds.data / 20.0) @@ -298,7 +309,7 @@ class TestEnhancerUserConfigs(_BaseCustomEnhancementConfigTests): ENH_FN3: """""", } - def test_enhance_empty_config(self): + def test_enhance_empty_config(self, test_configs_path): """Test Enhancer doesn't fail with empty enhancement file.""" from xarray import DataArray @@ -309,10 +320,10 @@ def test_enhance_empty_config(self): e = Enhancer() assert e.enhancement_tree is not None get_enhanced_image(ds, enhance=e) - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN3)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN3}) - def test_enhance_with_sensor_no_entry(self): + def test_enhance_with_sensor_no_entry(self, test_configs_path): """Test enhancing an image that has no configuration sections.""" from xarray import DataArray @@ -323,9 +334,9 @@ def test_enhance_with_sensor_no_entry(self): e = Enhancer() assert e.enhancement_tree is not None get_enhanced_image(ds, enhance=e) - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN2), - os.path.abspath(self.ENH_ENH_FN2)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN2, + test_configs_path / self.ENH_ENH_FN2}) def test_no_enhance(self): """Test turning off enhancements.""" @@ -344,7 +355,7 @@ def test_writer_no_enhance(self): ds = DataArray(np.arange(1, 11.).reshape((2, 5)), attrs=dict(name="test1", sensor="test_sensor", mode="L"), dims=["y", "x"]) - writer = self.CustomImageWriter(enhance=False) + writer = _CustomImageWriter(enhance=False) writer.save_datasets((ds,), compute=False) img = writer.img np.testing.assert_allclose(img.data.data.compute().squeeze(), ds.data) @@ -358,12 +369,12 @@ def test_writer_custom_enhance(self): attrs=dict(name="test1", sensor="test_sensor", mode="L"), dims=["y", "x"]) enhance = Enhancer() - writer = self.CustomImageWriter(enhance=enhance) + writer = _CustomImageWriter(enhance=enhance) writer.save_datasets((ds,), compute=False) img = writer.img np.testing.assert_almost_equal(img.data.isel(bands=0).max().values, 1.) - def test_enhance_with_sensor_entry(self): + def test_enhance_with_sensor_entry(self, test_configs_path): """Test enhancing an image with a configuration section.""" from xarray import DataArray @@ -374,9 +385,9 @@ def test_enhance_with_sensor_entry(self): e = Enhancer() assert e.enhancement_tree is not None img = get_enhanced_image(ds, enhance=e) - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN), - os.path.abspath(self.ENH_ENH_FN)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN, + test_configs_path / self.ENH_ENH_FN}) np.testing.assert_almost_equal(img.data.isel(bands=0).max().values, 1.) @@ -386,12 +397,12 @@ def test_enhance_with_sensor_entry(self): e = Enhancer() assert e.enhancement_tree is not None img = get_enhanced_image(ds, enhance=e) - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN), - os.path.abspath(self.ENH_ENH_FN)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN, + test_configs_path / self.ENH_ENH_FN}) np.testing.assert_almost_equal(img.data.isel(bands=0).max().values, 1.) - def test_enhance_with_sensor_entry2(self): + def test_enhance_with_sensor_entry2(self, test_configs_path): """Test enhancing an image with a more detailed configuration section.""" from xarray import DataArray @@ -403,9 +414,9 @@ def test_enhance_with_sensor_entry2(self): e = Enhancer() assert e.enhancement_tree is not None img = get_enhanced_image(ds, enhance=e) - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN), - os.path.abspath(self.ENH_ENH_FN)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN, + test_configs_path / self.ENH_ENH_FN}) np.testing.assert_almost_equal(img.data.isel(bands=0).max().values, 0.5) @@ -460,45 +471,45 @@ def _get_test_data_array(self): dims=["y", "x"]) return ds - def _get_enhanced_image(self, data_arr): + def _get_enhanced_image(self, data_arr, test_configs_path): from satpy.writers import Enhancer, get_enhanced_image e = Enhancer() assert e.enhancement_tree is not None img = get_enhanced_image(data_arr, enhance=e) # make sure that both configs were loaded - assert (set(e.sensor_enhancement_configs) == - {os.path.abspath(self.ENH_FN)}) + assert (set(pathlib.Path(config) for config in e.sensor_enhancement_configs) == + {test_configs_path / self.ENH_FN}) return img - def test_no_reader(self): + def test_no_reader(self, test_configs_path): """Test that a DataArray with no 'reader' metadata works.""" data_arr = self._get_test_data_array() - img = self._get_enhanced_image(data_arr) + img = self._get_enhanced_image(data_arr, test_configs_path) # no reader available, should use default no specified reader np.testing.assert_allclose(img.data.values[0], data_arr.data / 100.0) - def test_no_matching_reader(self): + def test_no_matching_reader(self, test_configs_path): """Test that a DataArray with no matching 'reader' works.""" data_arr = self._get_test_data_array() data_arr.attrs["reader"] = "reader3" - img = self._get_enhanced_image(data_arr) + img = self._get_enhanced_image(data_arr, test_configs_path) # no reader available, should use default no specified reader np.testing.assert_allclose(img.data.values[0], data_arr.data / 100.0) - def test_only_reader_matches(self): + def test_only_reader_matches(self, test_configs_path): """Test that a DataArray with only a matching 'reader' works.""" data_arr = self._get_test_data_array() data_arr.attrs["reader"] = "reader2" data_arr.attrs["name"] = "not_configured" - img = self._get_enhanced_image(data_arr) + img = self._get_enhanced_image(data_arr, test_configs_path) # no reader available, should use default no specified reader np.testing.assert_allclose(img.data.values[0], data_arr.data / 75.0) - def test_reader_and_name_match(self): + def test_reader_and_name_match(self, test_configs_path): """Test that a DataArray with a matching 'reader' and 'name' works.""" data_arr = self._get_test_data_array() data_arr.attrs["reader"] = "reader2" - img = self._get_enhanced_image(data_arr) + img = self._get_enhanced_image(data_arr, test_configs_path) # no reader available, should use default no specified reader np.testing.assert_allclose(img.data.values[0], data_arr.data / 50.0) From f52f4f9e4ba8d97365e14a88ca2757db9c51c18c Mon Sep 17 00:00:00 2001 From: David Hoese Date: Tue, 16 Jul 2024 12:52:57 -0500 Subject: [PATCH 7/9] Convert unittest writer tests to pytest --- satpy/tests/test_writers.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/satpy/tests/test_writers.py b/satpy/tests/test_writers.py index be9dc87ff1..04952a29f9 100644 --- a/satpy/tests/test_writers.py +++ b/satpy/tests/test_writers.py @@ -23,7 +23,6 @@ import os import pathlib import shutil -import unittest import warnings from typing import Iterator from unittest import mock @@ -37,12 +36,11 @@ from satpy.writers import ImageWriter -class TestWritersModule(unittest.TestCase): +class TestWritersModule: """Test the writers module.""" def test_to_image_1d(self): """Conversion to image.""" - # 1D from satpy.writers import to_image p = xr.DataArray(np.arange(25), dims=["y"]) with pytest.raises(ValueError, match="Need at least a 2D array to make an image."): @@ -53,7 +51,6 @@ def test_to_image_2d(self, mock_geoimage): """Conversion to image.""" from satpy.writers import to_image - # 2D data = np.arange(25).reshape((5, 5)) p = xr.DataArray(data, attrs=dict(mode="L", fill_value=0, palette=[0, 1, 2, 3, 4, 5]), @@ -67,8 +64,8 @@ def test_to_image_2d(self, mock_geoimage): @mock.patch("satpy.writers.XRImage") def test_to_image_3d(self, mock_geoimage): """Conversion to image.""" - # 3D from satpy.writers import to_image + data = np.arange(75).reshape((3, 5, 5)) p = xr.DataArray(data, dims=["bands", "y", "x"]) p["bands"] = ["R", "G", "B"] @@ -88,7 +85,7 @@ def test_show(self, mock_get_image): assert mock_get_image.return_value.show.called -class TestEnhancer(unittest.TestCase): +class TestEnhancer: """Test basic `Enhancer` functionality with builtin configs.""" def test_basic_init_no_args(self): @@ -514,7 +511,7 @@ def test_reader_and_name_match(self, test_configs_path): np.testing.assert_allclose(img.data.values[0], data_arr.data / 50.0) -class TestYAMLFiles(unittest.TestCase): +class TestYAMLFiles: """Test and analyze the writer configuration files.""" def test_filename_matches_writer_name(self): @@ -551,10 +548,10 @@ def test_available_writers(self): assert "name" in writer_info -class TestComputeWriterResults(unittest.TestCase): +class TestComputeWriterResults: """Test compute_writer_results().""" - def setUp(self): + def setup_method(self): """Create temporary directory to save files to and a mock scene.""" import tempfile @@ -579,7 +576,7 @@ def setUp(self): # Temp dir self.base_dir = tempfile.mkdtemp() - def tearDown(self): + def teardown_method(self): """Remove the temporary directory created for a test.""" try: shutil.rmtree(self.base_dir, ignore_errors=True) @@ -737,10 +734,10 @@ def test_save_dataset_dynamic_filename_with_dir(self): assert os.path.isfile(os.path.join(self.base_dir, exp_fn)) -class TestOverlays(unittest.TestCase): +class TestOverlays: """Tests for add_overlay and add_decorate functions.""" - def setUp(self): + def setup_method(self): """Create test data and mock pycoast/pydecorate.""" from pyresample.geometry import AreaDefinition from trollimage.xrimage import XRImage @@ -794,7 +791,7 @@ def setUp(self): self.module_patcher = mock.patch.dict("sys.modules", modules) self.module_patcher.start() - def tearDown(self): + def teardown_method(self): """Turn off pycoast/pydecorate mocking.""" self.module_patcher.stop() From c022bdb38d822da5aacc2c164345c2e5d81a8276 Mon Sep 17 00:00:00 2001 From: David Hoese Date: Tue, 16 Jul 2024 13:06:34 -0500 Subject: [PATCH 8/9] Cleanup enhancer tests --- satpy/tests/test_writers.py | 46 +++++++++++++++---------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/satpy/tests/test_writers.py b/satpy/tests/test_writers.py index 04952a29f9..174913c6e7 100644 --- a/satpy/tests/test_writers.py +++ b/satpy/tests/test_writers.py @@ -18,13 +18,11 @@ from __future__ import annotations -import contextlib import datetime as dt import os import pathlib import shutil import warnings -from typing import Iterator from unittest import mock import dask.array as da @@ -129,38 +127,32 @@ def save_image(self, img, **kwargs): self.img = img -@contextlib.contextmanager -def _create_test_configs(base_dir: pathlib.Path, test_configs: dict[str, str]) -> Iterator[None]: - for fn, content in test_configs.items(): - config_rel_dir = os.path.dirname(fn) - if config_rel_dir: - os.makedirs(base_dir / config_rel_dir, exist_ok=True) - with open(base_dir / fn, "w") as f: - f.write(content) - yield - # teardown: Is this needed if we use a tmp_path? - for fn, _content in test_configs.items(): - config_base_dir = base_dir / os.path.dirname(fn) - if config_base_dir != base_dir and os.path.isdir(config_base_dir): - shutil.rmtree(config_base_dir) - elif os.path.isfile(base_dir / fn): - os.remove(base_dir / fn) - - class _BaseCustomEnhancementConfigTests: TEST_CONFIGS: dict[str, str] = {} @pytest.fixture(scope="class", autouse=True) def test_configs_path(self, tmp_path_factory): + """Create test enhancement configuration files in a temporary directory. + + The root temporary directory is changed to and returned. + + """ + prev_cwd = pathlib.Path.cwd() tmp_path = tmp_path_factory.mktemp("config") - with _create_test_configs(tmp_path, self.TEST_CONFIGS): - prev_cwd = pathlib.Path.cwd() - os.chdir(tmp_path) - try: - yield tmp_path - finally: - os.chdir(prev_cwd) + os.chdir(tmp_path) + + for fn, content in self.TEST_CONFIGS.items(): + config_rel_dir = os.path.dirname(fn) + if config_rel_dir: + os.makedirs(config_rel_dir, exist_ok=True) + with open(fn, "w") as f: + f.write(content) + + try: + yield tmp_path + finally: + os.chdir(prev_cwd) class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): From 53b10be9f8173ad4af230b1cb01764429a69dfd2 Mon Sep 17 00:00:00 2001 From: David Hoese Date: Wed, 17 Jul 2024 10:06:24 -0500 Subject: [PATCH 9/9] Use "auto" number of CPUs in CI --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7393b69822..9d9e552da5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -111,7 +111,7 @@ jobs: shell: bash -l {0} run: | export LD_PRELOAD=${{ env.LD_PRELOAD }}; - pytest -n 6 --cov=satpy satpy/tests --cov-report=xml --cov-report= + pytest -n auto --cov=satpy satpy/tests --cov-report=xml --cov-report= - name: Upload unittest coverage to Codecov uses: codecov/codecov-action@v4