From 7b9e04406bff0dc126a1a3b47dda5c014fbd8b19 Mon Sep 17 00:00:00 2001 From: "stephen.worsley" Date: Thu, 28 May 2026 14:08:24 +0100 Subject: [PATCH 1/3] add support for handling zarr urls --- lib/iris/fileformats/__init__.py | 14 ++++++++++++++ lib/iris/fileformats/netcdf/saver.py | 5 ++++- lib/iris/io/__init__.py | 5 ++++- lib/iris/loading.py | 4 ++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/iris/fileformats/__init__.py b/lib/iris/fileformats/__init__.py index 0af0888b3f..8de17eb5b8 100644 --- a/lib/iris/fileformats/__init__.py +++ b/lib/iris/fileformats/__init__.py @@ -155,6 +155,20 @@ def _load_grib(*args, **kwargs): ) +# +# Zarr files. +# +FORMAT_AGENT.add_spec( + FormatSpecification( + "zarr", + FileExtension(requires_fh=False), + ".zarr#mode=nczarr,file", + netcdf.load_cubes, + priority=3, + ) +) + + # # UM Fieldsfiles. # diff --git a/lib/iris/fileformats/netcdf/saver.py b/lib/iris/fileformats/netcdf/saver.py index 98c565e990..1cf0635388 100644 --- a/lib/iris/fileformats/netcdf/saver.py +++ b/lib/iris/fileformats/netcdf/saver.py @@ -418,7 +418,10 @@ def __init__(self, filename, netcdf_format, compute=True): else: # Given a filepath string/path : create a dataset from that try: - self.filepath = Path(filename).absolute() + if ".zarr" not in filename: + self.filepath = Path(filename).absolute() + else: + self.filepath = Path(filename) self._dataset = _thread_safe_nc.DatasetWrapper( self.filepath, mode="w", format=netcdf_format ) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 1a895ff1a3..3a6f38c44d 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -129,7 +129,10 @@ def decode_uri(uri, default="file"): # put - last in the brackets so it refers to the character, not a range # reference on valid schemes: https://tools.ietf.org/html/std66#section-3.1 match = re.match(r"^([a-zA-Z][a-zA-Z0-9+.-]+):(.+)", uri) - if match: + if ".zarr" in uri: + scheme = "zarr" + part = uri + elif match: scheme = match.group(1) part = match.group(2) else: diff --git a/lib/iris/loading.py b/lib/iris/loading.py index 68042847c1..69bfab30e3 100644 --- a/lib/iris/loading.py +++ b/lib/iris/loading.py @@ -97,6 +97,10 @@ def _generate_cubes(uris, callback, constraints): urls = [":".join(x) for x in groups] for cube in iris.io.load_http(urls, callback): yield cube + elif scheme == "zarr": + urls = [x[1] for x in groups] + for cube in iris.io.load_http(urls, callback): + yield cube elif scheme == "data": data_objects = [x[1] for x in groups] for cube in iris.io.load_data_objects(data_objects, callback): From 398600ea31704f932f69bf0940e32ffc431fc853 Mon Sep 17 00:00:00 2001 From: "stephen.worsley" Date: Thu, 28 May 2026 14:32:16 +0100 Subject: [PATCH 2/3] fix filepath handling --- lib/iris/fileformats/netcdf/saver.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/iris/fileformats/netcdf/saver.py b/lib/iris/fileformats/netcdf/saver.py index 1cf0635388..fc420a2a99 100644 --- a/lib/iris/fileformats/netcdf/saver.py +++ b/lib/iris/fileformats/netcdf/saver.py @@ -418,10 +418,11 @@ def __init__(self, filename, netcdf_format, compute=True): else: # Given a filepath string/path : create a dataset from that try: - if ".zarr" not in filename: - self.filepath = Path(filename).absolute() + filepath = Path(filename) + if ".zarr" not in filepath.suffix: + self.filepath = filepath.absolute() else: - self.filepath = Path(filename) + self.filepath = filepath self._dataset = _thread_safe_nc.DatasetWrapper( self.filepath, mode="w", format=netcdf_format ) From beb11097b2bb409960c0fd720ff8577324f3f4c6 Mon Sep 17 00:00:00 2001 From: "stephen.worsley" Date: Thu, 28 May 2026 14:43:01 +0100 Subject: [PATCH 3/3] fix test --- lib/iris/tests/results/file_load/known_loaders.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/iris/tests/results/file_load/known_loaders.txt b/lib/iris/tests/results/file_load/known_loaders.txt index 98ac3e4a07..8aa7afc4e1 100644 --- a/lib/iris/tests/results/file_load/known_loaders.txt +++ b/lib/iris/tests/results/file_load/known_loaders.txt @@ -14,4 +14,5 @@ * UM Fieldsfile (FF) converted with ieee to 32 bit (priority 3) * UM Fieldsfile (FF) pre v3.1 (priority 3) * UM Post Processing file (PP) little-endian (priority 3) + * zarr (priority 3) * GRIB (priority 1) \ No newline at end of file