Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions python/zarrista/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
AsyncArray,
AsyncGroup,
ChunkGrid,
Config,
DataType,
FilesystemStore,
FillValue,
Expand All @@ -19,6 +20,7 @@
Tensor,
VariableArray,
__version__,
config,
)

DecodedArray: TypeAlias = Tensor | VariableArray | MaskedTensor | MaskedVariableArray
Expand All @@ -36,6 +38,7 @@
"AsyncArray",
"AsyncGroup",
"ChunkGrid",
"Config",
"DataType",
"DecodedArray",
"FilesystemStore",
Expand All @@ -48,5 +51,6 @@
"VariableArray",
"__version__",
"codec",
"config",
"exceptions",
]
113 changes: 113 additions & 0 deletions python/zarrista/_config.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from typing import Literal

MetadataConvertVersion = Literal["default", "v3"]

Check failure on line 3 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (PYI026)

python/zarrista/_config.pyi:3:1: PYI026 Use `typing.TypeAlias` for type alias, e.g., `MetadataConvertVersion: TypeAlias = Literal["default", "v3"]` help: Add `TypeAlias` annotation
"""The Zarr version to write when converting metadata.

- `"default"`: write the same version as the input metadata.
- `"v3"`: write Zarr V3 metadata (existing V2 metadata is not removed).
"""

MetadataEraseVersion = Literal["default", "all", "v3", "v2"]

Check failure on line 10 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (PYI026)

python/zarrista/_config.pyi:10:1: PYI026 Use `typing.TypeAlias` for type alias, e.g., `MetadataEraseVersion: TypeAlias = Literal["default", "all", "v3", "v2"]` help: Add `TypeAlias` annotation
"""The Zarr version of metadata to erase.

- `"default"`: erase the same version as the input metadata.
- `"all"`: erase all metadata.
- `"v3"`: erase Zarr V3 metadata.
- `"v2"`: erase Zarr V2 metadata.
"""

UseConsolidatedMetadata = Literal["auto", "must", "never"]

Check failure on line 19 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (PYI026)

python/zarrista/_config.pyi:19:1: PYI026 Use `typing.TypeAlias` for type alias, e.g., `UseConsolidatedMetadata: TypeAlias = Literal["auto", "must", "never"]` help: Add `TypeAlias` annotation
"""Whether to use a root group's consolidated metadata when opening a hierarchy.

- `"auto"`: use consolidated metadata if present, otherwise list storage.
- `"must"`: require consolidated metadata to be present, else fail.
- `"never"`: never use consolidated metadata, always re-discover from storage.
"""

class Config:
"""A proxy to the `zarrs` global configuration.

This type is not constructable; use the module-level singleton
[`zarrista.config`][zarrista.config] instead. Its getters and setters read
from and write to the process-wide global configuration.
"""

@property
def validate_checksums(self) -> bool:
"""Whether checksum codecs (e.g. `crc32c`, `fletcher32`) validate that
encoded data matches stored checksums. Default `True`."""

Check failure on line 38 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D209)

python/zarrista/_config.pyi:37:9: D209 Multi-line docstring closing quotes should be on a separate line help: Move closing quotes to new line

Check failure on line 38 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D205)

python/zarrista/_config.pyi:37:9: D205 1 blank line required between summary line and description help: Insert single blank line

@validate_checksums.setter
def validate_checksums(self, value: bool) -> None: ...
@property
def store_empty_chunks(self) -> bool:
"""If `False`, chunks where every element equals the fill value are not
stored. If `True`, the fill-value check is skipped and empty chunks are
stored. Default `False`."""

Check failure on line 46 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D209)

python/zarrista/_config.pyi:44:9: D209 Multi-line docstring closing quotes should be on a separate line help: Move closing quotes to new line

Check failure on line 46 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D205)

python/zarrista/_config.pyi:44:9: D205 1 blank line required between summary line and description help: Insert single blank line

@store_empty_chunks.setter
def store_empty_chunks(self, value: bool) -> None: ...
@property
def codec_concurrent_target(self) -> int:
"""The default number of concurrent operations to target for codec
encoding and decoding. Zero means unconstrained. Defaults to the number
of available threads."""

Check failure on line 54 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D209)

python/zarrista/_config.pyi:52:9: D209 Multi-line docstring closing quotes should be on a separate line help: Move closing quotes to new line

Check failure on line 54 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D205)

python/zarrista/_config.pyi:52:9: D205 1 blank line required between summary line and description help: Insert single blank line

@codec_concurrent_target.setter
def codec_concurrent_target(self, value: int) -> None: ...
@property
def chunk_concurrent_minimum(self) -> int:
"""The preferred minimum chunk concurrency for array operations spanning
multiple chunks. Default `4`."""

Check failure on line 61 in python/zarrista/_config.pyi

View workflow job for this annotation

GitHub Actions / Lint

ruff (D205)

python/zarrista/_config.pyi:60:9: D205 1 blank line required between summary line and description help: Insert single blank line

@chunk_concurrent_minimum.setter
def chunk_concurrent_minimum(self, value: int) -> None: ...
@property
def codec_store_metadata_if_encode_only(self) -> bool:
"""Whether codecs performing irreversible encode-only transformations
(currently only `bitround`) write their metadata. Default `True`."""

@codec_store_metadata_if_encode_only.setter
def codec_store_metadata_if_encode_only(self, value: bool) -> None: ...
@property
def include_zarrs_metadata(self) -> bool:
"""Whether generated array metadata includes the `_zarrs` attribute
recording the `zarrs` version and source repository. Default `True`."""

@include_zarrs_metadata.setter
def include_zarrs_metadata(self, value: bool) -> None: ...
@property
def experimental_partial_encoding(self) -> bool:
"""Whether `store_chunk_subset` / `store_array_subset` may use partial
encoding (relevant to the sharding codec). Experimental. Default
`False`."""

@experimental_partial_encoding.setter
def experimental_partial_encoding(self, value: bool) -> None: ...
@property
def convert_aliased_extension_names(self) -> bool:
"""Whether aliased extension names are replaced by their standard name
when metadata is resaved. Default `False`."""

@convert_aliased_extension_names.setter
def convert_aliased_extension_names(self, value: bool) -> None: ...
@property
def metadata_convert_version(self) -> MetadataConvertVersion:
"""The Zarr version to write when converting metadata. Default
`"default"`."""

@metadata_convert_version.setter
def metadata_convert_version(self, value: MetadataConvertVersion) -> None: ...
@property
def metadata_erase_version(self) -> MetadataEraseVersion:
"""The Zarr version of metadata to erase. Default `"default"`."""

@metadata_erase_version.setter
def metadata_erase_version(self, value: MetadataEraseVersion) -> None: ...
@property
def use_consolidated_metadata(self) -> UseConsolidatedMetadata:
"""Whether to use a root group's consolidated metadata when opening a
hierarchy. Default `"auto"`."""

@use_consolidated_metadata.setter
def use_consolidated_metadata(self, value: UseConsolidatedMetadata) -> None: ...
6 changes: 6 additions & 0 deletions python/zarrista/_zarrista.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ._array import Array, AsyncArray
from ._array_bytes import ArrayBytes
from ._chunks import ChunkGrid
from ._config import Config
from ._decoded_array import MaskedTensor, MaskedVariableArray, Tensor, VariableArray
from ._dtype import DataType
from ._fill_value import FillValue
Expand All @@ -9,12 +10,16 @@ from ._store import FilesystemStore, MemoryStore

__version__: str

config: Config
"""The `zarrs` global configuration singleton."""

__all__ = [
"Array",
"ArrayBytes",
"AsyncArray",
"AsyncGroup",
"ChunkGrid",
"Config",
"DataType",
"FilesystemStore",
"FillValue",
Expand All @@ -25,4 +30,5 @@ __all__ = [
"Tensor",
"VariableArray",
"__version__",
"config",
]
193 changes: 193 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::pybacked::PyBackedStr;
use zarrs::config::{
global_config, global_config_mut, MetadataConvertVersion, MetadataEraseVersion,
UseConsolidatedMetadata,
};

/// A proxy to the `zarrs` global configuration.
///
/// This type is not constructable from Python; use the module-level singleton
/// [`zarrista.config`] instead. Its getters and setters read from and write to
/// the process-wide global configuration.
#[pyclass(module = "zarrista", name = "Config")]
pub struct PyConfig;

#[pymethods]
impl PyConfig {
#[getter]
fn validate_checksums(&self) -> bool {
global_config().validate_checksums()
}

#[setter]
fn set_validate_checksums(&mut self, value: bool) {
global_config_mut().set_validate_checksums(value);
}

#[getter]
fn store_empty_chunks(&self) -> bool {
global_config().store_empty_chunks()
}

#[setter]
fn set_store_empty_chunks(&mut self, value: bool) {
global_config_mut().set_store_empty_chunks(value);
}

#[getter]
fn codec_concurrent_target(&self) -> usize {
global_config().codec_concurrent_target()
}

#[setter]
fn set_codec_concurrent_target(&mut self, value: usize) {
global_config_mut().set_codec_concurrent_target(value);
}

#[getter]
fn chunk_concurrent_minimum(&self) -> usize {
global_config().chunk_concurrent_minimum()
}

#[setter]
fn set_chunk_concurrent_minimum(&mut self, value: usize) {
global_config_mut().set_chunk_concurrent_minimum(value);
}

#[getter]
fn codec_store_metadata_if_encode_only(&self) -> bool {
global_config().codec_store_metadata_if_encode_only()
}

#[setter]
fn set_codec_store_metadata_if_encode_only(&mut self, value: bool) {
global_config_mut().set_codec_store_metadata_if_encode_only(value);
}

#[getter]
fn include_zarrs_metadata(&self) -> bool {
global_config().include_zarrs_metadata()
}

#[setter]
fn set_include_zarrs_metadata(&mut self, value: bool) {
global_config_mut().set_include_zarrs_metadata(value);
}

#[getter]
fn experimental_partial_encoding(&self) -> bool {
global_config().experimental_partial_encoding()
}

#[setter]
fn set_experimental_partial_encoding(&mut self, value: bool) {
global_config_mut().set_experimental_partial_encoding(value);
}

#[getter]
fn convert_aliased_extension_names(&self) -> bool {
global_config().convert_aliased_extension_names()
}

#[setter]
fn set_convert_aliased_extension_names(&mut self, value: bool) {
global_config_mut().set_convert_aliased_extension_names(value);
}

#[getter]
fn metadata_convert_version(&self) -> &'static str {
metadata_convert_version_to_str(global_config().metadata_convert_version())
}

#[setter]
fn set_metadata_convert_version(&mut self, value: PyBackedStr) -> PyResult<()> {
let version = parse_metadata_convert_version(&value)?;
global_config_mut().set_metadata_convert_version(version);
Ok(())
}

#[getter]
fn metadata_erase_version(&self) -> &'static str {
metadata_erase_version_to_str(global_config().metadata_erase_version())
}

#[setter]
fn set_metadata_erase_version(&mut self, value: PyBackedStr) -> PyResult<()> {
let version = parse_metadata_erase_version(&value)?;
global_config_mut().set_metadata_erase_version(version);
Ok(())
}

#[getter]
fn use_consolidated_metadata(&self) -> &'static str {
use_consolidated_metadata_to_str(global_config().use_consolidated_metadata())
}

#[setter]
fn set_use_consolidated_metadata(&mut self, value: PyBackedStr) -> PyResult<()> {
let mode = parse_use_consolidated_metadata(&value)?;
global_config_mut().set_use_consolidated_metadata(mode);
Ok(())
}
}

fn metadata_convert_version_to_str(version: MetadataConvertVersion) -> &'static str {
match version {
MetadataConvertVersion::Default => "default",
MetadataConvertVersion::V3 => "v3",
}
}

fn parse_metadata_convert_version(value: &str) -> PyResult<MetadataConvertVersion> {
match value.to_ascii_lowercase().as_str() {
"default" => Ok(MetadataConvertVersion::Default),
"v3" => Ok(MetadataConvertVersion::V3),
other => Err(PyValueError::new_err(format!(
"unknown metadata convert version {other:?}; expected one of 'default', 'v3'"
))),
}
}

fn metadata_erase_version_to_str(version: MetadataEraseVersion) -> &'static str {
match version {
MetadataEraseVersion::Default => "default",
MetadataEraseVersion::All => "all",
MetadataEraseVersion::V3 => "v3",
MetadataEraseVersion::V2 => "v2",
}
}

fn parse_metadata_erase_version(value: &str) -> PyResult<MetadataEraseVersion> {
match value.to_ascii_lowercase().as_str() {
"default" => Ok(MetadataEraseVersion::Default),
"all" => Ok(MetadataEraseVersion::All),
"v3" => Ok(MetadataEraseVersion::V3),
"v2" => Ok(MetadataEraseVersion::V2),
other => Err(PyValueError::new_err(format!(
"unknown metadata erase version {other:?}; expected one of \
'default', 'all', 'v3', 'v2'"
))),
}
}

fn use_consolidated_metadata_to_str(mode: UseConsolidatedMetadata) -> &'static str {
match mode {
UseConsolidatedMetadata::Auto => "auto",
UseConsolidatedMetadata::Must => "must",
UseConsolidatedMetadata::Never => "never",
}
}

fn parse_use_consolidated_metadata(value: &str) -> PyResult<UseConsolidatedMetadata> {
match value.to_ascii_lowercase().as_str() {
"auto" => Ok(UseConsolidatedMetadata::Auto),
"must" => Ok(UseConsolidatedMetadata::Must),
"never" => Ok(UseConsolidatedMetadata::Never),
other => Err(PyValueError::new_err(format!(
"unknown use consolidated metadata mode {other:?}; expected one of \
'auto', 'must', 'never'"
))),
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod array;
mod array_bytes;
mod chunks;
mod codec;
mod config;
mod decoded_array;
mod dtype;
mod error;
Expand All @@ -20,6 +21,7 @@ use crate::array::{PyArray, PyAsyncArray};
use crate::array_bytes::PyArrayBytes;
use crate::chunks::PyChunkGrid;
use crate::codec::register_codec_module;
use crate::config::PyConfig;
use crate::decoded_array::{PyMaskedTensor, PyMaskedVariableArray, PyTensor, PyVariableArray};
use crate::dtype::PyDataType;
use crate::exceptions::register_exceptions_module;
Expand All @@ -37,6 +39,8 @@ fn _zarrista(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyAsyncArray>()?;
m.add_class::<PyAsyncGroup>()?;
m.add_class::<PyChunkGrid>()?;
m.add_class::<PyConfig>()?;
m.add("config", Bound::new(m.py(), PyConfig)?)?;
m.add_class::<PyTensor>()?;
m.add_class::<PyVariableArray>()?;
m.add_class::<PyMaskedTensor>()?;
Expand Down
Loading