Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand All @@ -28,4 +33,4 @@ jobs:

- name: Run tests
run: |
uv run pytest
uv run pytest --random-order
2 changes: 1 addition & 1 deletion src/xarray_grass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
22 changes: 13 additions & 9 deletions src/xarray_grass/xarray_grass.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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"]])
Expand Down Expand Up @@ -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()
Expand Down
17 changes: 15 additions & 2 deletions tests/test_grass_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down