diff options
Diffstat (limited to 'silx/gui/hdf5/_utils.py')
-rw-r--r-- | silx/gui/hdf5/_utils.py | 184 |
1 files changed, 163 insertions, 21 deletions
diff --git a/silx/gui/hdf5/_utils.py b/silx/gui/hdf5/_utils.py index af9c79f..048aa20 100644 --- a/silx/gui/hdf5/_utils.py +++ b/silx/gui/hdf5/_utils.py @@ -28,11 +28,10 @@ package `silx.gui.hdf5` package. __authors__ = ["V. Valls"] __license__ = "MIT" -__date__ = "26/04/2017" +__date__ = "29/09/2017" import logging -import numpy from .. import qt import silx.io.utils from silx.utils.html import escape @@ -138,10 +137,61 @@ class H5Node(object): :param Hdf5Item h5py_item: An Hdf5Item """ self.__h5py_object = h5py_item.obj + self.__h5py_target = None self.__h5py_item = h5py_item def __getattr__(self, name): - return object.__getattribute__(self.__h5py_object, name) + if hasattr(self.__h5py_object, name): + attr = getattr(self.__h5py_object, name) + return attr + raise AttributeError("H5Node has no attribute %s" % name) + + def __get_target(self, obj): + """ + Return the actual physical target of the provided object. + + Objects can contains links in the middle of the path, this function + check each groups and remove this prefix in case of the link by the + link of the path. + + :param obj: A valid h5py object (File, group or dataset) + :type obj: h5py.Dataset or h5py.Group or h5py.File + :rtype: h5py.Dataset or h5py.Group or h5py.File + """ + elements = obj.name.split("/") + if obj.name == "/": + return obj + elif obj.name.startswith("/"): + elements.pop(0) + path = "" + while len(elements) > 0: + e = elements.pop(0) + path = path + "/" + e + link = obj.parent.get(path, getlink=True) + + if isinstance(link, h5py.ExternalLink): + subpath = "/".join(elements) + external_obj = obj.parent.get(self.basename + "/" + subpath) + return self.__get_target(external_obj) + elif silx.io.utils.is_softlink(link): + # Restart from this stat + path = "" + root_elements = link.path.split("/") + if link.path == "/": + root_elements = [] + elif link.path.startswith("/"): + root_elements.pop(0) + for name in reversed(root_elements): + elements.insert(0, name) + + return obj.file[path] + + @property + def h5py_target(self): + if self.__h5py_target is not None: + return self.__h5py_target + self.__h5py_target = self.__get_target(self.__h5py_object) + return self.__h5py_target @property def h5py_object(self): @@ -170,8 +220,18 @@ class H5Node(object): return self.__h5py_object.name.split("/")[-1] @property + def is_broken(self): + """Returns true if the node is a broken link. + + :rtype: bool + """ + if self.__h5py_item is None: + raise RuntimeError("h5py_item is not defined") + return self.__h5py_item.isBrokenObj() + + @property def local_name(self): - """Returns the local path of this h5py node. + """Returns the path from the master file root to this node. For links, this path is not equal to the h5py one. @@ -183,34 +243,46 @@ class H5Node(object): result = [] item = self.__h5py_item while item is not None: - if issubclass(item.h5pyClass, h5py.File): + # stop before the root item (item without parent) + if item.parent.parent is None: + name = item.obj.name + if name != "/": + result.append(item.obj.name) break - result.append(item.basename) + else: + result.append(item.basename) item = item.parent if item is None: raise RuntimeError("The item does not have parent holding h5py.File") if result == []: return "/" - result.append("") + if not result[-1].startswith("/"): + result.append("") result.reverse() - return "/".join(result) + name = "/".join(result) + return name - def __file_item(self): - """Returns the parent item holding the :class:`h5py.File` object + def __get_local_file(self): + """Returns the file of the root of this tree :rtype: h5py.File - :raises RuntimeException: If no file are found """ item = self.__h5py_item - while item is not None: - if issubclass(item.h5pyClass, h5py.File): - return item + while item.parent.parent is not None: + class_ = item.h5pyClass + if class_ is not None and issubclass(class_, h5py.File): + break item = item.parent - raise RuntimeError("The item does not have parent holding h5py.File") + + class_ = item.h5pyClass + if class_ is not None and issubclass(class_, h5py.File): + return item.obj + else: + return item.obj.file @property def local_file(self): - """Returns the local :class:`h5py.File` object. + """Returns the master file in which is this node. For path containing external links, this file is not equal to the h5py one. @@ -218,12 +290,11 @@ class H5Node(object): :rtype: h5py.File :raises RuntimeException: If no file are found """ - item = self.__file_item() - return item.obj + return self.__get_local_file() @property def local_filename(self): - """Returns the local filename of the h5py node. + """Returns the filename from the master file of this node. For path containing external links, this path is not equal to the filename provided by h5py. @@ -235,13 +306,84 @@ class H5Node(object): @property def local_basename(self): - """Returns the local filename of the h5py node. + """Returns the basename from the master file root to this node. For path containing links, this basename can be different than the basename provided by h5py. :rtype: str """ - if issubclass(self.__h5py_item.h5pyClass, h5py.File): + class_ = self.__h5py_item.h5pyClass + if class_ is not None and issubclass(class_, h5py.File): return "" return self.__h5py_item.basename + + @property + def physical_file(self): + """Returns the physical file in which is this node. + + .. versionadded:: 0.6 + + :rtype: h5py.File + :raises RuntimeError: If no file are found + """ + if isinstance(self.__h5py_object, h5py.ExternalLink): + # It means the link is broken + raise RuntimeError("No file node found") + if isinstance(self.__h5py_object, h5py.SoftLink): + # It means the link is broken + return self.local_file + + physical_obj = self.h5py_target + return physical_obj.file + + @property + def physical_name(self): + """Returns the path from the location this h5py node is physically + stored. + + For broken links, this filename can be different from the + filename provided by h5py. + + :rtype: str + """ + if isinstance(self.__h5py_object, h5py.ExternalLink): + # It means the link is broken + return self.__h5py_object.path + if isinstance(self.__h5py_object, h5py.SoftLink): + # It means the link is broken + return self.__h5py_object.path + + physical_obj = self.h5py_target + return physical_obj.name + + @property + def physical_filename(self): + """Returns the filename from the location this h5py node is physically + stored. + + For broken links, this filename can be different from the + filename provided by h5py. + + :rtype: str + """ + if isinstance(self.__h5py_object, h5py.ExternalLink): + # It means the link is broken + return self.__h5py_object.filename + if isinstance(self.__h5py_object, h5py.SoftLink): + # It means the link is broken + return self.local_file.filename + + return self.physical_file.filename + + @property + def physical_basename(self): + """Returns the basename from the location this h5py node is physically + stored. + + For broken links, this basename can be different from the + basename provided by h5py. + + :rtype: str + """ + return self.physical_name.split("/")[-1] |