Skip to content

Commit c4f97d1

Browse files
authored
Merge pull request #58 from bcdev/forman-56-no_gobal_attrs
Attributes of target dataset no longer empty
2 parents b654f6f + 4c50302 commit c4f97d1

File tree

4 files changed

+81
-14
lines changed

4 files changed

+81
-14
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Fixes
44

5+
* Global metadata attributes of target dataset is no longer empty. [#56]
6+
57
* If the target _parent_ directory did not exist, an exception was raised
68
reporting that the lock file to be written does not exist. Changed this to
79
report that the target parent directory does not exist. [#55]

tests/helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ def make_test_dataset(
108108
np.linspace(0, 1, shape[2], dtype="float64"), dims=dims[2]
109109
),
110110
},
111+
attrs={
112+
"Conventions": "CF-1.8",
113+
"title": f"Test {index + 1}-{index + shape[0]}",
114+
},
111115
)
112116

113117
if crs:

tests/test_api.py

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,37 @@ def test_no_slices(self):
2626
zappend([], target_dir=target_dir)
2727
self.assertFalse(FileObj(target_dir).exists())
2828

29+
def test_one_slices_memory(self):
30+
target_dir = "memory://target.zarr"
31+
slices = [make_test_dataset()]
32+
zappend(slices, target_dir=target_dir)
33+
ds = xr.open_zarr(target_dir)
34+
self.assertEqual({"time": 3, "y": 50, "x": 100}, ds.sizes)
35+
self.assertEqual({"chl", "tsm"}, set(ds.data_vars))
36+
self.assertEqual({"time", "y", "x"}, set(ds.coords))
37+
self.assertEqual(
38+
{
39+
"Conventions": "CF-1.8",
40+
"title": "Test 1-3",
41+
},
42+
ds.attrs,
43+
)
44+
2945
def test_some_slices_memory(self):
3046
target_dir = "memory://target.zarr"
31-
slices = [make_test_dataset(), make_test_dataset(), make_test_dataset()]
47+
slices = [make_test_dataset(index=3 * i) for i in range(3)]
3248
zappend(slices, target_dir=target_dir)
3349
ds = xr.open_zarr(target_dir)
3450
self.assertEqual({"time": 9, "y": 50, "x": 100}, ds.sizes)
3551
self.assertEqual({"chl", "tsm"}, set(ds.data_vars))
3652
self.assertEqual({"time", "y", "x"}, set(ds.coords))
53+
self.assertEqual(
54+
{
55+
"Conventions": "CF-1.8",
56+
"title": "Test 1-3",
57+
},
58+
ds.attrs,
59+
)
3760

3861
def test_some_slices_local(self):
3962
target_dir = "target.zarr"
@@ -42,14 +65,21 @@ def test_some_slices_local(self):
4265
"slice-2.zarr",
4366
"slice-3.zarr",
4467
]
45-
for uri in slices:
46-
make_test_dataset(uri=uri)
68+
for index, uri in enumerate(slices):
69+
make_test_dataset(uri=uri, index=3 * index)
4770
try:
4871
zappend(slices, target_dir=target_dir)
4972
ds = xr.open_zarr(target_dir)
5073
self.assertEqual({"time": 9, "y": 50, "x": 100}, ds.sizes)
5174
self.assertEqual({"chl", "tsm"}, set(ds.data_vars))
5275
self.assertEqual({"time", "y", "x"}, set(ds.coords))
76+
self.assertEqual(
77+
{
78+
"Conventions": "CF-1.8",
79+
"title": "Test 1-3",
80+
},
81+
ds.attrs,
82+
)
5383
finally:
5484
shutil.rmtree(target_dir, ignore_errors=True)
5585
for slice_dir in slices:
@@ -72,52 +102,74 @@ def test_some_slices_local_output_to_non_existing_dir(self):
72102

73103
def test_some_slices_with_class_slice_source(self):
74104
target_dir = "memory://target.zarr"
75-
slices = [make_test_dataset(), make_test_dataset(), make_test_dataset()]
105+
slices = [make_test_dataset(index=3 * i) for i in range(3)]
76106
zappend(slices, target_dir=target_dir, slice_source=MySliceSource)
77107
ds = xr.open_zarr(target_dir)
78108
self.assertEqual({"time": 9, "y": 50, "x": 100}, ds.sizes)
79109
self.assertEqual({"chl"}, set(ds.data_vars))
80110
self.assertEqual({"time", "y", "x"}, set(ds.coords))
111+
self.assertEqual(
112+
{
113+
"Conventions": "CF-1.8",
114+
"title": "Test 1-3",
115+
},
116+
ds.attrs,
117+
)
81118

82119
def test_some_slices_with_func_slice_source(self):
83120
def process_slice(ctx, slice_ds: xr.Dataset) -> SliceSource:
84121
return MySliceSource(ctx, slice_ds)
85122

86123
target_dir = "memory://target.zarr"
87-
slices = [make_test_dataset(), make_test_dataset(), make_test_dataset()]
124+
slices = [make_test_dataset(index=3 * i) for i in range(3)]
88125
zappend(slices, target_dir=target_dir, slice_source=process_slice)
89126
ds = xr.open_zarr(target_dir)
90127
self.assertEqual({"time": 9, "y": 50, "x": 100}, ds.sizes)
91128
self.assertEqual({"chl"}, set(ds.data_vars))
92129
self.assertEqual({"time", "y", "x"}, set(ds.coords))
130+
self.assertEqual(
131+
{
132+
"Conventions": "CF-1.8",
133+
"title": "Test 1-3",
134+
},
135+
ds.attrs,
136+
)
93137

94138
def test_some_slices_with_inc_append_step(self):
95139
target_dir = "memory://target.zarr"
96-
slices = [
97-
make_test_dataset(index=0, shape=(1, 50, 100)),
98-
make_test_dataset(index=1, shape=(1, 50, 100)),
99-
make_test_dataset(index=2, shape=(1, 50, 100)),
100-
]
140+
slices = [make_test_dataset(index=i, shape=(1, 50, 100)) for i in range(3)]
101141
zappend(slices, target_dir=target_dir, append_step="1D")
102142
ds = xr.open_zarr(target_dir)
103143
np.testing.assert_array_equal(
104144
ds.time.values,
105145
np.array(["2024-01-01", "2024-01-02", "2024-01-03"], dtype=np.datetime64),
106146
)
147+
self.assertEqual(
148+
{
149+
"Conventions": "CF-1.8",
150+
"title": "Test 1-1",
151+
},
152+
ds.attrs,
153+
)
107154

108155
def test_some_slices_with_dec_append_step(self):
109156
target_dir = "memory://target.zarr"
110157
slices = [
111-
make_test_dataset(index=2, shape=(1, 50, 100)),
112-
make_test_dataset(index=1, shape=(1, 50, 100)),
113-
make_test_dataset(index=0, shape=(1, 50, 100)),
158+
make_test_dataset(index=i, shape=(1, 50, 100)) for i in reversed(range(3))
114159
]
115160
zappend(slices, target_dir=target_dir, append_step="-1D")
116161
ds = xr.open_zarr(target_dir)
117162
np.testing.assert_array_equal(
118163
ds.time.values,
119164
np.array(["2024-01-03", "2024-01-02", "2024-01-01"], dtype=np.datetime64),
120165
)
166+
self.assertEqual(
167+
{
168+
"Conventions": "CF-1.8",
169+
"title": "Test 3-3",
170+
},
171+
ds.attrs,
172+
)
121173

122174
# # See https://github.com/bcdev/zappend/issues/21
123175
#

zappend/tailoring.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,18 @@ def tailor_slice_dataset(
4040
# as dimension, e.g., "x", "y", "crs", ...
4141
dataset = dataset.drop_vars(const_variables)
4242

43+
# https://github.com/bcdev/zappend/issues/56
44+
# slice_dataset.to_zarr(store, mode="a", ...) will replace
45+
# global attributes.
46+
# Therefore, we must replace slice dataset attributes by
47+
# existing target dataset attributes.
48+
# However, users should be able to select the appropriate
49+
# operation, e.g., a new config setting target_attrs_op with
50+
# values "first" (default), "last", "update".
51+
dataset.attrs = target_metadata.attrs
52+
4353
# Remove any encoding and attributes from slice,
4454
# since both are prescribed by target
45-
dataset.attrs.clear()
4655
for variable in dataset.variables.values():
4756
variable.encoding = {}
4857
variable.attrs = {}

0 commit comments

Comments
 (0)