Skip to content

Commit 4889848

Browse files
committed
Added args validation and removal of existing target
1 parent 5d3b073 commit 4889848

File tree

2 files changed

+93
-20
lines changed

2 files changed

+93
-20
lines changed

tests/test_levels.py

+54
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import unittest
77

8+
import pytest
89
import xarray as xr
910

1011
from zappend.fsutil import FileObj
@@ -75,6 +76,59 @@ class WriteLevelsTest(unittest.TestCase):
7576
def setUp(self):
7677
clear_memory_fs()
7778

79+
# noinspection PyMethodMayBeStatic
80+
def test_args_validation(self):
81+
source_path = "memory://source.zarr"
82+
target_path = "memory://target.levels"
83+
84+
make_test_dataset(
85+
uri=source_path,
86+
dims=("time", "lat", "lon"),
87+
shape=(3, 1024, 2048),
88+
chunks=(1, 128, 256),
89+
)
90+
91+
with pytest.raises(
92+
ValueError,
93+
match="missing 'target_path' argument",
94+
):
95+
write_levels(source_path=source_path)
96+
97+
with pytest.raises(
98+
ValueError,
99+
match="either 'target_dir' or 'target_path' can be given, not both",
100+
):
101+
write_levels(
102+
source_path=source_path,
103+
target_path=target_path,
104+
target_dir=target_path,
105+
)
106+
107+
with pytest.raises(
108+
TypeError,
109+
match="write_levels\\(\\) got an unexpected keyword argument 'config'",
110+
):
111+
write_levels(
112+
source_path=source_path,
113+
target_path=target_path,
114+
config={"dry_run": True},
115+
)
116+
117+
with pytest.raises(
118+
FileNotFoundError,
119+
match="Target parent directory does not exist: /target.levels",
120+
):
121+
with pytest.warns(
122+
UserWarning,
123+
match="'use_saved_levels' argument is not applicable if dry_run=True",
124+
):
125+
write_levels(
126+
source_path=source_path,
127+
target_path=target_path,
128+
dry_run=True,
129+
use_saved_levels=True,
130+
)
131+
78132
def test_default_x_y_with_crs(self):
79133
source_path = "memory://source.zarr"
80134
make_test_dataset(

zappend/levels.py

+39-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import json
66
import logging
7+
import warnings
78
from typing import Any, Hashable
89

910
import xarray as xr
@@ -91,19 +92,29 @@ def write_levels(
9192
If `False`, the default, a level dataset `{target_path}/0.zarr`
9293
will be written instead.
9394
zappend_config:
94-
Configuration passed to the `zappend()` call for each
95-
slice in the append dimension.
95+
Configuration passed to zappend as `zappend(slice, **zappend_config)`
96+
for each slice in the append dimension. The zappend `config`
97+
parameter is not supported.
9698
"""
97-
from xcube.core.tilingscheme import get_num_levels
9899
from xcube.core.gridmapping import GridMapping
99100
from xcube.core.subsampling import get_dataset_agg_methods
100101
from xcube.core.subsampling import subsample_dataset
102+
from xcube.core.tilingscheme import get_num_levels
101103
from xcube.util.fspath import get_fs_path_class
102104

105+
dry_run = zappend_config.pop("dry_run", False)
106+
107+
if dry_run and use_saved_levels:
108+
warnings.warn(f"'use_saved_levels' argument is not applicable if dry_run=True")
109+
use_saved_levels = False
110+
config = zappend_config.pop("config", None)
111+
if config is not None:
112+
raise TypeError("write_levels() got an unexpected keyword argument 'config'")
113+
103114
target_dir = zappend_config.pop("target_dir", None)
104-
if not target_dir and not target_path:
105-
raise ValueError("either 'target_dir' or 'target_path' can be given, not both")
106-
if target_dir and target_path and target_dir != target_path:
115+
if not target_path and not target_dir:
116+
raise ValueError("missing 'target_path' argument")
117+
if target_dir and target_path:
107118
raise ValueError("either 'target_dir' or 'target_path' can be given, not both")
108119
target_path = target_path or target_dir
109120
target_storage_options = zappend_config.pop(
@@ -161,17 +172,24 @@ def write_levels(
161172
variables=zappend_config.pop("variables", None),
162173
)
163174

164-
target_fs.mkdirs(target_root, exist_ok=True)
165-
with target_fs.open(f"{target_root}/.zlevels", "wt") as fp:
166-
levels_data: dict[str, Any] = dict(
167-
version="1.0",
168-
num_levels=num_levels,
169-
agg_methods=agg_methods,
170-
use_saved_levels=use_saved_levels,
171-
)
172-
json.dump(levels_data, fp, indent=2)
173-
174-
if link_level_zero:
175+
if target_fs.exists(target_root):
176+
if target_fs.exists(target_root):
177+
logger.warning(f"Permanently deleting {target_dir}")
178+
if not dry_run:
179+
target_fs.rm(target_root, recursive=True)
180+
181+
if not dry_run:
182+
target_fs.mkdirs(target_root, exist_ok=True)
183+
with target_fs.open(f"{target_root}/.zlevels", "wt") as fp:
184+
levels_data: dict[str, Any] = dict(
185+
version="1.0",
186+
num_levels=num_levels,
187+
agg_methods=agg_methods,
188+
use_saved_levels=use_saved_levels,
189+
)
190+
json.dump(levels_data, fp, indent=2)
191+
192+
if link_level_zero and not dry_run:
175193
path_class = get_fs_path_class(target_fs)
176194
rel_source_path = (
177195
"../"
@@ -218,6 +236,7 @@ def write_levels(
218236
target_dir=level_slice_path,
219237
target_storage_options=target_storage_options,
220238
append_dim=append_dim,
239+
dry_run=dry_run,
221240
force_new=force_new if slice_index == 0 else False,
222241
variables=variables,
223242
**zappend_config,
@@ -227,13 +246,13 @@ def write_levels(
227246
100 * ((slice_index * num_levels) + level_index + 1) / steps_total
228247
)
229248
logger.info(
230-
f"Slice {level_slice_path} written,"
249+
f"Level slice written to {level_slice_path},"
231250
f" {slice_index + 1}/{num_slices} slices,"
232251
f" {level_index + 1}/{num_levels} levels,"
233252
f" {percent_total:.2f}% total"
234253
)
235-
236-
logger.info(f"Done appending {num_slices} slices to {target_path}")
254+
logger.info(f"Done appending {num_levels} level slices to {target_path}")
255+
logger.info(f"Done appending {num_slices} slices to {target_path}")
237256

238257

239258
def get_variables_config(

0 commit comments

Comments
 (0)