Skip to content

Commit db3ab06

Browse files
authored
Merge pull request #84 from bcdev/forman-82-slice_source_kwargs
New configuration setting `slice_source_kwargs`
2 parents 8dc8167 + 9835d42 commit db3ab06

File tree

8 files changed

+77
-3
lines changed

8 files changed

+77
-3
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
- Introduced `SliceSource.close()` so
1010
[contextlib.closing()](https://docs.python.org/3/library/contextlib.html#contextlib.closing)
1111
is applicable. Deprecated `SliceSource.dispose()`.
12+
13+
- Introduced new optional configuration setting `slice_source_kwargs` that
14+
contains keyword-arguments, which are passed to a configured `slice_source` together with
15+
each slice item.
16+
1217

1318
## Version 0.6.0 (from 2024-03-12)
1419

docs/guide.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,10 @@ argument to your slice source.
760760
- `dict`: keyword arguments only;
761761
- Any other type is interpreted as single positional argument.
762762

763+
You can also pass extra keyword arguments to your slice source using the
764+
`slice_source_kwargs` setting. Keyword arguments passed as slice items take
765+
precedence, that is, they overwrite arguments passed by `slice_source_kawrgs`.
766+
763767
In addition, your slice source function or class constructor specified by
764768
`slice_source` may define a 1st positional argument or keyword argument
765769
named `ctx`, which will receive the current processing context of type

tests/config/test_config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,24 @@ def test_slice_source_as_type(self):
162162
}
163163
)
164164

165+
def test_slice_source_kwargs(self):
166+
config = Config(
167+
{
168+
"target_dir": "memory://target.zarr",
169+
}
170+
)
171+
self.assertEqual(None, config.slice_source_kwargs)
172+
173+
config = Config(
174+
{
175+
"target_dir": "memory://target.zarr",
176+
"slice_source_kwargs": {"a": 1, "b": True, "c": "nearest"},
177+
}
178+
)
179+
self.assertEqual(
180+
{"a": 1, "b": True, "c": "nearest"}, config.slice_source_kwargs
181+
)
182+
165183

166184
def new_custom_slice_source(ctx: Context, index: int):
167185
return CustomSliceSource(ctx, index)

tests/config/test_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def test_get_config_schema(self):
3131
"slice_engine",
3232
"slice_polling",
3333
"slice_source",
34+
"slice_source_kwargs",
3435
"slice_storage_options",
3536
"target_storage_options",
3637
"target_dir",

tests/slice/test_cm.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
# noinspection PyUnusedLocal
2626

2727

28-
# noinspection PyShadowingBuiltins,PyRedeclaration
28+
# noinspection PyShadowingBuiltins,PyRedeclaration,PyMethodMayBeStatic
2929
class OpenSliceDatasetTest(unittest.TestCase):
3030
def setUp(self):
3131
clear_memory_fs()
@@ -164,7 +164,7 @@ def get_dataset(name):
164164
with slice_cm as slice_ds:
165165
self.assertIsInstance(slice_ds, xr.Dataset)
166166

167-
def test_slice_item_is_slice_source(self):
167+
def test_slice_item_is_slice_source_arg(self):
168168
class MySliceSource(SliceSource):
169169
def __init__(self, name):
170170
self.uri = f"memory://{name}.zarr"
@@ -191,7 +191,7 @@ def close(self):
191191
with slice_cm as slice_ds:
192192
self.assertIsInstance(slice_ds, xr.Dataset)
193193

194-
def test_slice_item_is_deprecated_slice_source(self):
194+
def test_slice_item_is_deprecated_slice_source_arg(self):
195195
class MySliceSource(SliceSource):
196196
def __init__(self, name):
197197
self.uri = f"memory://{name}.zarr"
@@ -219,6 +219,33 @@ def dispose(self):
219219
with slice_cm as slice_ds:
220220
self.assertIsInstance(slice_ds, xr.Dataset)
221221

222+
def test_slice_item_is_slice_source_arg_with_extra_kwargs(self):
223+
class MySliceSource(SliceSource):
224+
def __init__(self, *args, **kwargs):
225+
self.args = args
226+
self.kwargs = kwargs
227+
228+
def get_dataset(self):
229+
return xr.Dataset()
230+
231+
ctx = Context(
232+
dict(
233+
target_dir="memory://target.zarr",
234+
slice_source=MySliceSource,
235+
slice_source_kwargs={"a": 1, "b": True, "c": "nearest"},
236+
)
237+
)
238+
slice_cm = open_slice_dataset(ctx, (["bibo"], {"a": 2, "d": 3.14}))
239+
self.assertIsInstance(slice_cm, SliceSourceContextManager)
240+
slice_source = slice_cm.slice_source
241+
self.assertIsInstance(slice_source, MySliceSource)
242+
with slice_cm as slice_ds:
243+
self.assertIsInstance(slice_ds, xr.Dataset)
244+
self.assertEqual(slice_source.args, ("bibo",))
245+
self.assertEqual(
246+
slice_source.kwargs, {"a": 2, "b": True, "c": "nearest", "d": 3.14}
247+
)
248+
222249

223250
class IsContextManagerTest(unittest.TestCase):
224251
"""Assert that context managers are identified by isinstance()"""

zappend/config/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ def slice_source(self) -> Callable[[...], Any] | None:
133133
"""
134134
return self._slice_source
135135

136+
@property
137+
def slice_source_kwargs(self) -> dict[str, Any] | None:
138+
"""Extra keyword-arguments passed to a specified `slice_source`
139+
together with each slice item.
140+
"""
141+
return self._config.get("slice_source_kwargs")
142+
136143
@property
137144
def slice_storage_options(self) -> dict[str, Any] | None:
138145
"""The configured slice storage options to be used

zappend/config/schema.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,14 @@
643643
"type": "string",
644644
"minLength": 1,
645645
},
646+
slice_source_kwargs={
647+
"description": (
648+
"Extra keyword-arguments passed to a configured `slice_source`"
649+
" together with each slice item."
650+
),
651+
"type": "object",
652+
"additionalProperties": True,
653+
},
646654
slice_engine={
647655
"description": (
648656
"The name of the engine to be used for opening"

zappend/slice/callable.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def invoke_slice_callable(
3030
A slice item of type `SliceItem`.
3131
"""
3232
slice_args, slice_kwargs = to_slice_args(slice_item)
33+
if ctx.config.slice_source_kwargs:
34+
extra_kwargs = dict(ctx.config.slice_source_kwargs)
35+
extra_kwargs.update(slice_kwargs)
36+
slice_kwargs = extra_kwargs
3337

3438
signature = inspect.signature(slice_callable)
3539
ctx_parameter = signature.parameters.get("ctx")

0 commit comments

Comments
 (0)