summaryrefslogtreecommitdiff
path: root/silx/io/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/io/utils.py')
-rw-r--r--silx/io/utils.py141
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