from collections.abc import MutableMapping
from zarr.meta import parse_metadata
from zarr.util import json_dumps
[docs]class Attributes(MutableMapping):
"""Class providing access to user attributes on an array or group. Should not be
instantiated directly, will be available via the `.attrs` property of an array or
group.
Parameters
----------
store : MutableMapping
The store in which to store the attributes.
key : str, optional
The key under which the attributes will be stored.
read_only : bool, optional
If True, attributes cannot be modified.
cache : bool, optional
If True (default), attributes will be cached locally.
synchronizer : Synchronizer
Only necessary if attributes may be modified from multiple threads or processes.
"""
def __init__(self, store, key='.zattrs', read_only=False, cache=True,
synchronizer=None):
self.store = store
self.key = key
self.read_only = read_only
self.cache = cache
self._cached_asdict = None
self.synchronizer = synchronizer
def _get_nosync(self):
try:
data = self.store[self.key]
except KeyError:
d = dict()
else:
d = parse_metadata(data)
return d
[docs] def asdict(self):
"""Retrieve all attributes as a dictionary."""
if self.cache and self._cached_asdict is not None:
return self._cached_asdict
d = self._get_nosync()
if self.cache:
self._cached_asdict = d
return d
[docs] def refresh(self):
"""Refresh cached attributes from the store."""
if self.cache:
self._cached_asdict = self._get_nosync()
def __contains__(self, x):
return x in self.asdict()
[docs] def __getitem__(self, item):
return self.asdict()[item]
def _write_op(self, f, *args, **kwargs):
# guard condition
if self.read_only:
raise PermissionError('attributes are read-only')
# synchronization
if self.synchronizer is None:
return f(*args, **kwargs)
else:
with self.synchronizer[self.key]:
return f(*args, **kwargs)
[docs] def __setitem__(self, item, value):
self._write_op(self._setitem_nosync, item, value)
def _setitem_nosync(self, item, value):
# load existing data
d = self._get_nosync()
# set key value
d[item] = value
# _put modified data
self._put_nosync(d)
[docs] def __delitem__(self, item):
self._write_op(self._delitem_nosync, item)
def _delitem_nosync(self, key):
# load existing data
d = self._get_nosync()
# delete key value
del d[key]
# _put modified data
self._put_nosync(d)
[docs] def put(self, d):
"""Overwrite all attributes with the key/value pairs in the provided dictionary
`d` in a single operation."""
self._write_op(self._put_nosync, d)
def _put_nosync(self, d):
self.store[self.key] = json_dumps(d)
if self.cache:
self._cached_asdict = d
# noinspection PyMethodOverriding
[docs] def update(self, *args, **kwargs):
"""Update the values of several attributes in a single operation."""
self._write_op(self._update_nosync, *args, **kwargs)
def _update_nosync(self, *args, **kwargs):
# load existing data
d = self._get_nosync()
# update
d.update(*args, **kwargs)
# _put modified data
self._put_nosync(d)
[docs] def keys(self):
return self.asdict().keys()
[docs] def __iter__(self):
return iter(self.asdict())
[docs] def __len__(self):
return len(self.asdict())
def _ipython_key_completions_(self):
return sorted(self)