nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: rust-lang-nursery/futures-rs
Pull request #770 (2018-02-15 13:01:20)

add is_done to future::Fuse
parallel to the `Stream::is_done` method.


Patch:
diff --git a/src/future/fuse.rs b/src/future/fuse.rs
index e37eace0..05ad3d5a 100644
--- a/src/future/fuse.rs
+++ b/src/future/fuse.rs
@@ -19,6 +19,18 @@ pub fn new<A: Future>(f: A) -> Fuse<A> {
     }
 }
 
+impl<A: Future> Fuse<A> {
+    /// Returns whether the underlying future has finished or not.
+    /// 
+    /// If this method returns `true`, then all future calls to `poll`
+    /// are guaranteed to return `Ok(Async::NotReady)`. If this returns
+    /// false, then the underlying future has not been driven to
+    /// completion.
+    pub fn is_done(&self) -> bool {
+        self.future.is_none()
+    }
+}
+
 impl<A: Future> Future for Fuse<A> {
     type Item = A::Item;
     type Error = A::Error;


Test patch:
diff --git a/tests/fuse.rs b/tests/fuse.rs
index a1e6cee2..177d914e 100644
--- a/tests/fuse.rs
+++ b/tests/fuse.rs
@@ -13,3 +13,27 @@ fn fuse() {
     assert!(future.poll_future_notify(&notify_panic(), 0).unwrap().is_ready());
     assert!(future.poll_future_notify(&notify_panic(), 0).unwrap().is_not_ready());
 }
+
+#[test]
+fn fuse_is_done() {
+    use futures::future::{Fuse, FutureResult};
+
+    struct Wrapped(Fuse<FutureResult<i32, u32>>);
+
+    impl Future for Wrapped {
+        type Item = ();
+        type Error = ();
+
+        fn poll(&mut self) -> Poll<(), ()> {
+            assert!(!self.0.is_done());
+            assert_eq!(self.0.poll().unwrap(), Async::Ready(2));
+            assert!(self.0.is_done());
+            assert_eq!(self.0.poll().unwrap(), Async::NotReady);
+            assert!(self.0.is_done());
+            
+            Ok(Async::Ready(()))            
+        }
+    }
+
+    assert!(Wrapped(ok::<i32, u32>(2).fuse()).wait().is_ok());
+}
\ No newline at end of file
Repository: ecmwf/earthkit-data
Pull request #232 (2023-10-16 18:11:05)

Return index distance tuple in nearest_point_haversine
 `geo.nearest_point_haversine()` now returns a tuple of `(index, distance)`.

```python
>>> from earthkit.data.geo import nearest_point_haversine
>>> p_ref = (51.45, -0.97)
>>> p_lat = [44.49, 50.73, 50.1]
>>> p_lon = [11.34, 7.90, -8.1]
>>> nearest_point_haversine(p_ref, (p_lat, p_lon))
    (2, 523115.8314777661)
```


Patch:
diff --git a/earthkit/data/geo/distance.py b/earthkit/data/geo/distance.py
index 6f4564c..8fdc631 100644
--- a/earthkit/data/geo/distance.py
+++ b/earthkit/data/geo/distance.py
@@ -103,7 +103,9 @@ def nearest_point_haversine(ref_point, points):
     Returns
     -------
     number
-        Index of the nearest point from ``points`` to ``ref_point``
+        Index of the nearest point to ``ref_point` in ``points``.
+    number
+        Distance (m) between ``ref_point`` and the nearest point in ``points``.
 
     Examples
     --------
@@ -112,7 +114,7 @@ def nearest_point_haversine(ref_point, points):
     >>> p_lat = [44.49, 50.73, 50.1]
     >>> p_lon = [11.34, 7.90, -8.1]
     >>> nearest_point_haversine(p_ref, (p_lat, p_lon))
-    2
+    (2, 523115.8314777661)
 
     """
     if np.asarray(ref_point[0]).size != 1 or np.asarray(ref_point[1]).size != 1:
@@ -122,4 +124,4 @@ def nearest_point_haversine(ref_point, points):
     index = np.nanargmin(distance)
     if isinstance(index, np.ndarray):
         return index[0]
-    return index
+    return (index, distance[index])


Test patch:
diff --git a/tests/geo/test_geo.py b/tests/geo/test_geo.py
index 99a42e5..5db0cad 100644
--- a/tests/geo/test_geo.py
+++ b/tests/geo/test_geo.py
@@ -100,27 +100,28 @@ def test_haversine_distance_invalid(p_ref):
 
 
 @pytest.mark.parametrize(
-    "p_ref,index_ref",
+    "p_ref,index_ref,dist_ref",
     [
-        ((0, 0), 0),
-        ((15, 22), 0),
-        ((44, 10), 6),
-        ((44, -10), 7),
-        ((89, -120), 4),
-        ((-50, -18), 8),
-        ((-50, 18), 9),
+        ((0, 0), 0, 0),
+        ((15, 22), 0, 2937383.915942687),
+        ((44, 10), 6, 890348.1323086087),
+        ((44, -10), 7, 890348.1323086087),
+        ((89, -120), 4, 111198.92344854656),
+        ((-50, -18), 8, 265965.0757389435),
+        ((-50, 18), 9, 265965.0757389435),
     ],
 )
-def test_haversine_nearest(p_ref, index_ref):
+def test_haversine_nearest(p_ref, index_ref, dist_ref):
     lats = np.array([0.0, 0, 0, 0, 90, -90, 48, 48, -48, -48, np.nan])
     lons = np.array([0, 90, -90, 180, 0, 0, 20, -20, -20, 20, 1.0])
 
-    index = nearest_point_haversine(p_ref, (lats, lons))
+    index, distance = nearest_point_haversine(p_ref, (lats, lons))
     assert index == index_ref, p_ref
+    assert np.isclose(distance, dist_ref)
 
 
 def test_haversine_nearest_invalid():
-    # p_ref must be a single point
+    # checks: p_ref must be a single point
     p_ref = ([15, 21], [22, 7])
     p = ([0.0, 0], [0, 90])
Repository: ecmwf/earthkit-data
Pull request #284 (2024-01-22 13:31:50)

Add bits_per_value option when saving numpyfs grib data
 With this PR we can specify the bits per value for the grib encoding when saving a Numpy Fieldlists into a GRIB file:

```python
ds.save("my_data.grib", bits_per_value=24)
```
By default `bits_per_value=16`.


Patch:
diff --git a/earthkit/data/writers/grib.py b/earthkit/data/writers/grib.py
index 4c064af..f9ac7d3 100644
--- a/earthkit/data/writers/grib.py
+++ b/earthkit/data/writers/grib.py
@@ -13,7 +13,7 @@ from . import Writer
 class GribWriter(Writer):
     DATA_FORMAT = "grib"
 
-    def write(self, f, values, metadata, check_nans=True):
+    def write(self, f, values, metadata, check_nans=True, bits_per_value=16):
         r"""Write a GRIB field to a file object.
 
         Parameters
@@ -28,6 +28,9 @@ class GribWriter(Writer):
             Replace nans in ``values`` with GRIB missing values when writing to``f``.
         """
         handle = metadata._handle.clone()
+
+        handle.set_long("bitsPerValue", bits_per_value)
+
         if check_nans:
             import numpy as np
 


Test patch:
diff --git a/tests/numpy_fs/test_numpy_fs_write.py b/tests/numpy_fs/test_numpy_fs_write.py
index 59f6191..5b73a1d 100644
--- a/tests/numpy_fs/test_numpy_fs_write.py
+++ b/tests/numpy_fs/test_numpy_fs_write.py
@@ -11,6 +11,7 @@
 
 import logging
 import os
+import sys
 
 import numpy as np
 import pytest
@@ -20,6 +21,10 @@ from earthkit.data.core.fieldlist import FieldList
 from earthkit.data.core.temporary import temp_file
 from earthkit.data.testing import earthkit_examples_file
 
+here = os.path.dirname(__file__)
+sys.path.insert(0, here)
+from numpy_fs_fixtures import load_numpy_fs  # noqa: E402
+
 LOG = logging.getLogger(__name__)
 
 
@@ -144,6 +149,18 @@ def test_numpy_fs_grib_write_generating_proc_id():
         assert np.allclose(r_tmp.values[1], v2)
 
 
+@pytest.mark.parametrize(
+    "_kwargs,expected_value", [({}, 16), ({"bits_per_value": 12}, 12)]
+)
+def test_numpy_fs_grib_write_bits_per_value(_kwargs, expected_value):
+    ds, _ = load_numpy_fs(1)
+
+    with temp_file() as tmp:
+        ds.save(tmp, **_kwargs)
+        ds1 = from_source("file", tmp)
+        assert ds1.metadata("bitsPerValue") == [expected_value] * len(ds)
+
+
 if __name__ == "__main__":
     from earthkit.data.testing import main
Repository: ecmwf/earthkit-data
Pull request #305 (2024-02-19 17:54:47)

Fix datelists containing 5 items in retrievals
The following mars retrieval fails:

```python
ds = earthkit.data.from_source("mars",
                                   {
                                   "class":"ea",
                                   "stream":"oper",
                                   "param":'2t',
                                   "expver": 1,
                                   "type":"an",
                                   "levtype":"sfc", 
                                   "area":[-28.0,26.0,-31.0,30.0], 
                                   "grid":[0.25,0.25],    
                                   "date": "2016-07-23/to/2016-07-27",
                                   "time":[str(i) for i in list(range(0,24))],
                                   }
                                  )
 

AttributeError: 'datetime.datetime' object has no attribute 'lower'
```

This only happens when we have exactly 5 dates! E.g. 
``` 
"date": "2016-07-23/to/2016-07-28"
```
 works fine.


Patch:
diff --git a/earthkit/data/utils/dates.py b/earthkit/data/utils/dates.py
index 7bd7898..9ecf0b7 100644
--- a/earthkit/data/utils/dates.py
+++ b/earthkit/data/utils/dates.py
@@ -88,6 +88,8 @@ def to_datetime_list(datetimes):  # noqa C901
 
         if (
             len(datetimes) == 5
+            and isinstance(datetimes[1], str)
+            and isinstance(datetimes[3], str)
             and datetimes[1].lower() == "to"
             and datetimes[3].lower() == "by"
         ):


Test patch:
diff --git a/tests/normalize/test_normalize_date.py b/tests/normalize/test_normalize_date.py
index 198202a..4360f50 100644
--- a/tests/normalize/test_normalize_date.py
+++ b/tests/normalize/test_normalize_date.py
@@ -122,6 +122,42 @@ def test_normalize_dates_multiple():
                 "2020.05.13",
             ],
         ),
+        (
+            "20200511/to/20200513",
+            [
+                "2020.05.11",
+                "2020.05.12",
+                "2020.05.13",
+            ],
+        ),
+        (
+            ["20200511", "20200512", "20200513"],
+            [
+                "2020.05.11",
+                "2020.05.12",
+                "2020.05.13",
+            ],
+        ),
+        (
+            "20200511/to/20200515",
+            [
+                "2020.05.11",
+                "2020.05.12",
+                "2020.05.13",
+                "2020.05.14",
+                "2020.05.15",
+            ],
+        ),
+        (
+            ["20200511", "20200512", "20200513", "20200514", "20200515"],
+            [
+                "2020.05.11",
+                "2020.05.12",
+                "2020.05.13",
+                "2020.05.14",
+                "2020.05.15",
+            ],
+        ),
     ],
 )
 def test_normalize_date_list(date, expected):
Repository: ecmwf/earthkit-data
Pull request #319 (2024-02-23 14:49:12)

from_source should fail when try to load empty file
With this PR `from_source` throws an exception when the input is an empty file. Previously, the result was a `TextReader`.

The consequence of this change is that when reading/downloading multiple files now it is enough to have an empty file out of many valid ones and the program fail if the exception is uncaught. 


Patch:
diff --git a/earthkit/data/readers/__init__.py b/earthkit/data/readers/__init__.py
index 5871b99..51a19b2 100644
--- a/earthkit/data/readers/__init__.py
+++ b/earthkit/data/readers/__init__.py
@@ -169,6 +169,12 @@ def reader(source, path, **kwargs):
         return DirectoryReader(source, path).mutate()
     LOG.debug("Reader for %s", path)
 
+    if not os.path.exists(path):
+        raise FileExistsError(f"No such file exists: '{path}'")
+
+    if os.path.getsize(path) == 0:
+        raise Exception(f"File is empty: '{path}'")
+
     n_bytes = SETTINGS.get("reader-type-check-bytes")
     with open(path, "rb") as f:
         magic = f.read(n_bytes)


Test patch:
diff --git a/tests/data/empty_file.grib b/tests/data/empty_file.grib
new file mode 100644
index 0000000..e69de29
diff --git a/tests/readers/test_empty_file.py b/tests/readers/test_empty_file.py
new file mode 100644
index 0000000..4895cd1
--- /dev/null
+++ b/tests/readers/test_empty_file.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+# (C) Copyright 2020 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+#
+
+import pytest
+
+import earthkit.data
+from earthkit.data.testing import earthkit_test_data_file
+
+
+def test_empty_file_reader():
+    with pytest.raises(Exception):
+        earthkit.data.from_source("file", earthkit_test_data_file("empty_file.grib"))
+
+
+def test_nonexisting_file_reader():
+    with pytest.raises(FileExistsError):
+        earthkit.data.from_source("file", "__nonexistingfile__")
+
+
+if __name__ == "__main__":
+    from earthkit.data.testing import main
+
+    main(__file__)
Repository: ecmwf/earthkit-data
Pull request #342 (2024-03-12 13:56:05)

Feature/merge from main


Patch:
diff --git a/docs/release_notes/version_0.5_updates.rst b/docs/release_notes/version_0.5_updates.rst
index 0ff49fe..4b7b249 100644
--- a/docs/release_notes/version_0.5_updates.rst
+++ b/docs/release_notes/version_0.5_updates.rst
@@ -1,6 +1,13 @@
 Version 0.5 Updates
 /////////////////////////
 
+Version 0.5.6
+===============
+
+Fixes
+++++++
+- fixed issue when parsing a NetCDF input for fields caused a crash
+
 
 Version 0.5.5
 ===============
diff --git a/earthkit/data/readers/netcdf.py b/earthkit/data/readers/netcdf.py
index 5d94c62..c38ff98 100644
--- a/earthkit/data/readers/netcdf.py
+++ b/earthkit/data/readers/netcdf.py
@@ -163,11 +163,16 @@ def get_fields_from_ds(
 
     skip = set()
 
+    def _skip_attr(v, attr_name):
+        attr_val = getattr(v, attr_name, "")
+        if isinstance(attr_val, str):
+            skip.update(attr_val.split(" "))
+
     for name in ds.data_vars:
         v = ds[name]
-        skip.update(getattr(v, "coordinates", "").split(" "))
-        skip.update(getattr(v, "bounds", "").split(" "))
-        skip.update(getattr(v, "grid_mapping", "").split(" "))
+        _skip_attr(v, "coordinates")
+        _skip_attr(v, "bounds")
+        _skip_attr(v, "grid_mapping")
 
     for name in ds.data_vars:
         if name in skip:


Test patch:
diff --git a/tests/netcdf/test_netcdf_fieldlist.py b/tests/netcdf/test_netcdf_fieldlist.py
index fe15119..f26e1a0 100644
--- a/tests/netcdf/test_netcdf_fieldlist.py
+++ b/tests/netcdf/test_netcdf_fieldlist.py
@@ -38,6 +38,25 @@ def test_netcdf_fieldlist_string_coord():
     assert np.allclose(ds[0].to_points()["y"], y_ref)
 
 
+def test_netcdf_fieldlist_bounds():
+    """Check if having non string values in bounds does not cause a crash"""
+    import xarray as xr
+
+    a = xr.DataArray(
+        [
+            [[11, 12, 13], [21, 22, 23], [31, 32, 33]],
+            [[14, 15, 16], [24, 25, 26], [34, 35, 36]],
+        ],
+        coords={"level": [0, 0], "x": [1, 2, 3], "y": [4, 5, 6]},
+        attrs={"bounds": [1, 2, 3]},
+        name="dummyvar",
+    )
+
+    ds = earthkit.data.from_object(a)
+    assert ds
+    assert len(ds) == 2
+
+
 if __name__ == "__main__":
     from earthkit.data.testing import main
Repository: ecmwf/earthkit-data
Pull request #614 (2025-02-12 11:26:02)

Feature/merge from main into develop


Patch:
diff --git a/docs/release_notes/version_0.12_updates.rst b/docs/release_notes/version_0.12_updates.rst
index 0b5bdf4..b7227ab 100644
--- a/docs/release_notes/version_0.12_updates.rst
+++ b/docs/release_notes/version_0.12_updates.rst
@@ -1,6 +1,16 @@
 Version 0.12 Updates
 /////////////////////////
 
+
+Version 0.12.2
+===============
+
+Fixes
+++++++++
+
+- Fixed issue when failed to build a fieldlist with concatenation because maximum recursion depth exceeded (:pr:`599`)
+
+
 Version 0.12.1
 ===============
 
diff --git a/src/earthkit/data/core/index.py b/src/earthkit/data/core/index.py
index 1d06524..c13cf50 100644
--- a/src/earthkit/data/core/index.py
+++ b/src/earthkit/data/core/index.py
@@ -633,11 +633,18 @@ class MaskIndex(Index):
 
 class MultiIndex(Index):
     def __init__(self, indexes, *args, **kwargs):
-        self._indexes = list(indexes)
+        self._indexes = list(self._flatten(indexes))
         super().__init__(*args, **kwargs)
         # self.indexes = list(i for i in indexes if len(i))
         # TODO: propagate  index._init_args, index._init_order_by, index._init_kwargs, for each i in indexes?
 
+    def _flatten(self, indexes):
+        for i in indexes:
+            if isinstance(i, MultiIndex):
+                yield from self._flatten(i._indexes)
+            else:
+                yield i
+
     def sel(self, *args, **kwargs):
         if not args and not kwargs:
             return self


Test patch:
diff --git a/tests/grib/test_grib_concat.py b/tests/grib/test_grib_concat.py
index 5c92f47..1e94e1d 100644
--- a/tests/grib/test_grib_concat.py
+++ b/tests/grib/test_grib_concat.py
@@ -150,6 +150,17 @@ def test_grib_from_empty_3():
     _check_save_to_disk(ds3, 8, md)
 
 
+# See github issue #588
+def test_grib_concat_large():
+    ds_e = from_source("empty")
+    ds1 = from_source("file", earthkit_examples_file("test.grib"))
+
+    for _ in range(2000):
+        ds_e += ds1.sel(param="msl")
+
+    assert len(ds_e) == 2000
+
+
 if __name__ == "__main__":
     from earthkit.data.testing import main
Repository: ecmwf/earthkit-data
Pull request #631 (2025-02-25 11:13:51)

Feature/merge main 0.12.5 into develop


Patch:
diff --git a/docs/release_notes/version_0.12_updates.rst b/docs/release_notes/version_0.12_updates.rst
index 9b7ce83..6b985c1 100644
--- a/docs/release_notes/version_0.12_updates.rst
+++ b/docs/release_notes/version_0.12_updates.rst
@@ -2,6 +2,16 @@ Version 0.12 Updates
 /////////////////////////
 
 
+Version 0.12.5
+===============
+
+Fixed
+++++++++
+
+- Fixed issue when trying to iterate an empty source caused a crash (:pr:`630`)
+
+
+
 Version 0.12.4
 ===============
 
diff --git a/src/earthkit/data/sources/empty.py b/src/earthkit/data/sources/empty.py
index a4eb952..94b051c 100644
--- a/src/earthkit/data/sources/empty.py
+++ b/src/earthkit/data/sources/empty.py
@@ -16,6 +16,9 @@ class EmptySource(FieldList):
         # Used by multi-source
         return True
 
+    def __getitem__(self, key):
+        raise IndexError("Empty source")
+
     def __len__(self):
         return 0
 


Test patch:
diff --git a/tests/sources/test_empty.py b/tests/sources/test_empty.py
index 0f5bdc0..3340944 100644
--- a/tests/sources/test_empty.py
+++ b/tests/sources/test_empty.py
@@ -13,7 +13,7 @@ from earthkit.data import from_source
 from earthkit.data.testing import earthkit_examples_file
 
 
-def test_empty_source():
+def test_empty_source_len():
     ds = from_source("empty")
     assert len(ds) == 0
 
@@ -52,3 +52,9 @@ def test_empty_source_concat3():
     assert len(ds) == 2
     meta = ds.metadata("shortName")
     assert meta == ["2t", "msl"]
+
+
+def test_empty_source_iterate():
+    ds = from_source("empty")
+    for _ in ds:
+        assert False, "Empty source should not iterate"
Repository: ecmwf/earthkit-data
Pull request #674 (2025-04-07 14:55:18)

Merge 0.13.8 into develop


Patch:
diff --git a/docs/release_notes/version_0.13_updates.rst b/docs/release_notes/version_0.13_updates.rst
index b17ee63..240fbd3 100644
--- a/docs/release_notes/version_0.13_updates.rst
+++ b/docs/release_notes/version_0.13_updates.rst
@@ -2,6 +2,15 @@ Version 0.13 Updates
 /////////////////////////
 
 
+Version 0.13.8
+===============
+
+Fixes
+++++++++++
+
+- Fixed issue when kwargs passed to the Xarray engine as a dict were modified in-place (:pr:`671`).
+
+
 Version 0.13.7
 ===============
 
diff --git a/src/earthkit/data/readers/grib/xarray.py b/src/earthkit/data/readers/grib/xarray.py
index 0043920..9e68f8d 100644
--- a/src/earthkit/data/readers/grib/xarray.py
+++ b/src/earthkit/data/readers/grib/xarray.py
@@ -7,6 +7,7 @@
 # nor does it submit to any jurisdiction.
 #
 
+import copy
 import logging
 
 from earthkit.data.utils import ensure_dict
@@ -344,6 +345,7 @@ class XarrayMixIn:
             raise ValueError(f"Unsupported engine: {engine}. Please use one of {list(engines.keys())}")
 
         user_xarray_open_dataset_kwargs = ensure_dict(xarray_open_dataset_kwargs)
+        user_xarray_open_dataset_kwargs = copy.deepcopy(user_xarray_open_dataset_kwargs)
         if user_xarray_open_dataset_kwargs and kwargs:
             raise ValueError("Cannot specify extra keyword arguments when xarray_open_dataset_kwargs is set.")
 


Test patch:
diff --git a/tests/xr_engine/test_xr_engine.py b/tests/xr_engine/test_xr_engine.py
index 59a9e45..0910f78 100644
--- a/tests/xr_engine/test_xr_engine.py
+++ b/tests/xr_engine/test_xr_engine.py
@@ -23,6 +23,17 @@ sys.path.insert(0, here)
 from xr_engine_fixtures import load_grib_data  # noqa: E402
 
 
+@pytest.mark.cache
+@pytest.mark.parametrize("engine", ["earthkit", "cfgrib"])
+def test_xr_engine_kwargs_unchanged(engine):
+    ds = from_source("url", earthkit_remote_test_data_file("test-data/xr_engine/level/pl_small.grib"))
+
+    _kwargs = {"squeeze": True}
+    res = ds.to_xarray(engine=engine, xarray_open_dataset_kwargs=_kwargs)
+    assert res is not None
+    assert _kwargs == {"squeeze": True}
+
+
 @pytest.mark.cache
 @pytest.mark.parametrize(
     "file",
Repository: ecmwf/earthkit-data
Pull request #685 (2025-04-28 09:39:12)

Adds flatten argument to to_numpy methods
Adds the `flatten` argument to xarray DataArray and Dataset wrapper classes:
- If `flatten=True`, reshape the resulting NumPy array to be 1-dimensional after converting with `to_numpy()`.
- If `flatten=False` (default), maintain the current behavior and return the regular array.

See #684 for more information.


Patch:
diff --git a/src/earthkit/data/wrappers/xarray.py b/src/earthkit/data/wrappers/xarray.py
index bdbfb2b..fd79680 100644
--- a/src/earthkit/data/wrappers/xarray.py
+++ b/src/earthkit/data/wrappers/xarray.py
@@ -58,14 +58,17 @@ class XArrayDataArrayWrapper(Wrapper):
         """
         return self.data
 
-    def to_numpy(self):
+    def to_numpy(self, flatten=False):
         """Return a numpy `ndarray` representation of the data.
 
         Returns
         -------
         numpy.ndarray
         """
-        return self.data.to_numpy()
+        arr = self.data.to_numpy()
+        if flatten:
+            arr = arr.flatten()
+        return arr
 
     def to_pandas(self, *args, **kwargs):
         """Return a pandas `dataframe` representation of the data.
@@ -91,14 +94,17 @@ class XArrayDatasetWrapper(XArrayDataArrayWrapper):
     methods.
     """
 
-    def to_numpy(self):
+    def to_numpy(self, flatten=False):
         """Return a numpy `ndarray` representation of the data.
 
         Returns
         -------
         numpy.ndarray
         """
-        return self.data.to_array().to_numpy()
+        arr = self.data.to_array().to_numpy()
+        if flatten:
+            arr = arr.flatten()
+        return arr
 
     # def component(self, component):
     #     """


Test patch:
diff --git a/tests/wrappers/test_xarray.py b/tests/wrappers/test_xarray.py
index d3e43b5..d7466cd 100644
--- a/tests/wrappers/test_xarray.py
+++ b/tests/wrappers/test_xarray.py
@@ -49,3 +49,21 @@ def test_xarray_lazy_fieldlist_scan():
     assert ds._fields is None
     assert len(ds) == 2
     assert len(ds._fields) == 2
+
+
+@pytest.mark.no_eccodes
+def test_xarray_to_numpy():
+    import xarray as xr
+
+    data_array = xr.DataArray(
+        [[1, 2, 3], [4, 5, 6]],
+        dims=["x", "y"],
+        coords={"x": [1, 2], "y": [3, 4, 5]},
+    )
+    ds = from_object(data_array)
+
+    arr_2d = ds.to_numpy()
+    assert arr_2d.shape == (2, 3)
+
+    arr_1d = ds.to_numpy(flatten=True)
+    assert arr_1d.shape == (6,)