Skip to content

Commit

Permalink
implement get_partial_values
Browse files Browse the repository at this point in the history
  • Loading branch information
mpiannucci committed Sep 13, 2024
1 parent 95ac8dd commit c6d8782
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 7 deletions.
1 change: 1 addition & 0 deletions icechunk-python/python/icechunk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ async def get(
return None
except ValueError as _e:
# Zarr python expects None to be returned if the key does not exist
# but an IcechunkStore returns an error if the key does not exist
return None

return prototype.buffer.from_bytes(result)
Expand Down
23 changes: 19 additions & 4 deletions icechunk-python/tests/test_zarr/test_store/test_icechunk_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async def get(self, store: IcechunkStore, key: str) -> Buffer:
return None
except ValueError as _e:
# Zarr python expects None to be returned if the key does not exist
# but an IcechunkStore returns an error if the key does not exist
return None

return self.buffer_cls.from_bytes(result)
Expand Down Expand Up @@ -129,13 +130,25 @@ async def test_delete(self, store: IcechunkStore) -> None:
await store.delete("foo/zarr.json")
assert not await store.exists("foo/zarr.json")

@pytest.mark.xfail(reason="Invalid usage pattern with Icechunk")
async def test_get_partial_values(
self,
store: IcechunkStore,
key_ranges: list[tuple[str, tuple[int | None, int | None]]],
) -> None:
await super().test_get_partial_values(store, key_ranges)
await self.set(
store, "zarr.json", self.buffer_cls.from_bytes(DEFAULT_GROUP_METADATA)
)
# read back just part of it
values = await store.get_partial_values(
default_buffer_prototype(),
[
("zarr.json", (0, 5)),
],
)

assert len(values) == 1
data = values[0].to_bytes()
assert len(data) == 5
assert data == DEFAULT_GROUP_METADATA[:5]

async def test_set(self, store: IcechunkStore) -> None:
await store.set("zarr.json", self.buffer_cls.from_bytes(DEFAULT_GROUP_METADATA))
Expand All @@ -144,7 +157,9 @@ async def test_set(self, store: IcechunkStore) -> None:
assert result.to_bytes() == DEFAULT_GROUP_METADATA

async def test_get(self, store: IcechunkStore) -> None:
await self.set(store, "zarr.json", self.buffer_cls.from_bytes(DEFAULT_GROUP_METADATA))
await self.set(
store, "zarr.json", self.buffer_cls.from_bytes(DEFAULT_GROUP_METADATA)
)
assert await store.exists("zarr.json")
result = await store.get("zarr.json", default_buffer_prototype())
assert result.to_bytes() == DEFAULT_GROUP_METADATA
16 changes: 13 additions & 3 deletions icechunk/src/zarr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,23 @@ impl Store {
}

// TODO: prototype argument
pub async fn get(&self, key: &str, _byte_range: &ByteRange) -> StoreResult<Bytes> {
match Key::parse(key)? {
pub async fn get(&self, key: &str, byte_range: &ByteRange) -> StoreResult<Bytes> {
let bytes = match Key::parse(key)? {
Key::Metadata { node_path } => self.get_metadata(key, &node_path).await,
Key::Chunk { node_path, coords } => {
self.get_chunk(key, node_path, coords).await
}
}
}?;

let (start, end) = byte_range;
let bytes = match (start, end) {
(Some(start), Some(end)) => bytes.slice(*start as usize..*end as usize),
(Some(start), None) => bytes.slice(*start as usize..),
(None, Some(end)) => bytes.slice(..*end as usize),
(None, None) => bytes,
};

Ok(bytes)
}

/// Get all the requested keys concurrently.
Expand Down

0 comments on commit c6d8782

Please sign in to comment.