Skip to content
Open
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
18 changes: 11 additions & 7 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@ on:
jobs:
test:
runs-on: ubuntu-latest
env:
UV_NO_SYNC: "1"
strategy:
matrix:
python-version:
- "3.11"
python-version: ["3.11"]
with-zarr: [true, false]
resolution: [highest, lowest-direct]

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: uv sync --all-extras --dev
run: uv sync --no-dev --group test ${{ matrix.with-zarr && '--extra zarr' || '' }}
env:
UV_RESOLUTION: ${{ matrix.resolution }}

- name: Run tests
# For example, using `pytest`
Expand Down
17 changes: 9 additions & 8 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ build:
os: ubuntu-24.04
tools:
python: "3"

python:
install:
- method: pip
path: .
extra_requirements:
- docs
- pydantic
jobs:
pre_create_environment:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
create_environment:
- uv venv "${READTHEDOCS_VIRTUALENV_PATH}"
install:
- UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" uv sync --group docs

mkdocs:
configuration: mkdocs.yml
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Each class has

```sh
pip install ome-zarr-models
# or ... with zarr io support
pip install ome-zarr-models[zarr]
```

or
Expand Down
2 changes: 2 additions & 0 deletions docs/tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
fill_value=0,
codecs=[NamedConfig(name="bytes")],
dimension_names=["y", "x"],
attributes={},
),
ArraySpec(
shape=(100, 100),
Expand All @@ -103,6 +104,7 @@
fill_value=0,
codecs=[NamedConfig(name="bytes")],
dimension_names=["y", "x"],
attributes={},
),
]

Expand Down
42 changes: 26 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ dynamic = ["version"]
description = "A minimal Python package for reading OME-Zarr (meta)data "
readme = "README.md"
requires-python = ">=3.11"
dependencies = ["zarr>= 3.1.1", "pydantic >= 2.11.5", "pydantic-zarr >= 0.8.2"]
dependencies = ["pydantic >= 2.11.5", "pydantic-zarr>=0.8.2"]
maintainers = [{ name = "David Stansby" }]
license = { file = "LICENSE" }
classifiers = [
Expand All @@ -21,16 +21,8 @@ Repository = "https://github.com/ome-zarr-models/ome-zarr-models-py"
# Changelog = ""

[project.optional-dependencies]
docs = [
"mkdocs>=1.6.1",
"mkdocstrings-python>=1.12.2",
"mkdocs-material",
"mkdocs-jupyter",
"matplotlib",
"rich",
"griffe-pydantic",
"fsspec[http]",
]
zarr = ["zarr>= 3.1.1"]


[project.scripts]
ome-zarr-models = "ome_zarr_models._cli:main"
Expand All @@ -43,12 +35,30 @@ requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[dependency-groups]
docs = ["ome-zarr-models[docs]"]
dev = ["mypy", "ruff>=0.8", "pre-commit"]
test = ["pytest>=8.3.3", "pytest-cov", "pytest-recording"]
docs = [
"mkdocs>=1.6.1",
"mkdocstrings-python>=1.12.2",
"mkdocs-material",
"mkdocs-jupyter >=0.25",
"matplotlib >=3.10",
"rich >= 14",
"griffe-pydantic",
"fsspec[http] >=2025",
]
dev = [
{ include-group = "docs" },
{ include-group = "test" },
"mypy >=1.18",
"ruff>=0.8",
"pre-commit",
"ome-zarr-models[zarr]",
]
test = ["pytest>=8.3.3", "pytest-cov >=7.0.0", "pytest-recording", "fsspec[http] >=2025", "typos>=0.6.0"]


[tool.uv]
default-groups = ["docs", "dev", "test"]
# TEMPORARY: remove before merge
[tool.uv.sources]
pydantic-zarr = { git = "https://github.com/zarr-developers/pydantic-zarr", rev = "main" }

# Ruff configuration for linting and formatting
# https://docs.astral.sh/ruff
Expand Down
17 changes: 14 additions & 3 deletions src/ome_zarr_models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from __future__ import annotations

from importlib.metadata import PackageNotFoundError, version
from typing import TYPE_CHECKING, Any, Literal

import zarr
import zarr.storage

import ome_zarr_models.v04.hcs
import ome_zarr_models.v04.image
import ome_zarr_models.v04.image_label
Expand All @@ -18,6 +17,10 @@
from ome_zarr_models.v04.base import BaseGroupv04
from ome_zarr_models.v05.base import BaseGroupv05

if TYPE_CHECKING:
import zarr
import zarr.storage

try:
__version__ = version("ome_zarr_models")
except PackageNotFoundError: # pragma: no cover
Expand Down Expand Up @@ -99,6 +102,14 @@ def open_ome_zarr(
take a long time. It will be quicker to directly use the OME-Zarr group class if you
know which version and group you expect.
"""
try:
import zarr
except ImportError as e:
raise ImportError(
"zarr must be installed to use open_ome_zarr(). "
"You can install it with 'pip install ome-zarr-models[zarr]'"
) from e

if not isinstance(group, zarr.Group):
zarr_format = _ome_zarr_zarr_map.get(version, None) # type: ignore[arg-type]
group = zarr.open_group(group, zarr_format=zarr_format, mode="r")
Expand Down
5 changes: 5 additions & 0 deletions src/ome_zarr_models/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
import pydantic_zarr.v3
from pydantic import create_model

if TYPE_CHECKING:
from collections.abc import Iterable

from zarr.abc.store import Store

from ome_zarr_models.base import BaseAttrsv2, BaseAttrsv3
from ome_zarr_models.common.validation import (
check_array_path,
Expand Down
4 changes: 3 additions & 1 deletion src/ome_zarr_models/_v06/hcs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import annotations

from collections.abc import Generator, Mapping
from typing import TYPE_CHECKING, Self

# Import needed for pydantic type resolution
import pydantic_zarr # noqa: F401
import zarr
from pydantic import model_validator
from pydantic_zarr.v3 import GroupSpec

Expand All @@ -14,6 +15,7 @@
from ome_zarr_models.common.well import WellGroupNotFoundError

if TYPE_CHECKING:
import zarr
from pydantic_zarr.v3 import AnyGroupSpec

__all__ = ["HCS", "HCSAttrs"]
Expand Down
14 changes: 10 additions & 4 deletions src/ome_zarr_models/_v06/image.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Self
from typing import TYPE_CHECKING, Self

# Import needed for pydantic type resolution
import pydantic_zarr # noqa: F401
import zarr
import zarr.errors
from pydantic import Field, JsonValue, model_validator
from pydantic_zarr.v3 import AnyArraySpec, AnyGroupSpec, GroupSpec

Expand All @@ -15,6 +15,12 @@
from ome_zarr_models._v06.multiscales import Dataset, Multiscale
from ome_zarr_models.common.coordinate_transformations import _build_transforms

if TYPE_CHECKING:
from collections.abc import Sequence

import zarr


__all__ = ["Image", "ImageAttrs"]


Expand Down Expand Up @@ -71,7 +77,7 @@ def new(
metadata: JsonValue | None = None,
global_scale: Sequence[float] | None = None,
global_translation: Sequence[float] | None = None,
) -> "Image":
) -> Image:
"""
Create a new `Image` from a sequence of multiscale arrays
and spatial metadata.
Expand Down
9 changes: 7 additions & 2 deletions src/ome_zarr_models/_v06/image_label.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
from typing import Self
from __future__ import annotations

from typing import TYPE_CHECKING, Self

# Import needed for pydantic type resolution
import pydantic_zarr # noqa: F401
import zarr
from pydantic import Field

from ome_zarr_models._v06.base import BaseGroupv06, BaseOMEAttrs
from ome_zarr_models._v06.image import Image
from ome_zarr_models._v06.image_label_types import Label
from ome_zarr_models._v06.multiscales import Multiscale

if TYPE_CHECKING:
import zarr


__all__ = ["ImageLabel", "ImageLabelAttrs"]


Expand Down
16 changes: 12 additions & 4 deletions src/ome_zarr_models/_v06/labels.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Self

import numpy as np

# Import needed for pydantic type resolution
import pydantic_zarr # noqa: F401
import zarr
import zarr.errors
from pydantic import Field, ValidationError, model_validator
from pydantic_zarr.v3 import AnyArraySpec, AnyGroupSpec, GroupSpec

from ome_zarr_models._v06.base import BaseGroupv06, BaseOMEAttrs
from ome_zarr_models.common.validation import check_array_spec, check_group_spec

if TYPE_CHECKING:
import zarr

from ome_zarr_models._v06.image_label import ImageLabel


Expand All @@ -34,7 +36,7 @@
]


def _check_valid_dtypes(labels: "Labels") -> "Labels":
def _check_valid_dtypes(labels: Labels) -> Labels:
"""
Check that all multiscales levels of a labels image are valid Label data types.
"""
Expand Down Expand Up @@ -102,6 +104,12 @@ def from_zarr(cls, group: zarr.Group) -> Self: # type: ignore[override]
group : zarr.Group
A Zarr group that has valid OME-Zarr label metadata.
"""
try:
import zarr
import zarr.errors
except ImportError as e:
raise ImportError("zarr is required to use this function") from e

from ome_zarr_models._v06.image_label import ImageLabel

attrs_dict = group.attrs.asdict()
Expand Down Expand Up @@ -141,7 +149,7 @@ def label_paths(self) -> list[str]:
"""
return self.attributes.ome.labels

def get_image_labels_group(self, path: str) -> "ImageLabel":
def get_image_labels_group(self, path: str) -> ImageLabel:
"""
Get a image labels group at a given path.
"""
Expand Down
10 changes: 7 additions & 3 deletions src/ome_zarr_models/_v06/well.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Import needed for pydantic type resolution
from typing import Self
from __future__ import annotations

import zarr
# Import needed for pydantic type resolution
from typing import TYPE_CHECKING, Self

from ome_zarr_models._utils import _from_zarr_v3
from ome_zarr_models._v06.base import BaseGroupv06, BaseOMEAttrs
from ome_zarr_models._v06.image import Image
from ome_zarr_models._v06.well_types import WellMeta

if TYPE_CHECKING:
import zarr


__all__ = ["Well", "WellAttrs"]


Expand Down
13 changes: 11 additions & 2 deletions src/ome_zarr_models/common/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

from typing import TYPE_CHECKING, Literal, TypeVar, overload

import zarr
import zarr.errors
from pydantic import StringConstraints
from pydantic_zarr.v2 import AnyArraySpec as AnyArraySpecv2
from pydantic_zarr.v2 import AnyGroupSpec as AnyGroupSpecv2
Expand All @@ -20,6 +18,8 @@
if TYPE_CHECKING:
from collections.abc import Sequence

import zarr


__all__ = [
"AlphaNumericConstraint",
Expand Down Expand Up @@ -89,6 +89,12 @@ def check_array_path(
ValueError
If the array doesn't exist, or the array is not the expected Zarr version.
"""
try:
import zarr
import zarr.errors
except ImportError as e:
raise ImportError("zarr is required to use this function") from e

try:
array = zarr.open_array(
store=group.store_path,
Expand Down Expand Up @@ -158,6 +164,9 @@ def check_group_path(
ValueError
If the group doesn't exist, or the group is not the expected Zarr version.
"""
import zarr
import zarr.errors

try:
group = zarr.open_group(
store=group.store_path,
Expand Down
Loading