summaryrefslogtreecommitdiff
path: root/silx/io/commonh5.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/io/commonh5.py')
-rw-r--r--silx/io/commonh5.py1060
1 files changed, 0 insertions, 1060 deletions
diff --git a/silx/io/commonh5.py b/silx/io/commonh5.py
deleted file mode 100644
index 4b2d8c3..0000000
--- a/silx/io/commonh5.py
+++ /dev/null
@@ -1,1060 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-This module contains generic objects, emulating *h5py* groups, datasets and
-files. They are used in :mod:`spech5` and :mod:`fabioh5`.
-
-.. note:: This module has a dependency on the `h5py <http://www.h5py.org/>`_
- library, which is not a mandatory dependency for `silx`.
-"""
-import collections
-import h5py
-import numpy
-from silx.third_party import six
-import weakref
-
-from . import utils
-
-__authors__ = ["V. Valls", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "02/07/2018"
-
-
-class _MappingProxyType(collections.MutableMapping):
- """Read-only dictionary
-
- This class is available since Python 3.3, but not on earlyer Python
- versions.
- """
-
- def __init__(self, data):
- self._data = data
-
- def __getitem__(self, key):
- return self._data[key]
-
- def __len__(self):
- return len(self._data)
-
- def __iter__(self):
- return iter(self._data)
-
- def get(self, key, default=None):
- return self._data.get(key, default)
-
- def __setitem__(self, key, value):
- raise RuntimeError("Cannot modify read-only dictionary")
-
- def __delitem__(self, key):
- raise RuntimeError("Cannot modify read-only dictionary")
-
- def pop(self, key):
- raise RuntimeError("Cannot modify read-only dictionary")
-
- def clear(self):
- raise RuntimeError("Cannot modify read-only dictionary")
-
- def update(self, key, value):
- raise RuntimeError("Cannot modify read-only dictionary")
-
- def setdefault(self, key):
- raise RuntimeError("Cannot modify read-only dictionary")
-
-
-class Node(object):
- """This is the base class for all :mod:`spech5` and :mod:`fabioh5`
- classes. It represents a tree node, and knows its parent node
- (:attr:`parent`).
- The API mimics a *h5py* node, with following attributes: :attr:`file`,
- :attr:`attrs`, :attr:`name`, and :attr:`basename`.
- """
-
- def __init__(self, name, parent=None, attrs=None):
- self._set_parent(parent)
- self.__basename = name
- self.__attrs = {}
- if attrs is not None:
- self.__attrs.update(attrs)
-
- def _set_basename(self, name):
- self.__basename = name
-
- @property
- def h5_class(self):
- """Returns the HDF5 class which is mimicked by this class.
-
- :rtype: H5Type
- """
- raise NotImplementedError()
-
- @property
- def h5py_class(self):
- """Returns the h5py classes which is mimicked by this class. It can be
- one of `h5py.File, h5py.Group` or `h5py.Dataset`
-
- This should not be used anymore. Prefer using `h5_class`
-
- :rtype: Class
- """
- h5_class = self.h5_class
- if h5_class == utils.H5Type.FILE:
- return h5py.File
- elif h5_class == utils.H5Type.GROUP:
- return h5py.Group
- elif h5_class == utils.H5Type.DATASET:
- return h5py.Dataset
- elif h5_class == utils.H5Type.SOFT_LINK:
- return h5py.SoftLink
- raise NotImplementedError()
-
- @property
- def parent(self):
- """Returns the parent of the node.
-
- :rtype: Node
- """
- if self.__parent is None:
- parent = None
- else:
- parent = self.__parent()
- if parent is None:
- self.__parent = None
- return parent
-
- def _set_parent(self, parent):
- """Set the parent of this node.
-
- It do not update the parent object.
-
- :param Node parent: New parent for this node
- """
- if parent is not None:
- self.__parent = weakref.ref(parent)
- else:
- self.__parent = None
-
- @property
- def file(self):
- """Returns the file node of this node.
-
- :rtype: Node
- """
- node = self
- while node.parent is not None:
- node = node.parent
- if isinstance(node, File):
- return node
- else:
- return None
-
- @property
- def attrs(self):
- """Returns HDF5 attributes of this node.
-
- :rtype: dict
- """
- if self._is_editable():
- return self.__attrs
- else:
- return _MappingProxyType(self.__attrs)
-
- @property
- def name(self):
- """Returns the HDF5 name of this node.
- """
- parent = self.parent
- if parent is None:
- return "/"
- if parent.name == "/":
- return "/" + self.basename
- return parent.name + "/" + self.basename
-
- @property
- def basename(self):
- """Returns the HDF5 basename of this node.
- """
- return self.__basename
-
- def _is_editable(self):
- """Returns true if the file is editable or if the node is not linked
- to a tree.
-
- :rtype: bool
- """
- f = self.file
- return f is None or f.mode == "w"
-
-
-class Dataset(Node):
- """This class handles a numpy data object, as a mimicry of a
- *h5py.Dataset*.
- """
-
- def __init__(self, name, data, parent=None, attrs=None):
- Node.__init__(self, name, parent, attrs=attrs)
- if data is not None:
- self._check_data(data)
- self.__data = data
-
- def _check_data(self, data):
- """Check that the data provided by the dataset is valid.
-
- It is valid when it can be stored in a HDF5 using h5py.
-
- :param numpy.ndarray data: Data associated to the dataset
- :raises TypeError: In the case the data is not valid.
- """
- if isinstance(data, (six.text_type, six.binary_type)):
- return
-
- chartype = data.dtype.char
- if chartype == "U":
- pass
- elif chartype == "O":
- d = h5py.special_dtype(vlen=data.dtype)
- if d is not None:
- return
- d = h5py.special_dtype(ref=data.dtype)
- if d is not None:
- return
- else:
- return
-
- msg = "Type of the dataset '%s' is not supported. Found '%s'."
- raise TypeError(msg % (self.name, data.dtype))
-
- def _set_data(self, data):
- """Set the data exposed by the dataset.
-
- It have to be called only one time before the data is used. It should
- not be edited after use.
-
- :param numpy.ndarray data: Data associated to the dataset
- """
- self._check_data(data)
- self.__data = data
-
- def _get_data(self):
- """Returns the exposed data
-
- :rtype: numpy.ndarray
- """
- return self.__data
-
- @property
- def h5_class(self):
- """Returns the HDF5 class which is mimicked by this class.
-
- :rtype: H5Type
- """
- return utils.H5Type.DATASET
-
- @property
- def dtype(self):
- """Returns the numpy datatype exposed by this dataset.
-
- :rtype: numpy.dtype
- """
- return self._get_data().dtype
-
- @property
- def shape(self):
- """Returns the shape of the data exposed by this dataset.
-
- :rtype: tuple
- """
- if isinstance(self._get_data(), numpy.ndarray):
- return self._get_data().shape
- else:
- return tuple()
-
- @property
- def size(self):
- """Returns the size of the data exposed by this dataset.
-
- :rtype: int
- """
- if isinstance(self._get_data(), numpy.ndarray):
- return self._get_data().size
- else:
- # It is returned as float64 1.0 by h5py
- return numpy.float64(1.0)
-
- def __len__(self):
- """Returns the size of the data exposed by this dataset.
-
- :rtype: int
- """
- if isinstance(self._get_data(), numpy.ndarray):
- return len(self._get_data())
- else:
- # It is returned as float64 1.0 by h5py
- raise TypeError("Attempt to take len() of scalar dataset")
-
- def __getitem__(self, item):
- """Returns the slice of the data exposed by this dataset.
-
- :rtype: numpy.ndarray
- """
- if not isinstance(self._get_data(), numpy.ndarray):
- if item == Ellipsis:
- return numpy.array(self._get_data())
- elif item == tuple():
- return self._get_data()
- else:
- raise ValueError("Scalar can only be reached with an ellipsis or an empty tuple")
- return self._get_data().__getitem__(item)
-
- def __str__(self):
- basename = self.name.split("/")[-1]
- return '<HDF5-like dataset "%s": shape %s, type "%s">' % \
- (basename, self.shape, self.dtype.str)
-
- def __getslice__(self, i, j):
- """Returns the slice of the data exposed by this dataset.
-
- Deprecated but still in use for python 2.7
-
- :rtype: numpy.ndarray
- """
- return self.__getitem__(slice(i, j, None))
-
- @property
- def value(self):
- """Returns the data exposed by this dataset.
-
- Deprecated by h5py. It is prefered to use indexing `[()]`.
-
- :rtype: numpy.ndarray
- """
- return self._get_data()
-
- @property
- def compression(self):
- """Returns compression as provided by `h5py.Dataset`.
-
- There is no compression."""
- return None
-
- @property
- def compression_opts(self):
- """Returns compression options as provided by `h5py.Dataset`.
-
- There is no compression."""
- return None
-
- @property
- def chunks(self):
- """Returns chunks as provided by `h5py.Dataset`.
-
- There is no chunks."""
- return None
-
- def __array__(self, dtype=None):
- # Special case for (0,)*-shape datasets
- if numpy.product(self.shape) == 0:
- return self[()]
- else:
- return numpy.array(self[...], dtype=self.dtype if dtype is None else dtype)
-
- def __iter__(self):
- """Iterate over the first axis. TypeError if scalar."""
- if len(self.shape) == 0:
- raise TypeError("Can't iterate over a scalar dataset")
- return self._get_data().__iter__()
-
- # make comparisons and operations on the data
- def __eq__(self, other):
- """When comparing datasets, compare the actual data."""
- if utils.is_dataset(other):
- return self[()] == other[()]
- return self[()] == other
-
- def __add__(self, other):
- return self[()] + other
-
- def __radd__(self, other):
- return other + self[()]
-
- def __sub__(self, other):
- return self[()] - other
-
- def __rsub__(self, other):
- return other - self[()]
-
- def __mul__(self, other):
- return self[()] * other
-
- def __rmul__(self, other):
- return other * self[()]
-
- def __truediv__(self, other):
- return self[()] / other
-
- def __rtruediv__(self, other):
- return other / self[()]
-
- def __floordiv__(self, other):
- return self[()] // other
-
- def __rfloordiv__(self, other):
- return other // self[()]
-
- def __neg__(self):
- return -self[()]
-
- def __abs__(self):
- return abs(self[()])
-
- def __float__(self):
- return float(self[()])
-
- def __int__(self):
- return int(self[()])
-
- def __bool__(self):
- if self[()]:
- return True
- return False
-
- def __nonzero__(self):
- # python 2
- return self.__bool__()
-
- def __ne__(self, other):
- if utils.is_dataset(other):
- return self[()] != other[()]
- else:
- return self[()] != other
-
- def __lt__(self, other):
- if utils.is_dataset(other):
- return self[()] < other[()]
- else:
- return self[()] < other
-
- def __le__(self, other):
- if utils.is_dataset(other):
- return self[()] <= other[()]
- else:
- return self[()] <= other
-
- def __gt__(self, other):
- if utils.is_dataset(other):
- return self[()] > other[()]
- else:
- return self[()] > other
-
- def __ge__(self, other):
- if utils.is_dataset(other):
- return self[()] >= other[()]
- else:
- return self[()] >= other
-
- def __getattr__(self, item):
- """Proxy to underlying numpy array methods.
- """
- data = self._get_data()
- if hasattr(data, item):
- return getattr(data, item)
-
- raise AttributeError("Dataset has no attribute %s" % item)
-
-
-class DatasetProxy(Dataset):
- """Virtual dataset providing content of another dataset"""
-
- def __init__(self, name, target, parent=None):
- Dataset.__init__(self, name, data=None, parent=parent)
- if not utils.is_dataset(target):
- raise TypeError("A Dataset is expected but %s found", target.__class__)
- self.__target = target
-
- @property
- def shape(self):
- return self.__target.shape
-
- @property
- def size(self):
- return self.__target.size
-
- @property
- def dtype(self):
- return self.__target.dtype
-
- def _get_data(self):
- return self.__target[...]
-
- @property
- def attrs(self):
- return self.__target.attrs
-
-
-class _LinkToDataset(Dataset):
- """Virtual dataset providing link to another dataset"""
-
- def __init__(self, name, target, parent=None):
- Dataset.__init__(self, name, data=None, parent=parent)
- self.__target = target
-
- def _get_data(self):
- return self.__target._get_data()
-
- @property
- def attrs(self):
- return self.__target.attrs
-
-
-class LazyLoadableDataset(Dataset):
- """Abstract dataset which provides a lazy loading of the data.
-
- The class has to be inherited and the :meth:`_create_data` method has to be
- implemented to return the numpy data exposed by the dataset. This factory
- method is only called once, when the data is needed.
- """
-
- def __init__(self, name, parent=None, attrs=None):
- super(LazyLoadableDataset, self).__init__(name, None, parent, attrs=attrs)
- self._is_initialized = False
-
- def _create_data(self):
- """
- Factory to create the data exposed by the dataset when it is needed.
-
- It has to be implemented for the class to work.
-
- :rtype: numpy.ndarray
- """
- raise NotImplementedError()
-
- def _get_data(self):
- """Returns the data exposed by the dataset.
-
- Overwrite Dataset method :meth:`_get_data` to implement the lazy
- loading feature.
-
- :rtype: numpy.ndarray
- """
- if not self._is_initialized:
- data = self._create_data()
- # is_initialized before set_data to avoid infinit initialization
- # is case of wrong check of the data
- self._is_initialized = True
- self._set_data(data)
- return super(LazyLoadableDataset, self)._get_data()
-
-
-class SoftLink(Node):
- """This class is a tree node that mimics a *h5py.Softlink*.
-
- In this implementation, the path to the target must be absolute.
- """
- def __init__(self, name, path, parent=None):
- assert str(path).startswith("/") # TODO: h5py also allows a relative path
-
- Node.__init__(self, name, parent)
-
- # attr target defined for spech5 backward compatibility
- self.target = str(path)
-
- @property
- def h5_class(self):
- """Returns the HDF5 class which is mimicked by this class.
-
- :rtype: H5Type
- """
- return utils.H5Type.SOFT_LINK
-
- @property
- def path(self):
- """Soft link value. Not guaranteed to be a valid path."""
- return self.target
-
-
-class Group(Node):
- """This class mimics a `h5py.Group`."""
-
- def __init__(self, name, parent=None, attrs=None):
- Node.__init__(self, name, parent, attrs=attrs)
- self.__items = collections.OrderedDict()
-
- def _get_items(self):
- """Returns the child items as a name-node dictionary.
-
- :rtype: dict
- """
- return self.__items
-
- def add_node(self, node):
- """Add a child to this group.
-
- :param Node node: Child to add to this group
- """
- self._get_items()[node.basename] = node
- node._set_parent(self)
-
- @property
- def h5_class(self):
- """Returns the HDF5 class which is mimicked by this class.
-
- :rtype: H5Type
- """
- return utils.H5Type.GROUP
-
- def _get(self, name, getlink):
- """If getlink is True and name points to an existing SoftLink, this
- SoftLink is returned. In all other situations, we try to return a
- Group or Dataset, or we raise a KeyError if we fail."""
- if "/" not in name:
- result = self._get_items()[name]
- elif name.startswith("/"):
- root = self.file
- if name == "/":
- return root
- result = root._get(name[1:], getlink)
- else:
- path = name.split("/")
- result = self
- for item_name in path:
- if isinstance(result, SoftLink):
- # traverse links
- l_name, l_target = result.name, result.path
- result = result.file.get(l_target)
- if result is None:
- raise KeyError(
- "Unable to open object (broken SoftLink %s -> %s)" %
- (l_name, l_target))
- if not item_name:
- # trailing "/" in name (legal for accessing Groups only)
- if isinstance(result, Group):
- continue
- if not isinstance(result, Group):
- raise KeyError("Unable to open object (Component not found)")
- result = result._get_items()[item_name]
-
- if isinstance(result, SoftLink) and not getlink:
- link = result
- target = result.file.get(link.path)
- if result is None:
- msg = "Unable to open object (broken SoftLink %s -> %s)"
- raise KeyError(msg % (link.name, link.path))
- # Convert SoftLink into typed group/dataset
- if isinstance(target, Group):
- result = _LinkToGroup(name=link.basename, target=target, parent=link.parent)
- elif isinstance(target, Dataset):
- result = _LinkToDataset(name=link.basename, target=target, parent=link.parent)
- else:
- raise TypeError("Unexpected target type %s" % type(target))
-
- return result
-
- def get(self, name, default=None, getclass=False, getlink=False):
- """Retrieve an item or other information.
-
- If getlink only is true, the returned value is always `h5py.HardLink`,
- because this implementation do not use links. Like the original
- implementation.
-
- :param str name: name of the item
- :param object default: default value returned if the name is not found
- :param bool getclass: if true, the returned object is the class of the object found
- :param bool getlink: if true, links object are returned instead of the target
- :return: An object, else None
- :rtype: object
- """
- if name not in self:
- return default
-
- node = self._get(name, getlink=True)
- if isinstance(node, SoftLink) and not getlink:
- # get target
- try:
- node = self._get(name, getlink=False)
- except KeyError:
- return default
- elif not isinstance(node, SoftLink) and getlink:
- # ExternalLink objects don't exist in silx, so it must be a HardLink
- node = h5py.HardLink()
-
- if getclass:
- obj = utils.get_h5py_class(node)
- if obj is None:
- obj = node.__class__
- else:
- obj = node
- return obj
-
- def __setitem__(self, name, obj):
- """Add an object to the group.
-
- :param str name: Location on the group to store the object.
- This path name must not exists.
- :param object obj: Object to store on the file. According to the type,
- the behaviour will not be the same.
-
- - `commonh5.SoftLink`: Create the corresponding link.
- - `numpy.ndarray`: The array is converted to a dataset object.
- - `commonh5.Node`: A hard link should be created pointing to the
- given object. This implementation uses a soft link.
- If the node do not have parent it is connected to the tree
- without using a link (that's a hard link behaviour).
- - other object: Convert first the object with ndarray and then
- store it. ValueError if the resulting array dtype is not
- supported.
- """
- if name in self:
- # From the h5py API
- raise RuntimeError("Unable to create link (name already exists)")
-
- elements = name.rsplit("/", 1)
- if len(elements) == 1:
- parent = self
- basename = elements[0]
- else:
- group_path, basename = elements
- if group_path in self:
- parent = self[group_path]
- else:
- parent = self.create_group(group_path)
-
- if isinstance(obj, SoftLink):
- obj._set_basename(basename)
- node = obj
- elif isinstance(obj, Node):
- if obj.parent is None:
- obj._set_basename(basename)
- node = obj
- else:
- node = SoftLink(basename, obj.name)
- elif isinstance(obj, numpy.dtype):
- node = Dataset(basename, data=obj)
- elif isinstance(obj, numpy.ndarray):
- node = Dataset(basename, data=obj)
- else:
- data = numpy.array(obj)
- try:
- node = Dataset(basename, data=data)
- except TypeError as e:
- raise ValueError(e.args[0])
-
- parent.add_node(node)
-
- def __getitem__(self, name):
- """Return a child from his name.
-
- :param str name: name of a member or a path throug members using '/'
- separator. A '/' as a prefix access to the root item of the tree.
- :rtype: Node
- """
- if name is None or name == "":
- raise ValueError("No name")
- return self._get(name, getlink=False)
-
- def __contains__(self, name):
- """Returns true if name is an existing child of this group.
-
- :rtype: bool
- """
- if "/" not in name:
- return name in self._get_items()
-
- if name.startswith("/"):
- # h5py allows to access any valid full path from any group
- node = self.file
- else:
- node = self
-
- name = name.lstrip("/")
- basenames = name.split("/")
- for basename in basenames:
- if basename.strip() == "":
- # presence of a trailing "/" in name
- # (OK for groups, not for datasets)
- if isinstance(node, SoftLink):
- # traverse links
- node = node.file.get(node.path, getlink=False)
- if node is None:
- # broken link
- return False
- if utils.is_dataset(node):
- return False
- continue
- if basename not in node._get_items():
- return False
- node = node[basename]
-
- return True
-
- def __len__(self):
- """Returns the number of children contained in this group.
-
- :rtype: int
- """
- return len(self._get_items())
-
- def __iter__(self):
- """Iterate over member names"""
- for x in self._get_items().__iter__():
- yield x
-
- if six.PY2:
- def keys(self):
- """Returns a list of the children's names."""
- return self._get_items().keys()
-
- def values(self):
- """Returns a list of the children nodes (groups and datasets).
-
- .. versionadded:: 0.6
- """
- return self._get_items().values()
-
- def items(self):
- """Returns a list of tuples containing (name, node) pairs.
- """
- return self._get_items().items()
-
- else:
- def keys(self):
- """Returns an iterator over the children's names in a group."""
- return self._get_items().keys()
-
- def values(self):
- """Returns an iterator over the children nodes (groups and datasets)
- in a group.
-
- .. versionadded:: 0.6
- """
- return self._get_items().values()
-
- def items(self):
- """Returns items iterator containing name-node mapping.
-
- :rtype: iterator
- """
- return self._get_items().items()
-
- def visit(self, func, visit_links=False):
- """Recursively visit all names in this group and subgroups.
- See the documentation for `h5py.Group.visit` for more help.
-
- :param func: Callable (function, method or callable object)
- :type func: callable
- """
- origin_name = self.name
- return self._visit(func, origin_name, visit_links)
-
- def visititems(self, func, visit_links=False):
- """Recursively visit names and objects in this group.
- See the documentation for `h5py.Group.visititems` for more help.
-
- :param func: Callable (function, method or callable object)
- :type func: callable
- :param bool visit_links: If *False*, ignore links. If *True*,
- call `func(name)` for links and recurse into target groups.
- """
- origin_name = self.name
- return self._visit(func, origin_name, visit_links,
- visititems=True)
-
- def _visit(self, func, origin_name,
- visit_links=False, visititems=False):
- """
-
- :param origin_name: name of first group that initiated the recursion
- This is used to compute the relative path from each item's
- absolute path.
- """
- for member in self.values():
- ret = None
- if not isinstance(member, SoftLink) or visit_links:
- relative_name = member.name[len(origin_name):]
- # remove leading slash and unnecessary trailing slash
- relative_name = relative_name.strip("/")
- if visititems:
- ret = func(relative_name, member)
- else:
- ret = func(relative_name)
- if ret is not None:
- return ret
- if isinstance(member, Group):
- member._visit(func, origin_name, visit_links, visititems)
-
- def create_group(self, name):
- """Create and return a new subgroup.
-
- Name may be absolute or relative. Fails if the target name already
- exists.
-
- :param str name: Name of the new group
- """
- if not self._is_editable():
- raise RuntimeError("File is not editable")
- if name in self:
- raise ValueError("Unable to create group (name already exists)")
-
- if name.startswith("/"):
- name = name[1:]
- return self.file.create_group(name)
-
- elements = name.split('/')
- group = self
- for basename in elements:
- if basename in group:
- group = group[basename]
- if not isinstance(group, Group):
- raise RuntimeError("Unable to create group (group parent is missing")
- else:
- node = Group(basename)
- group.add_node(node)
- group = node
- return group
-
- def create_dataset(self, name, shape=None, dtype=None, data=None, **kwds):
- """Create and return a sub dataset.
-
- :param str name: Name of the dataset.
- :param shape: Dataset shape. Use "()" for scalar datasets.
- Required if "data" isn't provided.
- :param dtype: Numpy dtype or string.
- If omitted, dtype('f') will be used.
- Required if "data" isn't provided; otherwise, overrides data
- array's dtype.
- :param numpy.ndarray data: Provide data to initialize the dataset.
- If used, you can omit shape and dtype arguments.
- :param kwds: Extra arguments. Nothing yet supported.
- """
- if not self._is_editable():
- raise RuntimeError("File is not editable")
- if len(kwds) > 0:
- raise TypeError("Extra args provided, but nothing supported")
- if "/" in name:
- raise TypeError("Path are not supported")
- if data is None:
- if dtype is None:
- dtype = numpy.float
- data = numpy.empty(shape=shape, dtype=dtype)
- elif dtype is not None:
- data = data.astype(dtype)
- dataset = Dataset(name, data)
- self.add_node(dataset)
- return dataset
-
-
-class _LinkToGroup(Group):
- """Virtual group providing link to another group"""
-
- def __init__(self, name, target, parent=None):
- Group.__init__(self, name, parent=parent)
- self.__target = target
-
- def _get_items(self):
- return self.__target._get_items()
-
- @property
- def attrs(self):
- return self.__target.attrs
-
-
-class LazyLoadableGroup(Group):
- """Abstract group which provides a lazy loading of the child.
-
- The class has to be inherited and the :meth:`_create_child` method has
- to be implemented to add (:meth:`_add_node`) all children. This factory
- is only called once, when children are needed.
- """
-
- def __init__(self, name, parent=None, attrs=None):
- Group.__init__(self, name, parent, attrs)
- self.__is_initialized = False
-
- def _get_items(self):
- """Returns the internal structure which contains the children.
-
- It overwrite method :meth:`_get_items` to implement the lazy
- loading feature.
-
- :rtype: dict
- """
- if not self.__is_initialized:
- self.__is_initialized = True
- self._create_child()
- return Group._get_items(self)
-
- def _create_child(self):
- """
- Factory to create the child contained by the group when it is needed.
-
- It has to be implemented to work.
- """
- raise NotImplementedError()
-
-
-class File(Group):
- """This class is the special :class:`Group` that is the root node
- of the tree structure. It mimics `h5py.File`."""
-
- def __init__(self, name=None, mode=None, attrs=None):
- """
- Constructor
-
- :param str name: File name if it exists
- :param str mode: Access mode
- - "r": Read-only. Methods :meth:`create_dataset` and
- :meth:`create_group` are locked.
- - "w": File is editable. Methods :meth:`create_dataset` and
- :meth:`create_group` are available.
- :param dict attrs: Default attributes
- """
- Group.__init__(self, name="", parent=None, attrs=attrs)
- self._file_name = name
- if mode is None:
- mode = "r"
- assert(mode in ["r", "w"])
- self._mode = mode
-
- @property
- def filename(self):
- return self._file_name
-
- @property
- def mode(self):
- return self._mode
-
- @property
- def h5_class(self):
- """Returns the :class:`h5py.File` class"""
- return utils.H5Type.FILE
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- self.close()
-
- def close(self):
- """Close the object, and free up associated resources.
- """
- # should be implemented in subclass
- pass