|
24 | 24 | import textwrap |
25 | 25 | import threading |
26 | 26 | from abc import ABC, abstractmethod, abstractproperty |
| 27 | +from collections.abc import MutableMapping |
27 | 28 | from dataclasses import asdict |
28 | 29 | from enum import Enum |
29 | 30 | from pathlib import Path |
|
42 | 43 |
|
43 | 44 | import blosc2 |
44 | 45 |
|
45 | | -from .b2objects import encode_b2object_payload, make_b2object_carrier, write_b2object_payload |
| 46 | +from .b2objects import ( |
| 47 | + encode_b2object_payload, |
| 48 | + make_b2object_carrier, |
| 49 | + read_b2object_user_vlmeta, |
| 50 | + write_b2object_payload, |
| 51 | + write_b2object_user_vlmeta, |
| 52 | +) |
46 | 53 | from .dsl_kernel import DSLKernel, DSLSyntaxError, DSLValidator, specialize_miniexpr_inputs |
47 | 54 |
|
48 | 55 | if blosc2._HAS_NUMBA: |
@@ -325,7 +332,64 @@ class LazyArrayEnum(Enum): |
325 | 332 | UDF = 1 |
326 | 333 |
|
327 | 334 |
|
| 335 | +class LazyArrayVLMeta(MutableMapping): |
| 336 | + """User metadata attached to a LazyArray.""" |
| 337 | + |
| 338 | + def __init__(self, lazyarr: LazyArray): |
| 339 | + self.lazyarr = lazyarr |
| 340 | + |
| 341 | + def __getitem__(self, key): |
| 342 | + return self.lazyarr._get_user_vlmeta()[key] |
| 343 | + |
| 344 | + def __setitem__(self, key, value): |
| 345 | + data = self.lazyarr._get_user_vlmeta() |
| 346 | + data[key] = value |
| 347 | + self.lazyarr._sync_user_vlmeta() |
| 348 | + |
| 349 | + def __delitem__(self, key): |
| 350 | + data = self.lazyarr._get_user_vlmeta() |
| 351 | + del data[key] |
| 352 | + self.lazyarr._sync_user_vlmeta() |
| 353 | + |
| 354 | + def __iter__(self): |
| 355 | + return iter(self.lazyarr._get_user_vlmeta()) |
| 356 | + |
| 357 | + def __len__(self): |
| 358 | + return len(self.lazyarr._get_user_vlmeta()) |
| 359 | + |
| 360 | + def getall(self): |
| 361 | + return self.lazyarr._get_user_vlmeta().copy() |
| 362 | + |
| 363 | + def __repr__(self): |
| 364 | + return repr(self.getall()) |
| 365 | + |
| 366 | + def __str__(self): |
| 367 | + return str(self.getall()) |
| 368 | + |
| 369 | + |
328 | 370 | class LazyArray(ABC, blosc2.Operand): |
| 371 | + def _get_user_vlmeta(self) -> dict[str, Any]: |
| 372 | + if not hasattr(self, "_vlmeta_user"): |
| 373 | + self._vlmeta_user = {} |
| 374 | + return self._vlmeta_user |
| 375 | + |
| 376 | + def _set_user_vlmeta(self, metadata: dict[str, Any], *, sync: bool = True) -> None: |
| 377 | + self._vlmeta_user = dict(metadata) |
| 378 | + if sync: |
| 379 | + self._sync_user_vlmeta() |
| 380 | + |
| 381 | + def _sync_user_vlmeta(self) -> None: |
| 382 | + array = getattr(self, "array", None) |
| 383 | + if array is not None: |
| 384 | + write_b2object_user_vlmeta(array, self._get_user_vlmeta()) |
| 385 | + |
| 386 | + @property |
| 387 | + def vlmeta(self) -> LazyArrayVLMeta: |
| 388 | + """User variable-length metadata for this LazyArray.""" |
| 389 | + if not hasattr(self, "_vlmeta_proxy"): |
| 390 | + self._vlmeta_proxy = LazyArrayVLMeta(self) |
| 391 | + return self._vlmeta_proxy |
| 392 | + |
329 | 393 | @abstractmethod |
330 | 394 | def indices(self, order: str | list[str] | None = None) -> blosc2.LazyArray: |
331 | 395 | """ |
@@ -494,6 +558,9 @@ def save(self, **kwargs: Any) -> None: |
494 | 558 | section for more info). |
495 | 559 | * This is currently only supported for :ref:`LazyExpr` and :ref:`LazyUDF` |
496 | 560 | (including kernels decorated with :func:`blosc2.dsl_kernel`). |
| 561 | + * User metadata can be attached via :attr:`vlmeta`. For in-memory LazyArrays |
| 562 | + this stays in memory; for persisted LazyArrays it is serialized and restored |
| 563 | + on reopen. |
497 | 564 |
|
498 | 565 | Examples |
499 | 566 | -------- |
@@ -3764,6 +3831,7 @@ def _to_b2object_carrier(self, **kwargs): |
3764 | 3831 | **kwargs, |
3765 | 3832 | ) |
3766 | 3833 | write_b2object_payload(array, payload) |
| 3834 | + write_b2object_user_vlmeta(array, self._get_user_vlmeta()) |
3767 | 3835 | return array |
3768 | 3836 |
|
3769 | 3837 | @classmethod |
@@ -4125,6 +4193,7 @@ def save(self, urlpath=None, **kwargs): |
4125 | 4193 | if isinstance(self.func, DSLKernel) and self.func.dsl_source is not None: |
4126 | 4194 | meta["dsl_source"] = self.func.dsl_source |
4127 | 4195 | array.schunk.vlmeta["_LazyArray"] = meta |
| 4196 | + write_b2object_user_vlmeta(array, self._get_user_vlmeta()) |
4128 | 4197 |
|
4129 | 4198 | def to_cframe(self) -> bytes: |
4130 | 4199 | return self._to_b2object_carrier().to_cframe() |
@@ -4160,6 +4229,7 @@ def _to_b2object_carrier(self, **kwargs): |
4160 | 4229 | **kwargs, |
4161 | 4230 | ) |
4162 | 4231 | write_b2object_payload(array, payload) |
| 4232 | + write_b2object_user_vlmeta(array, self._get_user_vlmeta()) |
4163 | 4233 | return array |
4164 | 4234 |
|
4165 | 4235 |
|
@@ -4518,6 +4588,7 @@ def open_lazyarray(array): |
4518 | 4588 | new_expr.array = array |
4519 | 4589 | # We want to expose schunk too, so that .info() can be used on the LazyArray |
4520 | 4590 | new_expr.schunk = array.schunk |
| 4591 | + new_expr._set_user_vlmeta(read_b2object_user_vlmeta(array), sync=False) |
4521 | 4592 | return new_expr |
4522 | 4593 |
|
4523 | 4594 |
|
|
0 commit comments