diff --git a/CHANGELOG.md b/CHANGELOG.md index 54806611..e48cff30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix `ArrayShardedExt::inner_chunk_grid` when applied on a sharded array with the `transpose` codec preceding `sharding_indexed` + - Fix `ZipStorageAdapter` on windows ## [0.17.0-beta.0] - 2024-09-06 diff --git a/zarrs/tests/data/zarr_python_compat/zarr.zip b/zarrs/tests/data/zarr_python_compat/zarr.zip new file mode 100644 index 00000000..7574a864 Binary files /dev/null and b/zarrs/tests/data/zarr_python_compat/zarr.zip differ diff --git a/zarrs/tests/data/zarr_python_zip.py b/zarrs/tests/data/zarr_python_zip.py new file mode 100644 index 00000000..994d5c8f --- /dev/null +++ b/zarrs/tests/data/zarr_python_zip.py @@ -0,0 +1,9 @@ +import numpy as np +import zarr +print(zarr.__version__) # zarr-3.0.0a4.dev25+ge9f808b4 + +store = zarr.store.ZipStore('zarrs/tests/data/zarr_python_compat/zarr.zip', mode='w') +root = zarr.group(store=store, zarr_version=3) +z = root.create_array(shape=(100, 100), chunks=(10, 10), name="foo", dtype=np.uint8) # fill_value=42: Broken +z[:] = 42 +store.close() diff --git a/zarrs/tests/zarr_python_compat.rs b/zarrs/tests/zarr_python_compat.rs new file mode 100644 index 00000000..c0b10fdc --- /dev/null +++ b/zarrs/tests/zarr_python_compat.rs @@ -0,0 +1,20 @@ +use std::{error::Error, path::PathBuf, sync::Arc}; + +use zarrs::{array::Array, array_subset::ArraySubset}; +use zarrs_storage::{storage_adapter::zip::ZipStorageAdapter, store, StoreKey}; + +#[test] +fn zarr_python_compat_zip_store() -> Result<(), Box> { + let path = PathBuf::from("tests/data/zarr_python_compat"); + let store = Arc::new(store::FilesystemStore::new(&path)?); + let store = Arc::new(ZipStorageAdapter::new(store, StoreKey::new("zarr.zip")?)?); + + let array = Array::open(store, "/foo")?; + assert_eq!(array.shape(), vec![100, 100]); + let elements = array.retrieve_array_subset_elements::(&ArraySubset::new_with_shape( + array.shape().to_vec(), + ))?; + assert_eq!(elements, vec![42u8; 100 * 100]); + + Ok(()) +} diff --git a/zarrs_storage/src/storage_adapter/zip.rs b/zarrs_storage/src/storage_adapter/zip.rs index 8765b4db..f7bf759f 100644 --- a/zarrs_storage/src/storage_adapter/zip.rs +++ b/zarrs_storage/src/storage_adapter/zip.rs @@ -62,17 +62,25 @@ impl ZipStorageAdapter { }) } + fn key_str_to_zip_path(&self, key: &str) -> String { + let mut zip_name = self.zip_path.clone(); + zip_name.push(key); + + let mut zip_name = zip_name.to_string_lossy(); + if cfg!(windows) { + zip_name = zip_name.replace("\\", "/").into(); + } + zip_name.to_string() + } + fn get_impl( &self, key: &StoreKey, byte_ranges: &[ByteRange], ) -> Result>, StorageError> { let mut zip_archive = self.zip_archive.lock(); - let mut zip_name = self.zip_path.clone(); - zip_name.push(key.as_str()); - let mut file = { - let zip_file = zip_archive.by_name(&zip_name.to_string_lossy()); + let zip_file = zip_archive.by_name(&self.key_str_to_zip_path(key.as_str())); match zip_file { Ok(zip_file) => zip_file, Err(err) => match err { @@ -145,9 +153,7 @@ impl ListableStorageTraits .into_iter() .filter_map(|name| { if name.starts_with(prefix.as_str()) { - let mut zip_name = self.zip_path.clone(); - zip_name.push(&name); - if let Ok(file) = zip_archive.by_name(&zip_name.to_string_lossy()) { + if let Ok(file) = zip_archive.by_name(&self.key_str_to_zip_path(&name)) { if file.is_file() { let name = name.strip_suffix('/').unwrap_or(&name); if let Ok(store_key) = StoreKey::try_from(name) {