Skip to content

Commit 0f053c7

Browse files
committed
Change from 'slice' to 'offset'.
1 parent 4ad9e5f commit 0f053c7

File tree

5 files changed

+44
-29
lines changed

5 files changed

+44
-29
lines changed

tiled/_tests/test_writing.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ def test_extend_array(tree):
139139
# Patching data into a region beyond the current extent of the array
140140
# raises a ValueError (catching a 409 from the server).
141141
with pytest.raises(ValueError):
142-
ac.patch(new_data, slice=slice(3, 4))
142+
ac.patch(new_data, offset=(3,))
143143
# With extend=True, the array is expanded.
144-
ac.patch(new_data, slice=slice(3, 4), extend=True)
144+
ac.patch(new_data, offset=(3,), extend=True)
145145
# The local cache of the structure is updated.
146146
assert ac.shape == full_array.shape
147147
actual = ac.read()
@@ -153,18 +153,22 @@ def test_extend_array(tree):
153153
revised_data = numpy.ones((1, 2, 2)) * 3
154154
revised_array = full_array.copy()
155155
revised_array[3, :, :] = 3
156-
ac.patch(revised_data, slice=slice(3, 4))
156+
ac.patch(revised_data, offset=(3,))
157157
numpy.testing.assert_equal(ac.read(), revised_array)
158158

159159
# Extend out of order.
160160
ones = numpy.ones((1, 2, 2))
161-
ac.patch(ones * 7, slice=slice(7, 8), extend=True)
162-
ac.patch(ones * 5, slice=slice(5, 6), extend=True)
163-
ac.patch(ones * 6, slice=slice(6, 7), extend=True)
161+
ac.patch(ones * 7, offset=(7,), extend=True)
162+
ac.patch(ones * 5, offset=(5,), extend=True)
163+
ac.patch(ones * 6, offset=(6,), extend=True)
164164
numpy.testing.assert_equal(ac[5:6], ones * 5)
165165
numpy.testing.assert_equal(ac[6:7], ones * 6)
166166
numpy.testing.assert_equal(ac[7:8], ones * 7)
167167

168+
# Offset given as an int is acceptable.
169+
ac.patch(ones * 8, offset=8, extend=True)
170+
numpy.testing.assert_equal(ac[8:9], ones * 8)
171+
168172

169173
def test_write_dataframe_full(tree):
170174
with Context.from_app(

tiled/adapters/zarr.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ async def write_block(
182182
async def patch(
183183
self,
184184
data: NDArray[Any],
185-
slice: Tuple[Union[slice, int], ...],
185+
offset: Tuple[int, ...],
186186
extend: bool = False,
187187
) -> Tuple[Tuple[int, ...], Tuple[Tuple[int, ...], ...]]:
188188
"""
@@ -194,7 +194,7 @@ async def patch(
194194
Parameters
195195
----------
196196
data : array-like
197-
slice :
197+
offset : tuple[int]
198198
Where to place the new data
199199
extend : bool
200200
If slice does not fit wholly within the shape of the existing array,
@@ -207,14 +207,15 @@ async def patch(
207207
and expand is False
208208
"""
209209
current_shape = self._array.shape
210-
new_shape = list(current_shape)
211-
for i, (s, dim) in enumerate(zip(slice, current_shape)):
212-
if isinstance(s, int):
213-
new_shape[i] = max(new_shape[i], s)
214-
elif isinstance(s, builtins.slice) and isinstance(s.stop, int):
215-
new_shape[i] = max(new_shape[i], s.stop)
216-
else:
217-
raise TypeError(f"Unexpected slice parameter: {slice}")
210+
normalized_offset = [0] * len(current_shape)
211+
normalized_offset[: len(offset)] = list(offset)
212+
new_shape = []
213+
slice_ = []
214+
for data_dim, offset_dim, current_dim in zip(
215+
data.shape, normalized_offset, current_shape
216+
):
217+
new_shape.append(max(current_dim, data_dim + offset_dim))
218+
slice_.append(slice(offset_dim, offset_dim + data_dim))
218219
new_shape_tuple = tuple(new_shape)
219220
if new_shape_tuple != current_shape:
220221
if extend:
@@ -223,9 +224,9 @@ async def patch(
223224
else:
224225
raise Conflicts(
225226
f"Slice {slice} does not fit into array shape {current_shape}. "
226-
f"Use ?extend=true to extend array dimension to fit."
227+
"Use ?extend=true to extend array dimension to fit."
227228
)
228-
self._array[slice] = data
229+
self._array[tuple(slice_)] = data
229230
new_chunks = []
230231
# Zarr has regularly-sized chunks, so no user input is required to
231232
# simply extend the existing pattern.

tiled/client/array.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import itertools
2+
from typing import Union
23

34
import dask
45
import dask.array
@@ -7,7 +8,6 @@
78
from numpy.typing import NDArray
89

910
from ..structures.core import STRUCTURE_TYPES
10-
from ..type_aliases import NDSlice
1111
from .base import BaseClient
1212
from .utils import export_util, handle_error, params_from_slice
1313

@@ -181,15 +181,15 @@ def write_block(self, array, block, slice=...):
181181
)
182182
)
183183

184-
def patch(self, array: NDArray, slice: NDSlice, extend=False):
184+
def patch(self, array: NDArray, offset: Union[int, tuple[int, ...]], extend=False):
185185
"""
186186
Write data into a slice of an array, maybe extending the shape.
187187
188188
Parameters
189189
----------
190190
array : array-like
191191
The data to write
192-
slice : NDSlice
192+
offset : tuple[int, ...]
193193
Where to place this data in the array
194194
extend : bool
195195
Extend the array shape to fit the new slice, if necessary
@@ -217,7 +217,7 @@ def patch(self, array: NDArray, slice: NDSlice, extend=False):
217217
218218
Extend the array by concatenating a (1, 2, 2) array of zeros.
219219
220-
>>> ac.patch(numpy.zeros((1, 2, 2)), slice=slice(3, 4), extend=True)
220+
>>> ac.patch(numpy.zeros((1, 2, 2)), offset=(3,), extend=True)
221221
222222
Read it.
223223
@@ -234,9 +234,13 @@ def patch(self, array: NDArray, slice: NDSlice, extend=False):
234234
[0., 0.]]])
235235
"""
236236
array_ = numpy.ascontiguousarray(array)
237-
params = params_from_slice(slice)
238-
params["shape"] = ",".join(map(str, array_.shape))
239-
params["extend"] = bool(extend)
237+
if isinstance(offset, int):
238+
offset = (offset,)
239+
params = {
240+
"offset": ",".join(map(str, offset)),
241+
"shape": ",".join(map(str, array_.shape)),
242+
"extend": bool(extend),
243+
}
240244
response = self.context.http_client.patch(
241245
self.item["links"]["full"],
242246
content=array_.tobytes(),

tiled/server/dependencies.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ def shape_param(
172172
return tuple(map(int, shape.split(",")))
173173

174174

175+
def offset_param(
176+
offset: str = Query(..., min_length=1, pattern="^[0-9]+(,[0-9]+)*$"),
177+
):
178+
"Specify and parse an offset parameter."
179+
return tuple(map(int, offset.split(",")))
180+
181+
175182
def np_style_slicer(indices: tuple):
176183
return indices[0] if len(indices) == 1 else slice_func(*indices)
177184

tiled/server/router.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
get_query_registry,
5656
get_serialization_registry,
5757
get_validation_registry,
58+
offset_param,
5859
shape_param,
5960
slice_,
6061
)
@@ -1294,7 +1295,7 @@ async def put_array_block(
12941295
@router.patch("/array/full/{path:path}")
12951296
async def patch_array_full(
12961297
request: Request,
1297-
slice=Depends(slice_),
1298+
offset=Depends(offset_param),
12981299
shape=Depends(shape_param),
12991300
extend: bool = False,
13001301
entry=SecureEntry(
@@ -1303,8 +1304,6 @@ async def patch_array_full(
13031304
),
13041305
deserialization_registry=Depends(get_deserialization_registry),
13051306
):
1306-
if slice is None:
1307-
slice = ...
13081307
if not hasattr(entry, "patch"):
13091308
raise HTTPException(
13101309
status_code=HTTP_405_METHOD_NOT_ALLOWED,
@@ -1316,7 +1315,7 @@ async def patch_array_full(
13161315
media_type = request.headers["content-type"]
13171316
deserializer = deserialization_registry.dispatch("array", media_type)
13181317
data = await ensure_awaitable(deserializer, body, dtype, shape)
1319-
structure = await ensure_awaitable(entry.patch, data, slice, extend)
1318+
structure = await ensure_awaitable(entry.patch, data, offset, extend)
13201319
return json_or_msgpack(request, structure)
13211320

13221321

0 commit comments

Comments
 (0)