Skip to content

Commit 19a69ed

Browse files
committed
Improve usability. Test.
1 parent 0eed38e commit 19a69ed

File tree

6 files changed

+52
-40
lines changed

6 files changed

+52
-40
lines changed

tiled/_tests/test_writing.py

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -121,37 +121,33 @@ def test_write_array_chunked(tree):
121121
assert result.specs == specs
122122

123123

124-
def test_write_array_append(tree):
124+
def test_extend_array(tree):
125+
"Extend an array with additional data, expanding its shape."
125126
with Context.from_app(
126127
build_app(tree, validation_registry=validation_registry)
127128
) as context:
128129
client = from_context(context)
129130

130-
a = numpy.ones((1, 5, 5))
131-
new_chunk = numpy.ones((1, 5, 5)) * 2
132-
full_array = numpy.concatenate((a, new_chunk), axis=0)
133-
metadata = {"scan_id": 1, "method": "A"}
134-
specs = [Spec("SomeSpec")]
135-
136-
with record_history() as history:
137-
new_arr = client.write_array(a, metadata=metadata, specs=specs)
138-
new_arr.append_block(new_chunk, 0)
139-
# one request for metadata, one for data
140-
assert len(history.requests) == 1 + 1 + 1
141-
142-
results = client.search(Key("scan_id") == 1)
143-
result = results.values().first()
144-
result_array = result.read()
145-
assert result.shape == (2, 5, 5) # does the database have the right shape?
146-
assert result_array.shape == (
147-
2,
148-
5,
149-
5,
150-
) # does the array over the wire have the right shape?
151-
152-
numpy.testing.assert_equal(result_array, full_array)
153-
assert result.metadata == metadata
154-
assert result.specs == specs
131+
a = numpy.ones((3, 2, 2))
132+
new_data = numpy.ones((1, 2, 2)) * 2
133+
full_array = numpy.concatenate((a, new_data), axis=0)
134+
135+
# Upload a (3, 2, 2) array.
136+
ac = client.write_array(a)
137+
assert ac.shape == a.shape
138+
139+
# Patching data into a region beyond the current extent of the array
140+
# raises a ValueError (catching a 409 from the server).
141+
with pytest.raises(ValueError):
142+
ac.patch(new_data, slice=slice(3, 4))
143+
# With extend=True, the array is expanded.
144+
ac.patch(new_data, slice=slice(3, 4), extend=True)
145+
# The local cache of the structure is updated.
146+
assert ac.shape == full_array.shape
147+
actual = ac.read()
148+
# The array has the expected shape and data.
149+
assert actual.shape == full_array.shape
150+
numpy.testing.assert_equal(actual, full_array)
155151

156152

157153
def test_write_dataframe_full(tree):

tiled/adapters/zarr.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from ..structures.array import ArrayStructure
1616
from ..structures.core import Spec, StructureFamily
1717
from ..type_aliases import JSON, NDSlice
18-
from ..utils import node_repr, path_from_uri
18+
from ..utils import Conflicts, node_repr, path_from_uri
1919
from .array import ArrayAdapter, slice_and_shape_from_block_and_chunks
2020
from .protocols import AccessPolicy
2121

@@ -221,7 +221,10 @@ async def patch(
221221
# Resize the Zarr array to accommodate new data
222222
self._array.resize(new_shape_tuple)
223223
else:
224-
raise ValueError(f"Slice does not fit into array shape {current_shape}")
224+
raise Conflicts(
225+
f"Slice {slice} does not fit into array shape {current_shape}. "
226+
f"Use ?extend=true to extend array dimension to fit."
227+
)
225228
self._array[slice] = data
226229
new_chunks = []
227230
# Zarr has regularly-sized chunks, so no user input is required to

tiled/catalog/adapter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,7 @@ async def patch(self, *args, **kwargs):
10701070
data_source.structure_id = new_structure_id
10711071
db.add(data_source)
10721072
await db.commit()
1073+
return structure_dict
10731074

10741075

10751076
class CatalogAwkwardAdapter(CatalogNodeAdapter):

tiled/client/array.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import dask
44
import dask.array
5+
import httpx
56
import numpy
67
from numpy.typing import NDArray
78

9+
from ..structures.core import STRUCTURE_TYPES
810
from ..type_aliases import NDSlice
911
from .base import BaseClient
1012
from .utils import export_util, handle_error, params_from_slice
@@ -196,14 +198,23 @@ def patch(self, array: NDArray, slice: NDSlice, extend=False):
196198
params = params_from_slice(slice)
197199
params["shape"] = ",".join(map(str, array_.shape))
198200
params["extend"] = bool(extend)
199-
handle_error(
200-
self.context.http_client.patch(
201-
self.item["links"]["full"],
202-
content=array_.tobytes(),
203-
headers={"Content-Type": "application/octet-stream"},
204-
params=params,
205-
)
201+
response = self.context.http_client.patch(
202+
self.item["links"]["full"],
203+
content=array_.tobytes(),
204+
headers={"Content-Type": "application/octet-stream"},
205+
params=params,
206206
)
207+
if response.status_code == httpx.codes.CONFLICT:
208+
raise ValueError(
209+
f"Slice {slice} does not fit within current array shape. "
210+
"Pass keyword argument extend=True to extend the array "
211+
"dimensions to fit."
212+
)
213+
handle_error(response)
214+
# Update cached structure.
215+
new_structure = response.json()
216+
structure_type = STRUCTURE_TYPES[self.structure_family]
217+
self._structure = structure_type.from_json(new_structure)
207218

208219
def __getitem__(self, slice):
209220
return self.read(slice)

tiled/client/base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,10 @@ def refresh(self):
184184
)
185185
).json()
186186
self._item = content["data"]
187-
attributes = self._item["attributes"]
188-
structure_type = STRUCTURE_TYPES[attributes["structure_family"]]
189-
self._structure = structure_type.from_json(attributes["structure"])
187+
structure_type = STRUCTURE_TYPES[self.structure_family]
188+
self._structure = structure_type.from_json(
189+
self._item["attributes"]["structure"]
190+
)
190191
return self
191192

192193
@property

tiled/server/router.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,8 +1316,8 @@ async def patch_array_full(
13161316
media_type = request.headers["content-type"]
13171317
deserializer = deserialization_registry.dispatch("array", media_type)
13181318
data = await ensure_awaitable(deserializer, body, dtype, shape)
1319-
await ensure_awaitable(entry.patch, data, slice, extend)
1320-
return json_or_msgpack(request, None)
1319+
structure = await ensure_awaitable(entry.patch, data, slice, extend)
1320+
return json_or_msgpack(request, structure)
13211321

13221322

13231323
@router.put("/table/full/{path:path}")

0 commit comments

Comments
 (0)