Writes a piece of data into an open HDF5 file.

    Low level function to read a Python type of the specified name from
    specified Group.

    .. versionchanged:: 0.2
       Added argument `dsetgrp`.

    Parameters
    ----------
    f : h5py.File
        The open HDF5 file.
    grp : h5py.Group or h5py.File
        The Group to read the data from.
    name : str
        The name of the data to read.
    options : hdf5storage.core.Options
        The options to use when reading.
    dsetgrp : h5py.Dataset or h5py.Group or None, optional
        The Dataset or Group object to read if that has already been
        obtained and thus should not be re-obtained (``None``
        otherwise). If given, overrides `grp` and `name`.

    Returns
    -------
    data
        The data named `name` in Group `grp`.

    Raises
    ------
    CantReadError
        If the data cannot be read successfully.

    See Also
    --------
    hdf5storage.read : Higher level version.
    write_data
    hdf5storage.Options

def read_data(f, grp, name, options, dsetgrp=None):
    """ Writes a piece of data into an open HDF5 file.

    Low level function to read a Python type of the specified name from
    specified Group.

    .. versionchanged:: 0.2
       Added argument `dsetgrp`.

    Parameters
    ----------
    f : h5py.File
        The open HDF5 file.
    grp : h5py.Group or h5py.File
        The Group to read the data from.
    name : str
        The name of the data to read.
    options : hdf5storage.core.Options
        The options to use when reading.
    dsetgrp : h5py.Dataset or h5py.Group or None, optional
        The Dataset or Group object to read if that has already been
        obtained and thus should not be re-obtained (``None``
        otherwise). If given, overrides `grp` and `name`.

    Returns
    -------
    data
        The data named `name` in Group `grp`.

    Raises
    ------
    CantReadError
        If the data cannot be read successfully.

    See Also
    --------
    hdf5storage.read : Higher level version.
    write_data
    hdf5storage.Options

    """
    if dsetgrp is None:
        # If name isn't found, return error.
        try:
            dsetgrp = grp[name]
        except:
            raise hdf5storage.exceptions.CantReadError( \
                'Could not find ' + posixpath.join(grp.name, name))

    # Get all attributes with values.
    if sys.hexversion >= 0x03000000:
        defaultfactory = type(None)
    else:
        defaultfactory = lambda : None
    attributes = collections.defaultdict(defaultfactory,
                                         dsetgrp.attrs.items())

    # Get the different attributes that can be used to identify they
    # type, which are the type string and the MATLAB class.
    type_string = convert_attribute_to_string(attributes['Python.Type'])
    matlab_class = convert_attribute_to_string( \
        attributes['MATLAB_class'])

    # If the type_string is present, get the marshaller for it. If it is
    # not, use the one for the matlab class if it is given. Otherwise,
    # use the fallback (NumpyScalarArrayMarshaller for both Datasets and
    # Groups). If calls to the marshaller collection to get the right
    # marshaller don't return one (return None), we also go to the
    # default). Also get whether we have the modules required to read it
    # accurately or not (approximately)

    m = None
    has_modules = False
    mc = options.marshaller_collection
    if type_string is not None:
        m, has_modules = mc.get_marshaller_for_type_string(type_string)
    elif matlab_class is not None:
        m, has_modules = \
            mc.get_marshaller_for_matlab_class(matlab_class)
    elif hasattr(dsetgrp, 'dtype'):
        # Numpy dataset
        m, has_modules = \
            mc.get_marshaller_for_type(dsetgrp.dtype.type)
    elif isinstance(dsetgrp, (h5py.Group, h5py.File)):
        # Groups and files are like Matlab struct
        m, has_modules = mc.get_marshaller_for_matlab_class('struct')

    if m is None:
        # use Numpy as a fallback
        m, has_modules = mc.get_marshaller_for_type(np.uint8)

    # If a marshaller was found, use it to write the data. Otherwise,
    # return an error.

    if m is not None:
        if has_modules:
            return m.read(f, dsetgrp, attributes, options)
        else:
            return m.read_approximate(f, dsetgrp, attributes, options)
    else:
        raise hdf5storage.exceptions.CantReadError('Could not read '
                                                   + dsetgrp.name)