diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-10-07 07:59:01 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-10-07 07:59:01 +0200 |
commit | bfa4dba15485b4192f8bbe13345e9658c97ecf76 (patch) | |
tree | fb9c6e5860881fbde902f7cbdbd41dc4a3a9fb5d /silx/io/utils.py | |
parent | f7bdc2acff3c13a6d632c28c4569690ab106eed7 (diff) |
New upstream version 0.6.0+dfsg
Diffstat (limited to 'silx/io/utils.py')
-rw-r--r-- | silx/io/utils.py | 141 |
1 files changed, 127 insertions, 14 deletions
diff --git a/silx/io/utils.py b/silx/io/utils.py index 2ab4496..361a28b 100644 --- a/silx/io/utils.py +++ b/silx/io/utils.py @@ -28,7 +28,9 @@ import os.path import sys import time import logging -from silx.utils.decorators import deprecated + +from silx.utils.deprecation import deprecated +from silx.utils.proxy import Proxy try: import h5py @@ -39,9 +41,9 @@ else: h5py_missing = False -__authors__ = ["P. Knobel"] +__authors__ = ["P. Knobel", "V. Valls"] __license__ = "MIT" -__date__ = "13/12/2016" +__date__ = "28/09/2017" logger = logging.getLogger(__name__) @@ -377,14 +379,17 @@ def h5ls(h5group, lvl=0): return h5repr -def open(filename): # pylint:disable=redefined-builtin +def _open(filename): """ Load a file as an `h5py.File`-like object. Format supported: - h5 files, if `h5py` module is installed - - Spec files if `SpecFile` module is installed - - a set of raster image formats (tiff, edf...) if `fabio` is installed + - SPEC files exposed as a NeXus layout + - raster files exposed as a NeXus layout (if `fabio` is installed) + - Numpy files ('npy' and 'npz' files) + + The file is opened in read-only mode. :param str filename: A filename :raises: IOError if the file can't be loaded as an h5py.File like object @@ -393,29 +398,124 @@ def open(filename): # pylint:disable=redefined-builtin if not os.path.isfile(filename): raise IOError("Filename '%s' must be a file path" % filename) + debugging_info = [] + + _, extension = os.path.splitext(filename) + if not h5py_missing: if h5py.is_hdf5(filename): - return h5py.File(filename) + return h5py.File(filename, "r") + + if extension in [".npz", ".npy"]: + try: + from . import rawh5 + return rawh5.NumpyFile(filename) + except (IOError, ValueError) as e: + debugging_info.append((sys.exc_info(), + "File '%s' can't be read as a numpy file." % filename)) try: from . import fabioh5 return fabioh5.File(filename) except ImportError: - logger.debug("fabioh5 can't be loaded.", exc_info=True) + debugging_info.append((sys.exc_info(), "fabioh5 can't be loaded.")) except Exception: - logger.debug("File '%s' can't be read as fabio file.", filename, exc_info=True) + debugging_info.append((sys.exc_info(), + "File '%s' can't be read as fabio file." % filename)) try: from . import spech5 return spech5.SpecH5(filename) except ImportError: - logger.debug("spech5 can't be loaded.", exc_info=True) + debugging_info.append((sys.exc_info(), + "spech5 can't be loaded.")) except IOError: - logger.debug("File '%s' can't be read as spec file.", filename, exc_info=True) + debugging_info.append((sys.exc_info(), + "File '%s' can't be read as spec file." % filename)) + for exc_info, message in debugging_info: + logger.debug(message, exc_info=exc_info) raise IOError("File '%s' can't be read as HDF5" % filename) +class _MainNode(Proxy): + """A main node is a sub node of the HDF5 tree which is responsible of the + closure of the file. + + It is a proxy to the sub node, plus support context manager and `close` + method usually provided by `h5py.File`. + + :param h5_node: Target to the proxy. + :param h5_file: Main file. This object became the owner of this file. + """ + + def __init__(self, h5_node, h5_file): + super(_MainNode, self).__init__(h5_node) + self.__file = h5_file + self.__class = get_h5py_class(h5_node) + + @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`. + + :rtype: Class + """ + return self.__class + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def close(self): + """Close the file""" + self.__file.close() + self.__file = None + + +def open(filename): # pylint:disable=redefined-builtin + """ + Open a file as an `h5py`-like object. + + Format supported: + - h5 files, if `h5py` module is installed + - SPEC files exposed as a NeXus layout + - raster files exposed as a NeXus layout (if `fabio` is installed) + - Numpy files ('npy' and 'npz' files) + + The filename can be trailled an HDF5 path using the separator `::`. In this + case the object returned is a proxy to the target node, implementing the + `close` function and supporting `with` context. + + The file is opened in read-only mode. + + :param str filename: A filename which can containt an HDF5 path by using + `::` separator. + :raises: IOError if the file can't be loaded or path can't be found + :rtype: h5py-like node + """ + if "::" in filename: + filename, h5_path = filename.split("::") + else: + filename, h5_path = filename, "/" + + h5_file = _open(filename) + + if h5_path in ["/", ""]: + # Short cut + return h5_file + + if h5_path not in h5_file: + msg = "File '%s' do not contains path '%s'." % (filename, h5_path) + raise IOError(msg) + + node = h5_file[h5_path] + proxy = _MainNode(node, h5_file) + return proxy + + @deprecated def load(filename): """ @@ -447,7 +547,7 @@ def get_h5py_class(obj): """ if hasattr(obj, "h5py_class"): return obj.h5py_class - elif isinstance(obj, (h5py.File, h5py.Group, h5py.Dataset)): + elif isinstance(obj, (h5py.File, h5py.Group, h5py.Dataset, h5py.SoftLink)): return obj.__class__ else: return None @@ -467,7 +567,7 @@ def is_file(obj): def is_group(obj): """ - True is the object is an h5py.Group-like object. + True if the object is a h5py.Group-like object. :param obj: An object """ @@ -479,7 +579,7 @@ def is_group(obj): def is_dataset(obj): """ - True is the object is an h5py.Dataset-like object. + True if the object is a h5py.Dataset-like object. :param obj: An object """ @@ -489,6 +589,18 @@ def is_dataset(obj): return issubclass(class_, h5py.Dataset) +def is_softlink(obj): + """ + True if the object is a h5py.SoftLink-like object. + + :param obj: An object + """ + class_ = get_h5py_class(obj) + if class_ is None: + return False + return issubclass(class_, h5py.SoftLink) + + if h5py_missing: def raise_h5py_missing(obj): logger.error("get_h5py_class/is_file/is_group/is_dataset requires h5py") @@ -498,3 +610,4 @@ if h5py_missing: is_file = raise_h5py_missing is_group = raise_h5py_missing is_dataset = raise_h5py_missing + is_softlink = raise_h5py_missing |