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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
data
.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ file. Your class should follow the [VirtualizarrProcessor protocol](./lambda/vi

- **process_file** This method should take a file uri and a session and use a Virtualizarr parser to parse it and add the resulting ManifestStore or virtual dataset to the Icechunk store.

- **validate_dataset** This method should validate the parsed xarray Dataset before writing it to Icechunk and return `True` or `False`. The sample processor uses [Pandera xarray validation](https://pandera.readthedocs.io/en/latest/xarray_guide/) as an example.

- **commit_processed_files** This method commits all the changes made during the
session in a single commit.

Expand Down
1 change: 1 addition & 0 deletions lambda/virtualizarr-processor/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description = "Module for ingesting virtualizarr data to Icechunk"
requires-python = ">=3.11"
dependencies = [
"icechunk",
"pandera[xarray]>=0.31.0",
"virtualizarr",
]

Expand Down
33 changes: 31 additions & 2 deletions lambda/virtualizarr-processor/virtualizarr_processor/processor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import os
import tempfile
from copy import Error
from datetime import datetime
from itertools import islice

Expand All @@ -9,13 +9,27 @@
import obstore
import xarray as xr
from icechunk import Repository, Session
from pandera.xarray import Coordinate, DatasetSchema, DataVar
from virtualizarr.manifests import ChunkManifest, ManifestArray
from zarr.codecs import BytesCodec
from zarr.core.dtype import parse_data_type
from zarr.core.metadata import ArrayV3Metadata
import pandera as pa

CHUNK_DIR = os.path.realpath(tempfile.gettempdir())
CHUNK_DIRECTORY_URL_PREFIX = f"file://{CHUNK_DIR}/"
logger = logging.getLogger(__name__)


EXAMPLE_DATASET_SCHEMA = DatasetSchema(
data_vars={
"foo": DataVar(dtype=np.int64, dims=("y", "x")),
},
coords={
"time": Coordinate(dtype=np.datetime64, dims=("time",)),
},
strict=True,
)


def synthetic_vds(date: str) -> xr.Dataset:
Expand Down Expand Up @@ -80,6 +94,8 @@ def initialize_repo(self) -> Repository:
if len(history) == 1:
session = repo.writable_session("main")
vds = synthetic_vds("2024-01-01")
if not self.validate_dataset(vds):
raise ValueError("Dataset validation failed for initialization dataset")
vds.vz.to_icechunk(session.store, validate_containers=False)
session.commit(message="Initialization")
return repo
Expand All @@ -92,14 +108,27 @@ def process_file(self, file_key: str, session: Session) -> bool:
result = False
try:
vds = synthetic_vds(file_key)
if not self.validate_dataset(vds):
return False
vds.vz.to_icechunk(
session.store, append_dim="time", validate_containers=False
)
result = True
except Error:
except Exception:
result = False
return result

@classmethod
def validate_dataset(cls, dataset: xr.Dataset) -> bool:
try:
EXAMPLE_DATASET_SCHEMA.validate(dataset, lazy=True)
return True
except pa.errors.SchemaErrors as e:
logger.exception(
"Dataset validation failed:",
)
return False

def commit_processed_files(self, session: Session) -> str:
snapshot = session.commit(message=f"Append to {session.snapshot_id}")
return str(snapshot)
Expand Down
16 changes: 16 additions & 0 deletions lambda/virtualizarr-processor/virtualizarr_processor/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Protocol, runtime_checkable

import icechunk
import xarray as xr
from icechunk import Repository, Session


Expand Down Expand Up @@ -56,6 +57,21 @@ def process_file(self, file_key: str, session: Session) -> bool:
"""
...

@classmethod
def validate_dataset(cls, dataset: xr.Dataset) -> bool:
"""
Validate a parsed virtual dataset before writing it to Icechunk.

Parameters
----------
dataset: The parsed xarray Dataset.
Returns
-------
bool
True if the dataset passes validation.
"""
...

def commit_processed_files(self, session: Session) -> str:
"""
Commits the updates made by one or multiple calls to process_file
Expand Down
8 changes: 7 additions & 1 deletion tests/test_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import icechunk
from icechunk import Repository, Session
from virtualizarr_processor.processor import Processor
from virtualizarr_processor.processor import Processor, synthetic_vds
from virtualizarr_processor.typing import VirtualizarrProcessor


Expand All @@ -27,6 +27,12 @@ def test_process_file(icechunk_session: Session) -> None:
assert result


def test_validate_dataset() -> None:
processor = Processor()
validated = processor.validate_dataset(synthetic_vds("2024-01-03"))
assert validated


def test_commit_processed_files(icechunk_session: Session) -> None:
processor = Processor()
snapshot = processor.commit_processed_files(session=icechunk_session)
Expand Down
37 changes: 37 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.