Source code for z5py.attribute_manager

import json
import os
import errno
from functools import wraps
from contextlib import contextmanager

try:
    from collections.abc import MutableMapping
except ImportError:
    from collections import MutableMapping


__all__ = ['AttributeManager']


def restrict_metadata_keys(fn):
    """ Decorator for AttributeManager methods which checks that,
    if the manager is for N5, the key argument is not a
    reserved metadata name.
    """
    @wraps(fn)
    def wrapped(obj, key, *args, **kwargs):
        if key in obj.reserved_keys:
            raise RuntimeError("Reserved metadata (key {}) cannot be mutated".format(repr(key)))
        return fn(obj, key, *args, **kwargs)
    return wrapped


[docs]class AttributeManager(MutableMapping): """ Provides access to custom user attributes. Attributes will be saved as json in `attributes.json` for n5, `.zattributes` for zarr. Supports the default python dict api. N5 stores the dataset attributes in the same file; these attributes are NOT mutable via the AttributeManager. """ _zarr_fname = '.zattributes' _n5_fname = 'attributes.json' _n5_keys = frozenset(('dimensions', 'blockSize', 'dataType', 'compressionType', 'compression')) def __init__(self, path, is_zarr): self.path = os.path.join(path, self._zarr_fname if is_zarr else self._n5_fname) self.reserved_keys = frozenset() if is_zarr else self._n5_keys def __getitem__(self, key): with self._open_attributes() as attributes: return attributes[key] @restrict_metadata_keys def __setitem__(self, key, item): with self._open_attributes(True) as attributes: attributes[key] = item @restrict_metadata_keys def __delitem__(self, key): with self._open_attributes(True) as attributes: del attributes[key] @contextmanager def _open_attributes(self, write=False): """Context manager for JSON attribute store. Set ``write`` to True to dump out changes.""" attributes = self._read_attributes() hidden_attrs = {key: attributes.pop(key) for key in self.reserved_keys.intersection(attributes)} yield attributes if write: hidden_attrs.update(attributes) self._write_attributes(hidden_attrs) def _read_attributes(self): """Return dict from JSON attribute store. Caller needs to distinguish between valid and N5 metadata keys.""" try: with open(self.path, 'r') as f: attributes = json.load(f) except ValueError: attributes = {} except IOError as e: if e.errno == errno.ENOENT: attributes = {} else: raise return attributes def _write_attributes(self, attributes): """Dump ``attributes`` to JSON. Potentially dangerous for N5 metadata.""" with open(self.path, 'w') as f: json.dump(attributes, f) def __iter__(self): with self._open_attributes() as attributes: return iter(attributes) def __len__(self): with self._open_attributes() as attributes: return len(attributes)