diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3499ccd..1ae3933 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,12 +10,17 @@ jobs: test: strategy: matrix: - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13", "3.14"] runs-on: ubuntu-latest container: osgeo/grass-gis:releasebranch_8_4-ubuntu steps: - - uses: actions/checkout@v4 + - name: Install dependencies + run: | + apt update + apt install -y ca-certificates + + - uses: actions/checkout@v5 - name: Install uv run: | @@ -28,4 +33,4 @@ jobs: - name: Run tests run: | - uv run pytest + uv run pytest --random-order diff --git a/src/xarray_grass/__init__.py b/src/xarray_grass/__init__.py index 2e80bc5..733b680 100644 --- a/src/xarray_grass/__init__.py +++ b/src/xarray_grass/__init__.py @@ -5,4 +5,4 @@ from xarray_grass.to_grass import to_grass as to_grass from xarray_grass.coord_utils import RegionData as RegionData -__version__ = "0.2.0" +__version__ = "0.4.0" diff --git a/src/xarray_grass/xarray_grass.py b/src/xarray_grass/xarray_grass.py index 62011a3..3615b6e 100644 --- a/src/xarray_grass/xarray_grass.py +++ b/src/xarray_grass/xarray_grass.py @@ -15,7 +15,7 @@ import os from pathlib import Path -from typing import Iterable +from typing import Iterable, Optional import numpy as np from xarray.backends import BackendEntrypoint @@ -44,10 +44,10 @@ def open_dataset( self, filename_or_obj, *, - raster: str | Iterable[str] = [], - raster_3d: str | Iterable[str] = [], - strds: str | Iterable[str] = [], - str3ds: str | Iterable[str] = [], + raster: Optional[str | Iterable[str]] = None, + raster_3d: Optional[str | Iterable[str]] = None, + strds: Optional[str | Iterable[str]] = None, + str3ds: Optional[str | Iterable[str]] = None, drop_variables: Iterable[str], ) -> xr.Dataset: """Open GRASS project or mapset as an xarray.Dataset. @@ -69,13 +69,19 @@ def open_dataset( for strds_name in grass_objects["strds"]: maps_in_strds = gi.list_maps_in_strds(strds_name) rasters_in_strds.extend([map_data.id for map_data in maps_in_strds]) - open_func_params["strds_list"].append(strds_name) + if open_func_params["strds_list"] is None: + open_func_params["strds_list"] = [strds_name] + else: + open_func_params["strds_list"].append(strds_name) raster3ds_in_str3ds = [] # str3ds for str3ds_name in grass_objects["str3ds"]: maps_in_str3ds = gi.list_maps_in_str3ds(str3ds_name) raster3ds_in_str3ds.extend([map_data.id for map_data in maps_in_str3ds]) - open_func_params["str3ds_list"].append(str3ds_name) + if open_func_params["str3ds_list"] is None: + open_func_params["str3ds_list"] = [str3ds_name] + else: + open_func_params["str3ds_list"].append(str3ds_name) # rasters not in strds open_func_params["raster_list"] = [ name for name in grass_objects["raster"] if name not in rasters_in_strds @@ -287,7 +293,6 @@ def open_grass_raster(raster_name: str, grass_i: GrassInterface) -> xr.DataArray ) # Add CF attributes r_infos = grass_i.get_raster_info(raster_name) - print(f"{r_infos=}") da_with_attrs = set_cf_coordinates(data_array, gi=grass_i, is_3d=False) da_with_attrs.attrs["long_name"] = r_infos.get("title", "") da_with_attrs.attrs["source"] = ",".join([r_infos["source1"], r_infos["source2"]]) @@ -330,7 +335,6 @@ def open_grass_raster_3d(raster_3d_name: str, grass_i: GrassInterface) -> xr.Dat def open_grass_strds(strds_name: str, grass_i: GrassInterface) -> xr.DataArray: """must be called from within a grass session - TODO: add unit, description etc. as attributes TODO: lazy loading """ x_coords, y_coords, _ = get_coordinates(grass_i, raster_3d=False).values() diff --git a/tests/test_grass_interface.py b/tests/test_grass_interface.py index 066665a..87fe537 100644 --- a/tests/test_grass_interface.py +++ b/tests/test_grass_interface.py @@ -36,8 +36,21 @@ def test_no_grass_session(): - with pytest.raises(RuntimeError): - GrassInterface() + """Test that GrassInterface raises RuntimeError when no GRASS session exists. + This test must clear GISRC to ensure isolation from other tests that may have + set up a GRASS session. + """ + import os + + # Save and clear GISRC to ensure no session exists + original_gisrc = os.environ.pop("GISRC", None) + try: + with pytest.raises(RuntimeError): + GrassInterface() + finally: + # Restore GISRC if it was set + if original_gisrc is not None: + os.environ["GISRC"] = original_gisrc @pytest.mark.usefixtures("grass_session_fixture")