Source code for pyiron_base.storage.hdfstub

"""
Convenience class to lazily read values from HDF.
"""

# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.
from typing import Any, Callable, Type

from pyiron_base.storage.hdfio import BaseHDFio

__author__ = "Marvin Poul"
__copyright__ = (
    "Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH - "
    "Computational Materials Design (CM) Department"
)
__version__ = "1.0"
__maintainer__ = "Marvin Poul"
__email__ = "poul@mpie.de"
__status__ = "production"
__date__ = "Apr 26, 2021"


[docs] class HDFStub: """ Provides lazy loading of data from HDF. Instead of accessing an HDF group directly >>> hdf[group_name] ... you can wrap this with this class >>> stub = HDFStub(hdf, group_name) and then later perform this lookup with :method:`.load` >>> stub.load() == hdf[group_name] True For simple datatypes there's not a big advantages to this, but :class:`.DataContainer` uses this to load its contents lazily and ensure that nested containers are also lazily loaded. This is done by customizing what happend on :method:`.load` via :method:`.register`. This class method adds a callback to the class that will be called when the specified type name is found in the hdf group that is to be loaded. >>> hdf['mytype/NAME'] MyType >>> hdf['mytype/TYPE'] <class 'my.module.MyType'> >>> HDFStub.register(MyType, lambda hdf, group: print(42) or hdf[group].to_object()) >>> my = HDFStub(hdf, 'mytype').load() 42 >>> my MyType(...) This is intended to allow classes that want to be lazily loaded in a certain way to customize what arguments they pass `to_object()` (and therefore to their own initializers). """ _load_functions: dict[str, Callable[[Any, str], Any]] = {}
[docs] def __init__(self, hdf: "BaseHDFio", group_name: str) -> None: """ Create new stub. The given hdf object is copied, so that calls to its :meth:`ProjectHDFio.open` and :meth:`.ProjectHDFio.close` between this initialization and later calls to :meth:.load` do not change the location this stub is pointing at. Args: hdf (BaseHDFio): hdf object to load from group_name (str): node or group name to load from the hdf object """ self._hdf = hdf.copy() self._group_name = group_name
[docs] @classmethod def register(cls, type: Type, load: Callable[[Any, str], Any]) -> None: """ Register call back for a new type. Args: type (Type): class to be registered load (Callable[[Any, str], Any]): callback that is called on :method:`.load` when the type matches `type_name`, must accept `hdf` and `group_name` corresponding to the init parameters of this class and return (lazily) loaded object """ cls._load_functions[str(type)] = load
[docs] def load(self) -> Any: """ Read value from HDF. If `group_name` is a node in HDF, simply its value will be returned. If it is a group in HDF and the 'NAME' node matches any of the types registered with :method:`.register`, it will be loaded with the provided callback. Otherwise it will be loaded with :method:`.ProjectHDFio.to_object()`. """ if ( self._group_name in self._hdf.list_nodes() or "TYPE" not in self._hdf[self._group_name].list_nodes() ): return self._hdf[self._group_name] load = self._load_functions.get( self._hdf[self._group_name]["TYPE"], lambda h, g: to_object(hdf_group=h[g]) ) return load(self._hdf, self._group_name)
def __repr__(self) -> str: """ Return a string representation of the object. Returns: str: The string representation of the object. """ return f"{self.__class__.__name__}({self._hdf}, {self._group_name})"
[docs] def to_object(hdf_group: Any) -> Any: """ Convert HDF group to object. Args: hdf_group (Any): HDF group to convert. Returns: Any: Converted object. """ if isinstance(hdf_group, BaseHDFio): return hdf_group.to_object() else: return hdf_group