Skip to content

Commit 2459624

Browse files
Extendable zarr arrays (#802)
* add parameter to from_uri * growable zarr array * pass max_depth to server * revert max_shaape * checkpoint * checkpoint * Add endpoint link for append * fix errant code in write_block * Add append link to arrays, client sends shape and axis * zarr adapter is called for write_block * fix growth of zarr array * update database for size in array append_block * precommit cleanup * fix test * fix database issue * add client call docstring * WIP: Move type_aliases to root and fix misspelled name * WIP: Rework to use PATCH /array/full * Appending works * Rename 'grow' to 'extend'. * Raise if assumptions are not met * Improve usability. Test. * Update imports after rebase. * Use Python 3.9 compatibile typing. * Fix regression in refresh * Reference documentation * Add example to docstring. * Test overwrite and out-of-order updates. * Change from 'slice' to 'offset'. * update array test * add extend array to writing tutorial * whitespace * Finesse docs * Data type of patch must match. * Update CHANGELOG --------- Co-authored-by: Dan Allan <[email protected]>
1 parent a5fd789 commit 2459624

29 files changed

+345
-52
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ Write the date in place of the "Unreleased" in the case a new version is release
33

44
# Changelog
55

6+
## Unreleased
7+
8+
### Added
9+
10+
- Add HTTP endpoint `PATCH /array/full/{path}` to enable updating and
11+
optionally _extending_ an existing array.
12+
- Add associated Python client method `ArrayClient.patch`.
13+
614
## v0.1.0b11 (2024-11-14)
715

816
### Added

docs/source/reference/python-client.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ Tiled currently includes two clients for each structure family:
165165
tiled.client.array.DaskArrayClient.export
166166
tiled.client.array.DaskArrayClient.write
167167
tiled.client.array.DaskArrayClient.write_block
168+
tiled.client.array.DaskArrayClient.patch
168169
```
169170

170171
```{eval-rst}

docs/source/tutorials/writing.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
# Writing Data
22

3-
```{warning}
4-
5-
This is a highly experimental feature, recently introduced and included for
6-
evaluation by early users. At this time we do not recommend using it for
7-
anything important.
8-
```
9-
103
In this tutorial we will start Tiled in a mode where the client can
114
write (upload) data for later retrieving, search, or sharing.
125

@@ -48,21 +41,38 @@ where `...` will be whatever secret was printed at server startup above.
4841

4942
## Write data
5043

51-
Write array and tabular data.
44+
Write array.
5245

5346
```python
54-
# Write simple Python list (which gets converted to numpy.array).
55-
>>> client.write_array([1, 2, 3], metadata={"color": "red", "barcode": 10})
56-
<ArrayClient shape=(3,) chunks=((3,),) dtype=int64>
57-
5847
# Write an array.
5948
>>> import numpy
6049
>>> client.write_array(numpy.array([4, 5, 6]), metadata={"color": "blue", "barcode": 11})
6150
<ArrayClient shape=(3,) chunks=((3,),) dtype=int64>
6251

63-
# Write a table (DataFrame).
52+
# Write a Python list (which gets converted to numpy array).
53+
>>> client.write_array([1, 2, 3], metadata={"color": "red", "barcode": 10})
54+
<ArrayClient shape=(3,) chunks=((3,),) dtype=int64>
55+
56+
# Create an array and grow it by one.
57+
>>> new_array = client.write_array([1, 2, 3])
58+
>>> new_array
59+
<ArrayClient shape=(3,) chunks=((3,),) dtype=int64>
60+
61+
# Extend the array. This array has only one dimension, here we extend by one
62+
# along that dimension.
63+
>>> new_array.patch([4], offset=(3,), extend=True)
64+
>>> new_array
65+
<ArrayClient shape=(4,) chunks=((3, 1),) dtype=int64>
66+
>>> new_array.read()
67+
array([1, 2, 3, 4])
68+
```
69+
70+
Write tabular data in a pandas DataFrame.
71+
72+
```python
6473
>>> import pandas
65-
>>> client.write_dataframe(pandas.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}), metadata={"color": "green", "barcode": 12})
74+
>>> df = pandas.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
75+
>>> client.write_dataframe(df, metadata={"color": "green", "barcode": 12})
6676
<DataFrameClient ['x', 'y']>
6777
```
6878

tiled/_tests/test_protocols.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
SparseAdapter,
1919
TableAdapter,
2020
)
21-
from tiled.adapters.type_alliases import JSON, Filters, NDSlice, Scopes
2221
from tiled.server.schemas import Principal, PrincipalType
2322
from tiled.structures.array import ArrayStructure, BuiltinDtype
2423
from tiled.structures.awkward import AwkwardStructure
2524
from tiled.structures.core import Spec, StructureFamily
2625
from tiled.structures.sparse import COOStructure
2726
from tiled.structures.table import TableStructure
27+
from tiled.type_aliases import JSON, Filters, NDSlice, Scopes
2828

2929

3030
class CustomArrayAdapter:

tiled/_tests/test_writing.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,59 @@ def test_write_array_chunked(tree):
121121
assert result.specs == specs
122122

123123

124+
def test_extend_array(tree):
125+
"Extend an array with additional data, expanding its shape."
126+
with Context.from_app(
127+
build_app(tree, validation_registry=validation_registry)
128+
) as context:
129+
client = from_context(context)
130+
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, offset=(3,))
143+
# With extend=True, the array is expanded.
144+
ac.patch(new_data, offset=(3,), 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)
151+
152+
# Overwrite data (do not extend).
153+
revised_data = numpy.ones((1, 2, 2)) * 3
154+
revised_array = full_array.copy()
155+
revised_array[3, :, :] = 3
156+
ac.patch(revised_data, offset=(3,))
157+
numpy.testing.assert_equal(ac.read(), revised_array)
158+
159+
# Extend out of order.
160+
ones = numpy.ones((1, 2, 2))
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)
164+
numpy.testing.assert_equal(ac[5:6], ones * 5)
165+
numpy.testing.assert_equal(ac[6:7], ones * 6)
166+
numpy.testing.assert_equal(ac[7:8], ones * 7)
167+
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+
172+
# Data type must match.
173+
with pytest.raises(ValueError):
174+
ac.patch(ones.astype("uint8"), offset=9, extend=True)
175+
176+
124177
def test_write_dataframe_full(tree):
125178
with Context.from_app(
126179
build_app(tree, validation_registry=validation_registry)

tiled/adapters/array.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from ..structures.array import ArrayStructure
77
from ..structures.core import Spec, StructureFamily
8+
from ..type_aliases import JSON, NDSlice
89
from .protocols import AccessPolicy
9-
from .type_alliases import JSON, NDSlice
1010

1111

1212
class ArrayAdapter:

tiled/adapters/arrow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
from ..structures.core import Spec, StructureFamily
1010
from ..structures.data_source import Asset, DataSource, Management
1111
from ..structures.table import TableStructure
12+
from ..type_aliases import JSON
1213
from ..utils import ensure_uri, path_from_uri
1314
from .array import ArrayAdapter
1415
from .protocols import AccessPolicy
15-
from .type_alliases import JSON
1616

1717

1818
class ArrowAdapter:

tiled/adapters/awkward.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
from ..structures.awkward import AwkwardStructure
88
from ..structures.core import Spec, StructureFamily
9+
from ..type_aliases import JSON
910
from .awkward_directory_container import DirectoryContainer
1011
from .protocols import AccessPolicy
11-
from .type_alliases import JSON
1212

1313

1414
class AwkwardAdapter:

tiled/adapters/awkward_buffers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
from ..server.schemas import Asset
1010
from ..structures.awkward import AwkwardStructure
1111
from ..structures.core import Spec, StructureFamily
12+
from ..type_aliases import JSON
1213
from ..utils import path_from_uri
1314
from .awkward import AwkwardAdapter
1415
from .awkward_directory_container import DirectoryContainer
1516
from .protocols import AccessPolicy
16-
from .type_alliases import JSON
1717

1818

1919
class AwkwardBuffersAdapter(AwkwardAdapter):

tiled/adapters/csv.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
from ..structures.core import Spec, StructureFamily
88
from ..structures.data_source import Asset, DataSource, Management
99
from ..structures.table import TableStructure
10+
from ..type_aliases import JSON
1011
from ..utils import ensure_uri, path_from_uri
1112
from .array import ArrayAdapter
1213
from .dataframe import DataFrameAdapter
1314
from .protocols import AccessPolicy
1415
from .table import TableAdapter
15-
from .type_alliases import JSON
1616

1717

1818
def read_csv(

0 commit comments

Comments
 (0)