From 1ad5cef97e53ccf55117e1388a713d38f6a271ef Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 27 Dec 2025 00:57:45 +1100 Subject: [PATCH 1/7] chore: bump `zarrs` to 0.23.0-beta.1 --- Cargo.toml | 15 +++++-- src/chunk_item.rs | 75 +++++++++++++++++++------------ src/concurrency.rs | 7 +-- src/lib.rs | 110 ++++++++++++++++++++++++--------------------- src/utils.rs | 2 +- 5 files changed, 120 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f3afd94..be5e594 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] pyo3 = { version = "0.27.1", features = ["abi3-py311"] } -zarrs = { version = "0.22.4", features = ["async", "zlib", "pcodec", "bz2"] } +zarrs = { version = "0.23.0-beta.1", features = ["async", "zlib", "pcodec", "bz2"] } rayon_iter_concurrent_limit = "0.2.0" rayon = "1.10.0" # fix for https://stackoverflow.com/questions/76593417/package-openssl-was-not-found-in-the-pkg-config-search-path @@ -19,10 +19,19 @@ numpy = "0.27.0" unsafe_cell_slice = "0.2.0" serde_json = "1.0.128" pyo3-stub-gen = "0.17.1" -opendal = { version = "0.54.0", features = ["services-http"] } +opendal = { version = "0.55.0", features = ["services-http"] } tokio = { version = "1.41.1", features = ["rt-multi-thread"] } -zarrs_opendal = "0.9.0" +zarrs_opendal = "0.10.0" itertools = "0.14.0" +bytemuck = { version = "1.24.0", features = ["must_cast"] } [profile.release] lto = true + +[patch.crates-io] +# zarrs = { path = "../zarrs/zarrs" } +# zarrs_storage = { path = "../zarrs/zarrs_storage" } +# zarrs_filesystem = { path = "../zarrs/zarrs_filesystem" } +# zarrs = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } +# zarrs_storage = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } +# zarrs_filesystem = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } diff --git a/src/chunk_item.rs b/src/chunk_item.rs index 643e362..e57ff0b 100644 --- a/src/chunk_item.rs +++ b/src/chunk_item.rs @@ -8,7 +8,7 @@ use pyo3::{ }; use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods}; use zarrs::{ - array::{ChunkRepresentation, DataType, FillValue}, + array::{ChunkShape, DataType, FillValue, NamedDataType}, array_subset::ArraySubset, metadata::v3::MetadataV3, storage::StoreKey, @@ -18,7 +18,9 @@ use crate::utils::PyErrExt; pub(crate) trait ChunksItem { fn key(&self) -> &StoreKey; - fn representation(&self) -> &ChunkRepresentation; + fn shape(&self) -> &[NonZeroU64]; + fn data_type(&self) -> &DataType; + fn fill_value(&self) -> &FillValue; } #[derive(Clone)] @@ -26,7 +28,9 @@ pub(crate) trait ChunksItem { #[pyclass] pub(crate) struct Basic { key: StoreKey, - representation: ChunkRepresentation, + shape: ChunkShape, + data_type: DataType, + fill_value: FillValue, } fn fill_value_to_bytes(dtype: &str, fill_value: &Bound<'_, PyAny>) -> PyResult> { @@ -62,7 +66,8 @@ impl Basic { fn new(byte_interface: &Bound<'_, PyAny>, chunk_spec: &Bound<'_, PyAny>) -> PyResult { let path: String = byte_interface.getattr("path")?.extract()?; - let chunk_shape = chunk_spec.getattr("shape")?.extract()?; + let shape: Vec = chunk_spec.getattr("shape")?.extract()?; + let mut dtype: String = chunk_spec .getattr("dtype")? .call_method0("to_native_dtype")? @@ -73,11 +78,14 @@ impl Basic { // but maps it to "string" internally https://github.com/LDeakin/zarrs/blob/0532fe983b7b42b59dbf84e50a2fe5e6f7bad4ce/zarrs_metadata/src/v2_to_v3.rs#L288 dtype = String::from("string"); } + let data_type = get_data_type_from_dtype(&dtype)?; let fill_value: Bound<'_, PyAny> = chunk_spec.getattr("fill_value")?; - let fill_value_bytes = fill_value_to_bytes(&dtype, &fill_value)?; + let fill_value = FillValue::new(fill_value_to_bytes(&dtype, &fill_value)?); Ok(Self { key: StoreKey::new(path).map_py_err::()?, - representation: get_chunk_representation(chunk_shape, &dtype, fill_value_bytes)?, + shape, + data_type, + fill_value, }) } } @@ -102,8 +110,17 @@ impl WithSubset { subset: Vec>, shape: Vec, ) -> PyResult { - let chunk_subset = - selection_to_array_subset(&chunk_subset, &item.representation.shape_u64())?; + let chunk_subset = selection_to_array_subset(&chunk_subset, &item.shape)?; + let shape: Vec = shape + .into_iter() + .map(|dim| { + NonZeroU64::new(dim).ok_or_else(|| { + PyErr::new::( + "subset dimensions must be greater than zero".to_string(), + ) + }) + }) + .collect::>>()?; let subset = selection_to_array_subset(&subset, &shape)?; // Check that subset and chunk_subset have the same number of elements. // This permits broadcasting of a constant input. @@ -124,8 +141,14 @@ impl ChunksItem for Basic { fn key(&self) -> &StoreKey { &self.key } - fn representation(&self) -> &ChunkRepresentation { - &self.representation + fn shape(&self) -> &[NonZeroU64] { + &self.shape + } + fn data_type(&self) -> &DataType { + &self.data_type + } + fn fill_value(&self) -> &FillValue { + &self.fill_value } } @@ -133,30 +156,24 @@ impl ChunksItem for WithSubset { fn key(&self) -> &StoreKey { &self.item.key } - fn representation(&self) -> &ChunkRepresentation { - &self.item.representation + fn shape(&self) -> &[NonZeroU64] { + &self.item.shape + } + fn data_type(&self) -> &DataType { + &self.item.data_type + } + fn fill_value(&self) -> &FillValue { + &self.item.fill_value } } -fn get_chunk_representation( - chunk_shape: Vec, - dtype: &str, - fill_value: Vec, -) -> PyResult { - // Get the chunk representation - let data_type = DataType::from_metadata( +fn get_data_type_from_dtype(dtype: &str) -> PyResult { + let data_type = NamedDataType::from_metadata( &MetadataV3::new(dtype), zarrs::config::global_config().data_type_aliases_v3(), ) .map_py_err::()?; - let chunk_shape = chunk_shape - .into_iter() - .map(|x| NonZeroU64::new(x).expect("chunk shapes should always be non-zero")) - .collect(); - let chunk_representation = - ChunkRepresentation::new(chunk_shape, data_type, FillValue::new(fill_value)) - .map_py_err::()?; - Ok(chunk_representation) + Ok(data_type.into()) } fn slice_to_range(slice: &Bound<'_, PySlice>, length: isize) -> PyResult> { @@ -180,7 +197,7 @@ fn slice_to_range(slice: &Bound<'_, PySlice>, length: isize) -> PyResult], - shape: &[u64], + shape: &[NonZeroU64], ) -> PyResult { if selection.is_empty() { Ok(ArraySubset::new_with_shape(vec![1; shape.len()])) @@ -188,7 +205,7 @@ fn selection_to_array_subset( let chunk_ranges = selection .iter() .zip(shape) - .map(|(selection, &shape)| slice_to_range(selection, isize::try_from(shape)?)) + .map(|(selection, &shape)| slice_to_range(selection, isize::try_from(shape.get())?)) .collect::>>()?; Ok(ArraySubset::new_with_ranges(&chunk_ranges)) } diff --git a/src/concurrency.rs b/src/concurrency.rs index 364b33b..4cde33a 100644 --- a/src/concurrency.rs +++ b/src/concurrency.rs @@ -25,11 +25,10 @@ where let Some(chunk_descriptions0) = self.first() else { return Ok(None); }; - let chunk_representation = chunk_descriptions0.representation(); let codec_concurrency = codec_pipeline_impl .codec_chain - .recommended_concurrency(chunk_representation) + .recommended_concurrency(chunk_descriptions0.shape(), chunk_descriptions0.data_type()) .map_codec_err()?; let min_concurrent_chunks = @@ -43,9 +42,7 @@ where ); let codec_options = codec_pipeline_impl .codec_options - .into_builder() - .concurrent_target(codec_concurrent_limit) - .build(); + .with_concurrent_target(codec_concurrent_limit); Ok(Some((chunk_concurrent_limit, codec_options))) } } diff --git a/src/lib.rs b/src/lib.rs index ccf6bd8..20e2e3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,13 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_iter_concurrent_limit::iter_concurrent_limit; use unsafe_cell_slice::UnsafeCellSlice; use utils::is_whole_chunk; +use zarrs::array::codec::ArrayBytesDecodeIntoTarget; use zarrs::array::codec::{ - ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, CodecOptions, CodecOptionsBuilder, - StoragePartialDecoder, + ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, CodecOptions, StoragePartialDecoder, }; use zarrs::array::{ - ArrayBytes, ArrayBytesFixedDisjointView, ArrayMetadata, ArraySize, CodecChain, FillValue, - copy_fill_value_into, update_array_bytes, + ArrayBytes, ArrayBytesFixedDisjointView, ArrayMetadata, ChunkShapeTraits, CodecChain, + FillValue, copy_fill_value_into, update_array_bytes, }; use zarrs::array_subset::ArraySubset; use zarrs::config::global_config; @@ -71,14 +71,21 @@ impl CodecPipelineImpl { let value_decoded = if let Some(value_encoded) = value_encoded { let value_encoded: Vec = value_encoded.into(); // zero-copy in this case codec_chain - .decode(value_encoded.into(), item.representation(), codec_options) + .decode( + value_encoded.into(), + item.shape(), + item.data_type(), + item.fill_value(), + codec_options, + ) .map_codec_err()? } else { - let array_size = ArraySize::new( - item.representation().data_type().size(), - item.representation().num_elements(), - ); - ArrayBytes::new_fill_value(array_size, item.representation().fill_value()) + ArrayBytes::new_fill_value( + item.data_type(), + item.shape().num_elements_u64(), + item.fill_value(), + ) + .map_py_err::()? }; Ok(value_decoded) } @@ -91,17 +98,20 @@ impl CodecPipelineImpl { codec_options: &CodecOptions, ) -> PyResult<()> { value_decoded - .validate( - item.representation().num_elements(), - item.representation().data_type().size(), - ) + .validate(item.shape().num_elements_u64(), item.data_type()) .map_codec_err()?; - if value_decoded.is_fill_value(item.representation().fill_value()) { + if value_decoded.is_fill_value(item.fill_value()) { self.store.erase(item.key()).map_py_err::() } else { let value_encoded = codec_chain - .encode(value_decoded, item.representation(), codec_options) + .encode( + value_decoded, + item.shape(), + item.data_type(), + item.fill_value(), + codec_options, + ) .map(Cow::into_owned) .map_codec_err()?; @@ -120,21 +130,23 @@ impl CodecPipelineImpl { chunk_subset: &ArraySubset, codec_options: &CodecOptions, ) -> PyResult<()> { - let array_shape = item.representation().shape_u64(); - if !chunk_subset.inbounds_shape(&array_shape) { + let array_shape = item.shape(); + if !chunk_subset.inbounds_shape(bytemuck::must_cast_slice(array_shape)) { return Err(PyErr::new::(format!( "chunk subset ({chunk_subset}) is out of bounds for array shape ({array_shape:?})" ))); } - let data_type_size = item.representation().data_type().size(); + let data_type_size = item.data_type().size(); - if chunk_subset.start().iter().all(|&o| o == 0) && chunk_subset.shape() == array_shape { + if chunk_subset.start().iter().all(|&o| o == 0) + && chunk_subset.shape() == bytemuck::must_cast_slice::<_, u64>(array_shape) + { // Fast path if the chunk subset spans the entire chunk, no read required self.store_chunk_bytes(item, codec_chain, chunk_subset_bytes, codec_options) } else { // Validate the chunk subset bytes chunk_subset_bytes - .validate(chunk_subset.num_elements(), data_type_size) + .validate(chunk_subset.num_elements(), item.data_type()) .map_codec_err()?; // Retrieve the chunk @@ -143,7 +155,7 @@ impl CodecPipelineImpl { // Update the chunk let chunk_bytes_new = update_array_bytes( chunk_bytes_old, - &array_shape, + bytemuck::must_cast_slice(array_shape), chunk_subset, &chunk_subset_bytes, data_type_size, @@ -263,17 +275,15 @@ impl CodecPipelineImpl { serde_json::from_str(array_metadata).map_py_err::()?; let codec_metadata = array_metadata_to_codec_metadata_v3(metadata).map_py_err::()?; - let codec_chain = - Arc::new(CodecChain::from_metadata(&codec_metadata).map_py_err::()?); - - let mut codec_options = CodecOptionsBuilder::new(); + let codec_chain = Arc::new( + CodecChain::from_metadata(&codec_metadata, global_config().codec_aliases_v3()) + .map_py_err::()?, + ); - codec_options = codec_options.validate_checksums(validate_checksums); + let codec_options = CodecOptions::default().with_validate_checksums(validate_checksums); - let codec_options = codec_options.build(); - - let chunk_concurrent_minimum = chunk_concurrent_minimum - .unwrap_or(zarrs::config::global_config().chunk_concurrent_minimum()); + let chunk_concurrent_minimum = + chunk_concurrent_minimum.unwrap_or(global_config().chunk_concurrent_minimum()); let chunk_concurrent_maximum = chunk_concurrent_maximum.unwrap_or(rayon::current_num_threads()); let num_threads = num_threads.unwrap_or(rayon::current_num_threads()); @@ -330,7 +340,9 @@ impl CodecPipelineImpl { .clone() .partial_decoder( Arc::new(input_handle), - item.representation(), + item.shape(), + item.data_type(), + item.fill_value(), &codec_options, ) .map_codec_err()?; @@ -358,8 +370,7 @@ impl CodecPipelineImpl { ArrayBytesFixedDisjointView::new( output, // TODO: why is data_type in `item`, it should be derived from `output`, no? - item.representation() - .data_type() + item.data_type() .fixed_size() .ok_or("variable length data type not supported") .map_py_err::()?, @@ -371,7 +382,7 @@ impl CodecPipelineImpl { // See zarrs::array::Array::retrieve_chunk_subset_into if chunk_subset.start().iter().all(|&o| o == 0) - && chunk_subset.shape() == item.representation().shape_u64() + && chunk_subset.shape() == bytemuck::must_cast_slice::<_, u64>(item.shape()) { // See zarrs::array::Array::retrieve_chunk_into if let Some(chunk_encoded) = @@ -381,16 +392,18 @@ impl CodecPipelineImpl { let chunk_encoded: Vec = chunk_encoded.into(); self.codec_chain.decode_into( Cow::Owned(chunk_encoded), - item.representation(), - &mut output_view, + item.shape(), + item.data_type(), + item.fill_value(), + ArrayBytesDecodeIntoTarget::Fixed(&mut output_view), &codec_options, ) } else { // The chunk is missing, write the fill value copy_fill_value_into( - item.representation().data_type(), - item.representation().fill_value(), - &mut output_view, + item.data_type(), + item.fill_value(), + ArrayBytesDecodeIntoTarget::Fixed(&mut output_view), ) } } else { @@ -400,7 +413,7 @@ impl CodecPipelineImpl { })?; partial_decoder.partial_decode_into( &chunk_subset, - &mut output_view, + ArrayBytesDecodeIntoTarget::Fixed(&mut output_view), &codec_options, ) } @@ -452,11 +465,7 @@ impl CodecPipelineImpl { let store_chunk = |item: chunk_item::WithSubset| match &input { InputValue::Array(input) => { let chunk_subset_bytes = input - .extract_array_subset( - &item.subset, - &input_shape, - item.item.representation().data_type(), - ) + .extract_array_subset(&item.subset, &input_shape, item.item.data_type()) .map_codec_err()?; self.store_chunk_subset_bytes( &item, @@ -468,12 +477,11 @@ impl CodecPipelineImpl { } InputValue::Constant(constant_value) => { let chunk_subset_bytes = ArrayBytes::new_fill_value( - ArraySize::new( - item.representation().data_type().size(), - item.chunk_subset.num_elements(), - ), + item.data_type(), + item.chunk_subset.num_elements(), constant_value, - ); + ) + .map_py_err::()?; self.store_chunk_subset_bytes( &item, diff --git a/src/utils.rs b/src/utils.rs index eda2aa0..c87acf5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -57,5 +57,5 @@ impl PyUntypedArrayExt for Bound<'_, PyUntypedArray> { pub fn is_whole_chunk(item: &WithSubset) -> bool { item.chunk_subset.start().iter().all(|&o| o == 0) - && item.chunk_subset.shape() == item.representation().shape_u64() + && item.chunk_subset.shape() == bytemuck::must_cast_slice::<_, u64>(item.shape()) } From 57e2e8faa44ccb4d60d6f156e4dbac24c11f6d2b Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Wed, 31 Dec 2025 10:27:47 +1100 Subject: [PATCH 2/7] chore: bump `zarrs` to 0.23.0-beta.2 --- Cargo.toml | 2 +- src/chunk_item.rs | 7 ++----- src/concurrency.rs | 2 +- src/lib.rs | 23 +++++++---------------- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index be5e594..f9e3fe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] pyo3 = { version = "0.27.1", features = ["abi3-py311"] } -zarrs = { version = "0.23.0-beta.1", features = ["async", "zlib", "pcodec", "bz2"] } +zarrs = { version = "0.23.0-beta.2", features = ["async", "zlib", "pcodec", "bz2"] } rayon_iter_concurrent_limit = "0.2.0" rayon = "1.10.0" # fix for https://stackoverflow.com/questions/76593417/package-openssl-was-not-found-in-the-pkg-config-search-path diff --git a/src/chunk_item.rs b/src/chunk_item.rs index e57ff0b..7a79b9c 100644 --- a/src/chunk_item.rs +++ b/src/chunk_item.rs @@ -168,11 +168,8 @@ impl ChunksItem for WithSubset { } fn get_data_type_from_dtype(dtype: &str) -> PyResult { - let data_type = NamedDataType::from_metadata( - &MetadataV3::new(dtype), - zarrs::config::global_config().data_type_aliases_v3(), - ) - .map_py_err::()?; + let data_type = + NamedDataType::try_from(&MetadataV3::new(dtype)).map_py_err::()?; Ok(data_type.into()) } diff --git a/src/concurrency.rs b/src/concurrency.rs index 4cde33a..a8b6007 100644 --- a/src/concurrency.rs +++ b/src/concurrency.rs @@ -1,6 +1,6 @@ use pyo3::PyResult; use zarrs::array::{ - ArrayCodecTraits, RecommendedConcurrency, codec::CodecOptions, + RecommendedConcurrency, codec::ArrayCodecTraits, codec::CodecOptions, concurrency::calc_concurrency_outer_inner, }; diff --git a/src/lib.rs b/src/lib.rs index 20e2e3a..2786f8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,15 +24,15 @@ use zarrs::array::codec::{ }; use zarrs::array::{ ArrayBytes, ArrayBytesFixedDisjointView, ArrayMetadata, ChunkShapeTraits, CodecChain, - FillValue, copy_fill_value_into, update_array_bytes, + DataTypeExt, FillValue, copy_fill_value_into, update_array_bytes, }; use zarrs::array_subset::ArraySubset; use zarrs::config::global_config; -use zarrs::metadata::v2::data_type_metadata_v2_to_endianness; -use zarrs::metadata::v3::MetadataV3; -use zarrs::metadata_ext::v2_to_v3::{ +use zarrs::convert::{ ArrayMetadataV2ToV3Error, codec_metadata_v2_to_v3, data_type_metadata_v2_to_v3, }; +use zarrs::metadata::v2::data_type_metadata_v2_to_endianness; +use zarrs::metadata::v3::MetadataV3; use zarrs::storage::{ReadableWritableListableStorage, StorageHandle, StoreKey}; mod chunk_item; @@ -224,14 +224,9 @@ fn array_metadata_to_codec_metadata_v3( match metadata { ArrayMetadata::V3(metadata) => Ok(metadata.codecs), ArrayMetadata::V2(metadata) => { - let config = global_config(); let endianness = data_type_metadata_v2_to_endianness(&metadata.dtype) .map_err(ArrayMetadataV2ToV3Error::InvalidEndianness)?; - let data_type = data_type_metadata_v2_to_v3( - &metadata.dtype, - config.data_type_aliases_v2(), - config.data_type_aliases_v3(), - )?; + let data_type = data_type_metadata_v2_to_v3(&metadata.dtype)?; codec_metadata_v2_to_v3( metadata.order, @@ -240,8 +235,6 @@ fn array_metadata_to_codec_metadata_v3( endianness, &metadata.filters, &metadata.compressor, - config.codec_aliases_v2(), - config.codec_aliases_v3(), ) } } @@ -275,10 +268,8 @@ impl CodecPipelineImpl { serde_json::from_str(array_metadata).map_py_err::()?; let codec_metadata = array_metadata_to_codec_metadata_v3(metadata).map_py_err::()?; - let codec_chain = Arc::new( - CodecChain::from_metadata(&codec_metadata, global_config().codec_aliases_v3()) - .map_py_err::()?, - ); + let codec_chain = + Arc::new(CodecChain::from_metadata(&codec_metadata).map_py_err::()?); let codec_options = CodecOptions::default().with_validate_checksums(validate_checksums); From 95f28868d1e0b7af7e7ffdd4ec574602cab42b18 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Wed, 31 Dec 2025 10:48:53 +1100 Subject: [PATCH 3/7] chore: incr to 0.2.2-dev --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f9e3fe3..2532d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zarrs-python" -version = "0.2.1" +version = "0.2.2-dev" edition = "2024" publish = false From 7f9b244188cd50b64eb8ae7633c0a6a0c493cc1a Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Wed, 31 Dec 2025 11:03:05 +1100 Subject: [PATCH 4/7] chore: minimise diff --- Cargo.toml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2532d2a..ccab001 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,11 +27,3 @@ bytemuck = { version = "1.24.0", features = ["must_cast"] } [profile.release] lto = true - -[patch.crates-io] -# zarrs = { path = "../zarrs/zarrs" } -# zarrs_storage = { path = "../zarrs/zarrs_storage" } -# zarrs_filesystem = { path = "../zarrs/zarrs_filesystem" } -# zarrs = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } -# zarrs_storage = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } -# zarrs_filesystem = { git = "https://github.com/zarrs/zarrs.git", rev = "4a49c12a" } From 10b1f4c66afc6cc110fdcc6e964896d577513059 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Wed, 31 Dec 2025 12:43:17 +1100 Subject: [PATCH 5/7] chore: bump `zarrs` to 0.23.0-beta.3 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ccab001..43996b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] pyo3 = { version = "0.27.1", features = ["abi3-py311"] } -zarrs = { version = "0.23.0-beta.2", features = ["async", "zlib", "pcodec", "bz2"] } +zarrs = { version = "0.23.0-beta.3", features = ["async", "zlib", "pcodec", "bz2"] } rayon_iter_concurrent_limit = "0.2.0" rayon = "1.10.0" # fix for https://stackoverflow.com/questions/76593417/package-openssl-was-not-found-in-the-pkg-config-search-path From c6a5839f22911d658124668e6c5f7beefe5a91d7 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 3 Jan 2026 20:28:51 +1100 Subject: [PATCH 6/7] Revert "chore: incr to 0.2.2-dev" This reverts commit 95f28868d1e0b7af7e7ffdd4ec574602cab42b18. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 43996b8..6dece68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zarrs-python" -version = "0.2.2-dev" +version = "0.2.1" edition = "2024" publish = false From 3cbe4be3c5e7bded5e7499834e13ec89f70c3554 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sat, 3 Jan 2026 20:29:46 +1100 Subject: [PATCH 7/7] fix: unsupported data type tests --- tests/test_v2.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/test_v2.py b/tests/test_v2.py index 43de1d0..c75877f 100644 --- a/tests/test_v2.py +++ b/tests/test_v2.py @@ -60,12 +60,9 @@ def test_fill_single_value(store: Store) -> None: np.testing.assert_array_equal(result, expected) -@pytest.mark.filterwarnings( - "ignore:Array is unsupported by ZarrsCodecPipeline. data type |S1 is not supported:UserWarning" -) @pytest.mark.filterwarnings( # TODO: Fix handling of string fill values for Zarr v2 bytes data - "ignore:Array is unsupported by ZarrsCodecPipeline. incompatible fill value ..+. for data type bytes:UserWarning" + "ignore:Array is unsupported by ZarrsCodecPipeline. unsupported data type .+:UserWarning" ) @pytest.mark.parametrize( ("dtype", "expected_dtype", "fill_value", "fill_value_json"), @@ -111,10 +108,7 @@ async def test_v2_encode_decode( @pytest.mark.filterwarnings( - "ignore:Array is unsupported by ZarrsCodecPipeline. data type |U1 is not supported:UserWarning" -) -@pytest.mark.filterwarnings( - "ignore:Array is unsupported by ZarrsCodecPipeline. data type |S1 is not supported:UserWarning" + "ignore:Array is unsupported by ZarrsCodecPipeline. unsupported data type .+:UserWarning" ) @pytest.mark.parametrize( ("dtype", "value"),