Skip to content

Commit

Permalink
Change from 'slice' to 'offset'.
Browse files Browse the repository at this point in the history
  • Loading branch information
danielballan committed Nov 5, 2024
1 parent 4ad9e5f commit 0f053c7
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 29 deletions.
16 changes: 10 additions & 6 deletions tiled/_tests/test_writing.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ def test_extend_array(tree):
# Patching data into a region beyond the current extent of the array
# raises a ValueError (catching a 409 from the server).
with pytest.raises(ValueError):
ac.patch(new_data, slice=slice(3, 4))
ac.patch(new_data, offset=(3,))
# With extend=True, the array is expanded.
ac.patch(new_data, slice=slice(3, 4), extend=True)
ac.patch(new_data, offset=(3,), extend=True)
# The local cache of the structure is updated.
assert ac.shape == full_array.shape
actual = ac.read()
Expand All @@ -153,18 +153,22 @@ def test_extend_array(tree):
revised_data = numpy.ones((1, 2, 2)) * 3
revised_array = full_array.copy()
revised_array[3, :, :] = 3
ac.patch(revised_data, slice=slice(3, 4))
ac.patch(revised_data, offset=(3,))
numpy.testing.assert_equal(ac.read(), revised_array)

# Extend out of order.
ones = numpy.ones((1, 2, 2))
ac.patch(ones * 7, slice=slice(7, 8), extend=True)
ac.patch(ones * 5, slice=slice(5, 6), extend=True)
ac.patch(ones * 6, slice=slice(6, 7), extend=True)
ac.patch(ones * 7, offset=(7,), extend=True)
ac.patch(ones * 5, offset=(5,), extend=True)
ac.patch(ones * 6, offset=(6,), extend=True)
numpy.testing.assert_equal(ac[5:6], ones * 5)
numpy.testing.assert_equal(ac[6:7], ones * 6)
numpy.testing.assert_equal(ac[7:8], ones * 7)

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


def test_write_dataframe_full(tree):
with Context.from_app(
Expand Down
25 changes: 13 additions & 12 deletions tiled/adapters/zarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async def write_block(
async def patch(
self,
data: NDArray[Any],
slice: Tuple[Union[slice, int], ...],
offset: Tuple[int, ...],
extend: bool = False,
) -> Tuple[Tuple[int, ...], Tuple[Tuple[int, ...], ...]]:
"""
Expand All @@ -194,7 +194,7 @@ async def patch(
Parameters
----------
data : array-like
slice :
offset : tuple[int]
Where to place the new data
extend : bool
If slice does not fit wholly within the shape of the existing array,
Expand All @@ -207,14 +207,15 @@ async def patch(
and expand is False
"""
current_shape = self._array.shape
new_shape = list(current_shape)
for i, (s, dim) in enumerate(zip(slice, current_shape)):
if isinstance(s, int):
new_shape[i] = max(new_shape[i], s)
elif isinstance(s, builtins.slice) and isinstance(s.stop, int):
new_shape[i] = max(new_shape[i], s.stop)
else:
raise TypeError(f"Unexpected slice parameter: {slice}")
normalized_offset = [0] * len(current_shape)
normalized_offset[: len(offset)] = list(offset)
new_shape = []
slice_ = []
for data_dim, offset_dim, current_dim in zip(
data.shape, normalized_offset, current_shape
):
new_shape.append(max(current_dim, data_dim + offset_dim))
slice_.append(slice(offset_dim, offset_dim + data_dim))
new_shape_tuple = tuple(new_shape)
if new_shape_tuple != current_shape:
if extend:
Expand All @@ -223,9 +224,9 @@ async def patch(
else:
raise Conflicts(
f"Slice {slice} does not fit into array shape {current_shape}. "
f"Use ?extend=true to extend array dimension to fit."
"Use ?extend=true to extend array dimension to fit."
)
self._array[slice] = data
self._array[tuple(slice_)] = data
new_chunks = []
# Zarr has regularly-sized chunks, so no user input is required to
# simply extend the existing pattern.
Expand Down
18 changes: 11 additions & 7 deletions tiled/client/array.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import itertools
from typing import Union

import dask
import dask.array
Expand All @@ -7,7 +8,6 @@
from numpy.typing import NDArray

from ..structures.core import STRUCTURE_TYPES
from ..type_aliases import NDSlice
from .base import BaseClient
from .utils import export_util, handle_error, params_from_slice

Expand Down Expand Up @@ -181,15 +181,15 @@ def write_block(self, array, block, slice=...):
)
)

def patch(self, array: NDArray, slice: NDSlice, extend=False):
def patch(self, array: NDArray, offset: Union[int, tuple[int, ...]], extend=False):
"""
Write data into a slice of an array, maybe extending the shape.
Parameters
----------
array : array-like
The data to write
slice : NDSlice
offset : tuple[int, ...]
Where to place this data in the array
extend : bool
Extend the array shape to fit the new slice, if necessary
Expand Down Expand Up @@ -217,7 +217,7 @@ def patch(self, array: NDArray, slice: NDSlice, extend=False):
Extend the array by concatenating a (1, 2, 2) array of zeros.
>>> ac.patch(numpy.zeros((1, 2, 2)), slice=slice(3, 4), extend=True)
>>> ac.patch(numpy.zeros((1, 2, 2)), offset=(3,), extend=True)
Read it.
Expand All @@ -234,9 +234,13 @@ def patch(self, array: NDArray, slice: NDSlice, extend=False):
[0., 0.]]])
"""
array_ = numpy.ascontiguousarray(array)
params = params_from_slice(slice)
params["shape"] = ",".join(map(str, array_.shape))
params["extend"] = bool(extend)
if isinstance(offset, int):
offset = (offset,)
params = {
"offset": ",".join(map(str, offset)),
"shape": ",".join(map(str, array_.shape)),
"extend": bool(extend),
}
response = self.context.http_client.patch(
self.item["links"]["full"],
content=array_.tobytes(),
Expand Down
7 changes: 7 additions & 0 deletions tiled/server/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ def shape_param(
return tuple(map(int, shape.split(",")))


def offset_param(
offset: str = Query(..., min_length=1, pattern="^[0-9]+(,[0-9]+)*$"),
):
"Specify and parse an offset parameter."
return tuple(map(int, offset.split(",")))


def np_style_slicer(indices: tuple):
return indices[0] if len(indices) == 1 else slice_func(*indices)

Expand Down
7 changes: 3 additions & 4 deletions tiled/server/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
get_query_registry,
get_serialization_registry,
get_validation_registry,
offset_param,
shape_param,
slice_,
)
Expand Down Expand Up @@ -1294,7 +1295,7 @@ async def put_array_block(
@router.patch("/array/full/{path:path}")
async def patch_array_full(
request: Request,
slice=Depends(slice_),
offset=Depends(offset_param),
shape=Depends(shape_param),
extend: bool = False,
entry=SecureEntry(
Expand All @@ -1303,8 +1304,6 @@ async def patch_array_full(
),
deserialization_registry=Depends(get_deserialization_registry),
):
if slice is None:
slice = ...
if not hasattr(entry, "patch"):
raise HTTPException(
status_code=HTTP_405_METHOD_NOT_ALLOWED,
Expand All @@ -1316,7 +1315,7 @@ async def patch_array_full(
media_type = request.headers["content-type"]
deserializer = deserialization_registry.dispatch("array", media_type)
data = await ensure_awaitable(deserializer, body, dtype, shape)
structure = await ensure_awaitable(entry.patch, data, slice, extend)
structure = await ensure_awaitable(entry.patch, data, offset, extend)
return json_or_msgpack(request, structure)


Expand Down

0 comments on commit 0f053c7

Please sign in to comment.