Skip to content

Commit 7305962

Browse files
authored
Merge pull request #915 from davidhassell/zarr-write
Zarr write + Zarr groups
2 parents 6eeb074 + 0c8556b commit 7305962

File tree

14 files changed

+405
-39
lines changed

14 files changed

+405
-39
lines changed

Changelog.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
Version NEXTVERSION
2-
-------------------
2+
--------------
33

44
**2026-01-??**
55

6+
* Write Zarr v3 datasets with `cf.write`
7+
(https://github.com/NCAS-CMS/cf-python/issues/895)
8+
* Read Zarr v2 and v3 datasets that contain a group hierarchy with
9+
`cf.read` (https://github.com/NCAS-CMS/cf-python/issues/894)
10+
* Reduce the time taken to import `cf`
11+
(https://github.com/NCAS-CMS/cf-python/issues/902)
12+
* New optional dependency: ``zarr>=3.1.3``
613
* New function to control the creation of cached elements during data
714
display: `cf.display_data`
815
(https://github.com/NCAS-CMS/cf-python/issues/913)

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ of its array manipulation and can:
8787
* read field constructs from netCDF, CDL, Zarr, PP and UM datasets with a
8888
choice of netCDF backends,and in local, http, and s3 locations,
8989
* create new field constructs in memory,
90-
* write and append field and domain constructs to netCDF datasets on disk,
90+
* write and append field and domain constructs to netCDF and Zarr v3
91+
datasets on disk,
9192
* read, create, and manipulate UGRID mesh topologies,
9293
* read, write, and create coordinates defined by geometry cells,
9394
* read netCDF and CDL datasets containing hierarchical groups,

cf/aggregate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4112,7 +4112,7 @@ def _get_hfl(
41124112
# Record the bounds of the first and last (sorted) cells
41134113
first, last = hfl_cache.flb.get(hash_value, (None, None))
41144114
if first is None:
4115-
cached_elements = d._get_cached_elements()
4115+
cached_elements = d.get_cached_elements()
41164116
x = []
41174117
for i in (0, 1, -2, -1):
41184118
value = cached_elements.get(i)

cf/data/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2761,7 +2761,7 @@ def Units(self, value):
27612761
self._set_dask(dx, clear=self._ALL ^ self._CACHE ^ self._CFA)
27622762

27632763
# Adjust cached values for the new units
2764-
cache = self._get_cached_elements()
2764+
cache = self.get_cached_elements()
27652765
if cache:
27662766
self._set_cached_elements(
27672767
{index: cf_func(value) for index, value in cache.items()}

cf/dimensioncoordinate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def _infer_direction(self):
165165
if data is not None:
166166
# Infer the direction from the data
167167
if data.size > 1:
168-
c = data._get_cached_elements()
168+
c = data.get_cached_elements()
169169
if c:
170170
try:
171171
return bool(c.get(0) <= c.get(1))
@@ -179,7 +179,7 @@ def _infer_direction(self):
179179
data = self.get_bounds_data(None, _fill_value=False)
180180
if data is not None:
181181
# Infer the direction from the bounds
182-
c = data._get_cached_elements()
182+
c = data.get_cached_elements()
183183
if c:
184184
try:
185185
return bool(c.get(0) <= c.get(1))

cf/field.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6994,7 +6994,7 @@ def collapse(
69946994
else:
69956995
b = dim.data
69966996

6997-
cached_elements = b._get_cached_elements()
6997+
cached_elements = b.get_cached_elements()
69986998
try:
69996999
# Try to set the new bounds from cached values
70007000
bounds_data = Data(

cf/functions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3198,7 +3198,7 @@ def environment(display=True, paths=True):
31983198
netCDF4: 1.7.2 /home/miniconda3/lib/python3.12/site-packages/netCDF4/__init__.py
31993199
h5netcdf: 1.3.0 /home/miniconda3/lib/python3.12/site-packages/h5netcdf/__init__.py
32003200
h5py: 3.12.1 /home/miniconda3/lib/python3.12/site-packages/h5py/__init__.py
3201-
zarr: 3.0.8 /home/miniconda3/lib/python3.12/site-packages/zarr/__init__.py
3201+
zarr: 3.1.3 /home/miniconda3/lib/python3.12/site-packages/zarr/__init__.py
32023202
s3fs: 2024.12.0 /home/miniconda3/lib/python3.12/site-packages/s3fs/__init__.py
32033203
scipy: 1.15.1 /home/miniconda3/lib/python3.12/site-packages/scipy/__init__.py
32043204
dask: 2025.5.1 /home/miniconda3/lib/python3.12/site-packages/dask/__init__.py
@@ -3224,7 +3224,7 @@ def environment(display=True, paths=True):
32243224
netCDF4: 1.7.2
32253225
h5netcdf: 1.3.0
32263226
h5py: 3.12.1
3227-
zarr: 3.0.8
3227+
zarr: 3.1.3
32283228
s3fs: 2024.12.0
32293229
scipy: 1.15.1
32303230
dask: 2025.5.1

cf/read_write/read.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ class read(cfdm.read):
316316
317317
.. versionadded:: 3.17.0
318318
319+
{{read store_dataset_shards: `bool`, optional}}
320+
321+
.. versionadded:: NEXTVERSION
322+
319323
{{read cfa: `dict`, optional}}
320324
321325
.. versionadded:: 3.15.0
@@ -328,6 +332,10 @@ class read(cfdm.read):
328332
329333
.. versionadded:: 3.17.0
330334
335+
{{read group_dimension_search: `str`, optional}}
336+
337+
.. versionadded:: NEXTVERSION
338+
331339
umversion: deprecated at version 3.0.0
332340
Use the *um* parameter instead.
333341
@@ -434,6 +442,7 @@ def __new__(
434442
warn_valid=False,
435443
dask_chunks="storage-aligned",
436444
store_dataset_chunks=True,
445+
store_dataset_shards=True,
437446
domain=False,
438447
cfa=None,
439448
cfa_write=None,
@@ -445,6 +454,7 @@ def __new__(
445454
ignore_read_error=False,
446455
fmt=None,
447456
file_type=None,
457+
group_dimension_search="closest_ancestor",
448458
):
449459
"""Read field or domain constructs from a dataset."""
450460
kwargs = locals()

cf/test/test_Data.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4457,28 +4457,30 @@ def test_Data__init__datetime(self):
44574457
self.assertTrue((q == d).array.all())
44584458
self.assertTrue((d == q).array.all())
44594459

4460-
def test_Data__str__(self):
4461-
"""Test `Data.__str__`"""
4462-
elements0 = (0, -1, 1)
4463-
for array in ([1], [1, 2], [1, 2, 3]):
4464-
d = cf.Data(array)
4465-
d[0] = 1
4466-
self.assertEqual(str(d), str(array))
4467-
d += 0
4468-
self.assertEqual(str(d), str(array))
4469-
4470-
# Test when size > 3, i.e. second element is not there.
4471-
d = cf.Data([1, 2, 3, 4])
4472-
4473-
self.assertEqual(str(d), "[1, ..., 4]")
4474-
cache = d.get_cached_elements()
4475-
self.assertNotIn(1, cache)
4476-
for element in elements0[:2]:
4477-
self.assertIn(element, cache)
4478-
4479-
d[0] = 1
4480-
for element in elements0:
4481-
self.assertNotIn(element, d.get_cached_elements())
4460+
def test_Data__repr__str(self):
4461+
"""Test all means of Data inspection."""
4462+
for d in [
4463+
cf.Data(9, units="km"),
4464+
cf.Data([9], units="km"),
4465+
cf.Data([[9]], units="km"),
4466+
cf.Data([8, 9], units="km"),
4467+
cf.Data([[8, 9]], units="km"),
4468+
cf.Data([7, 8, 9], units="km"),
4469+
cf.Data([[7, 8, 9]], units="km"),
4470+
cf.Data([6, 7, 8, 9], units="km"),
4471+
cf.Data([[6, 7, 8, 9]], units="km"),
4472+
cf.Data([[6, 7], [8, 9]], units="km"),
4473+
cf.Data([[6, 7, 8, 9], [6, 7, 8, 9]], units="km"),
4474+
]:
4475+
_ = repr(d)
4476+
_ = str(d)
4477+
4478+
# Test when the data contains date-times with the first
4479+
# element masked
4480+
dt = np.ma.array([10, 20], mask=[True, False])
4481+
d = cf.Data(dt, units="days since 2000-01-01")
4482+
self.assertTrue(str(d) == "[--, 2000-01-21 00:00:00]")
4483+
self.assertTrue(repr(d) == "<CF Data(2): [--, 2000-01-21 00:00:00]>")
44824484

44834485
def test_Data_cull_graph(self):
44844486
"""Test Data.cull_graph."""

0 commit comments

Comments
 (0)