Skip to content

Commit e101af0

Browse files
authored
V2 groups (#25)
* remove zarrita * setup uv in ZarrPythonTests automatically * add core.Node * implement and test v2 Group with metadata handling * fix inheritance of v3.codec.Codec * add v2 sample * add static open function that auto-detects the Zarr version * overload open and create for Path and String arguments * add API tests for open, create and refactor tests structure * fix windows paths in tests * cleanup create api * finish merge update dependency versions * Refactor ZarrV2 and ZarrV3 tests to improve readability and structure; add new tests for array and group creation * detect typesize for blosc with evolve_from_core_array_metadata * fix BytesCodec for boolean * add testdata for bool * zarr-python tests for datatypes * fix BytesCodec encode for bool, float, double * make metadata() public but field metadata private * camelCase for evolveFromCoreArrayMetadata
1 parent ac4ecb1 commit e101af0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2131
-1038
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ jobs:
3636
- name: Install uv
3737
uses: astral-sh/setup-uv@v6
3838

39-
- name: Set up zarr-python
40-
run: |
41-
uv venv && uv init
42-
uv add zarr
43-
uv add zarrita
44-
4539
- name: Download testdata
4640
run: |
4741
mkdir testoutput

.github/workflows/deploy.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ jobs:
4343
run: |
4444
uv venv && uv init
4545
uv add zarr
46-
uv add zarrita
4746
4847
- name: Download testdata
4948
run: |

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,7 @@ array.write(
4343
## Development Start-Guide
4444

4545
### Run Tests Locally
46-
To be able to run the tests locally, make sure to have `python3.11` installed.
47-
Also, you need to set up a venv for zarrita at the root of the project:
48-
`python3.11 -m venv venv_zarrita`.
49-
50-
Then install zarrita there with `venv_zarrita/Scripts/pip install zarrita`
51-
for Windows and `venv_zarrita/bin/pip install zarrita` for Linux.
46+
To be able to run the tests locally, make sure to have `python3.11` and `uv` installed.
5247

5348
Furthermore, you will need the `l4_sample` test data:
5449

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package dev.zarr.zarrjava.core;
2+
3+
import dev.zarr.zarrjava.store.StoreHandle;
4+
5+
import javax.annotation.Nonnull;
6+
7+
public class AbstractNode implements Node {
8+
9+
@Nonnull
10+
public final StoreHandle storeHandle;
11+
12+
protected AbstractNode(@Nonnull StoreHandle storeHandle) {
13+
this.storeHandle = storeHandle;
14+
}
15+
}

src/main/java/dev/zarr/zarrjava/core/Array.java

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.zarr.zarrjava.core;
22

33
import dev.zarr.zarrjava.ZarrException;
4+
import dev.zarr.zarrjava.store.FilesystemStore;
45
import dev.zarr.zarrjava.store.StoreHandle;
56
import dev.zarr.zarrjava.utils.IndexingUtils;
67
import dev.zarr.zarrjava.utils.MultiArrayUtils;
@@ -10,27 +11,73 @@
1011

1112
import javax.annotation.Nonnull;
1213
import javax.annotation.Nullable;
14+
import java.io.IOException;
1315
import java.nio.ByteBuffer;
16+
import java.nio.file.Path;
17+
import java.nio.file.Paths;
1418
import java.util.Arrays;
1519
import java.util.stream.Stream;
1620

17-
public interface Array {
21+
public abstract class Array extends AbstractNode {
1822

19-
ArrayMetadata metadata();
20-
21-
StoreHandle storeHandle();
23+
protected CodecPipeline codecPipeline;
24+
public abstract ArrayMetadata metadata();
2225

23-
CodecPipeline codecPipeline();
26+
protected Array(StoreHandle storeHandle) throws ZarrException {
27+
super(storeHandle);
28+
}
29+
30+
/**
31+
* Opens an existing Zarr array at a specified storage location. Automatically detects the Zarr version.
32+
*
33+
* @param storeHandle the storage location of the Zarr array
34+
* @throws IOException throws IOException if the metadata cannot be read
35+
* @throws ZarrException throws ZarrException if the Zarr array cannot be opened
36+
*/
37+
public static Array open(StoreHandle storeHandle) throws IOException, ZarrException {
38+
boolean isV3 = storeHandle.resolve(ZARR_JSON).exists();
39+
boolean isV2 = storeHandle.resolve(ZARRAY).exists();
40+
if (isV3 && isV2) {
41+
throw new ZarrException("Both Zarr v2 and v3 arrays found at the specified location.");
42+
} else if (isV3) {
43+
return dev.zarr.zarrjava.v3.Array.open(storeHandle);
44+
} else if (isV2) {
45+
return dev.zarr.zarrjava.v2.Array.open(storeHandle);
46+
} else {
47+
throw new ZarrException("No Zarr array found at the specified location.");
48+
}
49+
}
50+
/**
51+
* Opens an existing Zarr array at a specified storage location. Automatically detects the Zarr version.
52+
*
53+
* @param path the storage location of the Zarr array
54+
* @throws IOException throws IOException if the metadata cannot be read
55+
* @throws ZarrException throws ZarrException if the Zarr array cannot be opened
56+
*/
57+
public static Array open(Path path) throws IOException, ZarrException {
58+
return open(new StoreHandle(new FilesystemStore(path)));
59+
}
60+
61+
/**
62+
* Opens an existing Zarr array at a specified storage location. Automatically detects the Zarr version.
63+
*
64+
* @param path the storage location of the Zarr array
65+
* @throws IOException throws IOException if the metadata cannot be read
66+
* @throws ZarrException throws ZarrException if the Zarr array cannot be opened
67+
*/
68+
public static Array open(String path) throws IOException, ZarrException {
69+
return open(Paths.get(path));
70+
}
2471

2572
/**
2673
* Writes a ucar.ma2.Array into the Zarr array at a specified offset. The shape of the Zarr array
2774
* needs be large enough for the write.
2875
*
29-
* @param offset the offset where to write the data
30-
* @param array the data to write
76+
* @param offset the offset where to write the data
77+
* @param array the data to write
3178
* @param parallel utilizes parallelism if true
3279
*/
33-
default void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
80+
public void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
3481
ArrayMetadata metadata = metadata();
3582
if (offset.length != metadata.ndim()) {
3683
throw new IllegalArgumentException("'offset' needs to have rank '" + metadata.ndim() + "'.");
@@ -42,15 +89,15 @@ default void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
4289
int[] shape = array.getShape();
4390

4491
final int[] chunkShape = metadata.chunkShape();
45-
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape(), chunkShape, offset, shape));
92+
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape, chunkShape, offset, shape));
4693
if (parallel) {
4794
chunkStream = chunkStream.parallel();
4895
}
4996
chunkStream.forEach(
5097
chunkCoords -> {
5198
try {
5299
final IndexingUtils.ChunkProjection chunkProjection =
53-
IndexingUtils.computeProjection(chunkCoords, metadata.shape(), chunkShape, offset,
100+
IndexingUtils.computeProjection(chunkCoords, metadata.shape, chunkShape, offset,
54101
shape
55102
);
56103

@@ -82,19 +129,19 @@ default void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
82129
*
83130
* @param chunkCoords The coordinates of the chunk as computed by the offset of the chunk divided
84131
* by the chunk shape.
85-
* @param chunkArray The data to write into the chunk
132+
* @param chunkArray The data to write into the chunk
86133
* @throws ZarrException throws ZarrException if the write fails
87134
*/
88-
default void writeChunk(long[] chunkCoords, ucar.ma2.Array chunkArray) throws ZarrException {
135+
public void writeChunk(long[] chunkCoords, ucar.ma2.Array chunkArray) throws ZarrException {
89136
ArrayMetadata metadata = metadata();
90137
String[] chunkKeys = metadata.chunkKeyEncoding().encodeChunkKey(chunkCoords);
91-
StoreHandle chunkHandle = storeHandle().resolve(chunkKeys);
138+
StoreHandle chunkHandle = storeHandle.resolve(chunkKeys);
92139
Object parsedFillValue = metadata.parsedFillValue();
93140

94141
if (parsedFillValue != null && MultiArrayUtils.allValuesEqual(chunkArray, parsedFillValue)) {
95142
chunkHandle.delete();
96143
} else {
97-
ByteBuffer chunkBytes = codecPipeline().encode(chunkArray);
144+
ByteBuffer chunkBytes = codecPipeline.encode(chunkArray);
98145
chunkHandle.set(chunkBytes);
99146
}
100147
}
@@ -108,22 +155,21 @@ default void writeChunk(long[] chunkCoords, ucar.ma2.Array chunkArray) throws Za
108155
* @throws ZarrException throws ZarrException if the requested chunk is outside the array's domain or if the read fails
109156
*/
110157
@Nonnull
111-
default ucar.ma2.Array readChunk(long[] chunkCoords)
112-
throws ZarrException {
158+
public ucar.ma2.Array readChunk(long[] chunkCoords) throws ZarrException {
113159
ArrayMetadata metadata = metadata();
114160
if (!chunkIsInArray(chunkCoords)) {
115161
throw new ZarrException("Attempting to read data outside of the array's domain.");
116162
}
117163

118164
final String[] chunkKeys = metadata.chunkKeyEncoding().encodeChunkKey(chunkCoords);
119-
final StoreHandle chunkHandle = storeHandle().resolve(chunkKeys);
165+
final StoreHandle chunkHandle = storeHandle.resolve(chunkKeys);
120166

121167
ByteBuffer chunkBytes = chunkHandle.read();
122168
if (chunkBytes == null) {
123169
return metadata.allocateFillValueChunk();
124170
}
125171

126-
return codecPipeline().decode(chunkBytes);
172+
return codecPipeline.decode(chunkBytes);
127173
}
128174

129175

@@ -134,7 +180,7 @@ default ucar.ma2.Array readChunk(long[] chunkCoords)
134180
*
135181
* @param array the data to write
136182
*/
137-
default void write(ucar.ma2.Array array) {
183+
public void write(ucar.ma2.Array array) {
138184
write(new long[metadata().ndim()], array);
139185
}
140186

@@ -144,20 +190,20 @@ default void write(ucar.ma2.Array array) {
144190
* Utilizes no parallelism.
145191
*
146192
* @param offset the offset where to write the data
147-
* @param array the data to write
193+
* @param array the data to write
148194
*/
149-
default void write(long[] offset, ucar.ma2.Array array) {
195+
public void write(long[] offset, ucar.ma2.Array array) {
150196
write(offset, array, false);
151197
}
152198

153199
/**
154200
* Writes a ucar.ma2.Array into the Zarr array at the beginning of the Zarr array. The shape of
155201
* the Zarr array needs be large enough for the write.
156202
*
157-
* @param array the data to write
203+
* @param array the data to write
158204
* @param parallel utilizes parallelism if true
159205
*/
160-
default void write(ucar.ma2.Array array, boolean parallel) {
206+
public void write(ucar.ma2.Array array, boolean parallel) {
161207
write(new long[metadata().ndim()], array, parallel);
162208
}
163209

@@ -168,20 +214,20 @@ default void write(ucar.ma2.Array array, boolean parallel) {
168214
* @throws ZarrException throws ZarrException if the read fails
169215
*/
170216
@Nonnull
171-
default ucar.ma2.Array read() throws ZarrException {
172-
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape()));
217+
public ucar.ma2.Array read() throws ZarrException {
218+
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape));
173219
}
174220

175221
/**
176222
* Reads a part of the Zarr array based on a requested offset and shape into an ucar.ma2.Array.
177223
* Utilizes no parallelism.
178224
*
179225
* @param offset the offset where to start reading
180-
* @param shape the shape of the data to read
226+
* @param shape the shape of the data to read
181227
* @throws ZarrException throws ZarrException if the requested data is outside the array's domain or if the read fails
182228
*/
183229
@Nonnull
184-
default ucar.ma2.Array read(final long[] offset, final int[] shape) throws ZarrException {
230+
public ucar.ma2.Array read(final long[] offset, final int[] shape) throws ZarrException {
185231
return read(offset, shape, false);
186232
}
187233

@@ -192,15 +238,15 @@ default ucar.ma2.Array read(final long[] offset, final int[] shape) throws ZarrE
192238
* @throws ZarrException throws ZarrException if the requested data is outside the array's domain or if the read fails
193239
*/
194240
@Nonnull
195-
default ucar.ma2.Array read(final boolean parallel) throws ZarrException {
196-
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape()), parallel);
241+
public ucar.ma2.Array read(final boolean parallel) throws ZarrException {
242+
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape), parallel);
197243
}
198244

199-
default boolean chunkIsInArray(long[] chunkCoords) {
245+
boolean chunkIsInArray(long[] chunkCoords) {
200246
final int[] chunkShape = metadata().chunkShape();
201247
for (int dimIdx = 0; dimIdx < metadata().ndim(); dimIdx++) {
202248
if (chunkCoords[dimIdx] < 0
203-
|| chunkCoords[dimIdx] * chunkShape[dimIdx] >= metadata().shape()[dimIdx]) {
249+
|| chunkCoords[dimIdx] * chunkShape[dimIdx] >= metadata().shape[dimIdx]) {
204250
return false;
205251
}
206252
}
@@ -210,23 +256,22 @@ default boolean chunkIsInArray(long[] chunkCoords) {
210256
/**
211257
* Reads a part of the Zarr array based on a requested offset and shape into an ucar.ma2.Array.
212258
*
213-
* @param offset the offset where to start reading
214-
* @param shape the shape of the data to read
259+
* @param offset the offset where to start reading
260+
* @param shape the shape of the data to read
215261
* @param parallel utilizes parallelism if true
216262
* @throws ZarrException throws ZarrException if the requested data is outside the array's domain or if the read fails
217263
*/
218264
@Nonnull
219-
default ucar.ma2.Array read(final long[] offset, final int[] shape, final boolean parallel) throws ZarrException {
265+
public ucar.ma2.Array read(final long[] offset, final int[] shape, final boolean parallel) throws ZarrException {
220266
ArrayMetadata metadata = metadata();
221-
CodecPipeline codecPipeline = codecPipeline();
222267
if (offset.length != metadata.ndim()) {
223268
throw new IllegalArgumentException("'offset' needs to have rank '" + metadata.ndim() + "'.");
224269
}
225270
if (shape.length != metadata.ndim()) {
226271
throw new IllegalArgumentException("'shape' needs to have rank '" + metadata.ndim() + "'.");
227272
}
228273
for (int dimIdx = 0; dimIdx < metadata.ndim(); dimIdx++) {
229-
if (offset[dimIdx] < 0 || offset[dimIdx] + shape[dimIdx] > metadata.shape()[dimIdx]) {
274+
if (offset[dimIdx] < 0 || offset[dimIdx] + shape[dimIdx] > metadata.shape[dimIdx]) {
230275
throw new ZarrException("Requested data is outside of the array's domain.");
231276
}
232277
}
@@ -238,15 +283,15 @@ default ucar.ma2.Array read(final long[] offset, final int[] shape, final boolea
238283

239284
final ucar.ma2.Array outputArray = ucar.ma2.Array.factory(metadata.dataType().getMA2DataType(),
240285
shape);
241-
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape(), chunkShape, offset, shape));
286+
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape, chunkShape, offset, shape));
242287
if (parallel) {
243288
chunkStream = chunkStream.parallel();
244289
}
245290
chunkStream.forEach(
246291
chunkCoords -> {
247292
try {
248293
final IndexingUtils.ChunkProjection chunkProjection =
249-
IndexingUtils.computeProjection(chunkCoords, metadata.shape(), chunkShape, offset,
294+
IndexingUtils.computeProjection(chunkCoords, metadata.shape, chunkShape, offset,
250295
shape
251296
);
252297

@@ -258,7 +303,7 @@ default ucar.ma2.Array read(final long[] offset, final int[] shape, final boolea
258303
}
259304

260305
final String[] chunkKeys = metadata.chunkKeyEncoding().encodeChunkKey(chunkCoords);
261-
final StoreHandle chunkHandle = storeHandle().resolve(chunkKeys);
306+
final StoreHandle chunkHandle = storeHandle.resolve(chunkKeys);
262307
if (!chunkHandle.exists()) {
263308
return;
264309
}
@@ -281,11 +326,11 @@ default ucar.ma2.Array read(final long[] offset, final int[] shape, final boolea
281326
return outputArray;
282327
}
283328

284-
default ArrayAccessor access() {
329+
public ArrayAccessor access() {
285330
return new ArrayAccessor(this);
286331
}
287332

288-
final class ArrayAccessor {
333+
public static final class ArrayAccessor {
289334
@Nullable
290335
long[] offset;
291336
@Nullable
@@ -335,4 +380,4 @@ public void write(@Nonnull ucar.ma2.Array content) throws ZarrException {
335380
}
336381

337382
}
338-
}
383+
}

0 commit comments

Comments
 (0)