Source code for nxswriter.FElement

#!/usr/bin/env python
#   This file is part of nexdatas - Tango Server for NeXus data writer
#
#    Copyright (C) 2012-2017 DESY, Jan Kotanski <jkotan@mail.desy.de>
#
#    nexdatas is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    nexdatas is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with nexdatas.  If not, see <http://www.gnu.org/licenses/>.
#

""" Definitions of file tag evaluation classes """

import numpy
import sys

from .DataHolder import DataHolder
from .Element import Element
from .Types import NTP
from .Errors import (XMLSettingSyntaxError)


[docs]class FElement(Element): """ NeXuS runnable tag element tag element corresponding to one of H5 objects """ def __init__(self, name, attrs, last, h5object=None, streams=None): """ constructor :param name: tag name :type name: :obj: `str` :param attrs: dictionary of the tag attributes :type attrs: :obj:`dict` <:obj:`str`, :obj:`str`> :param last: the last element from the stack :type last: :class:`nxswriter.Element.Element` :param h5object: H5 file object :type h5object: :class:`nxswriter.FileWriter.FTObject` :param streams: tango-like steamset class :type streams: :class:`StreamSet` or :class:`tango.LatestDeviceImpl` """ Element.__init__(self, name, attrs, last, streams=streams) #: (:class:`nxswriter.FileWriter.FTObject`) stored H5 file object self.h5Object = h5object #: (:class:`nxswriter.DataSources.DataSource`) data source self.source = None #: (:obj:`str`) notification of error in the run method self.error = None #: (:obj:`float`) runtime self.runtime = 0 #: (:obj:`bool`) flag for devices for which is allowed to failed self.canfail = False #: (:obj:`bool`) scalar type self._scalar = False
[docs] def run(self): """ runner :brief: During its thread run it fetches the data from the source """ if self.source: self.source.getData()
@classmethod def _reshape(cls, dsShape, rank, extends, extraD, exDim): """ recalculates shape :param dsShape: origin shape of the object :type dsShape: :obj:`list` <:obj:`int` > :param rank: rank of the object :type rank: : obj:`str` or obj:`int` :param extends: If True extends the shape up to rank value :type extends: :obj:`bool` :param exDim: grows growing dimension + 1 :type exDim: :obj:`int` :param extraD: True if the object grows :type extraD: :obj:`bool` :returns: shape of the h5 field :rtype: :obj:`list` <:obj:`int` > """ shape = [] if dsShape: for s in dsShape: if s and extends: shape.append(s) elif not extends and s and s > 0: shape.append(s) while extends and len(shape) < int(rank): shape.append(0) if extraD: shape.insert(exDim - 1, 0) return shape def __fetchShape(self, value, rank): """ fetches shape from value and rank :param rank: rank of the object :type rank: : obj:`str` or obj:`int` :param value: value of the object :type value: : obj:`str` :returns: shape of the value :rtype: :obj:`list` <:obj:`int` > """ if not rank or int(rank) == 0: return [1] elif int(rank) == 1: spec = value.split() return [len(spec)] elif int(rank) == 2: lines = value.split("\n") image = [ln.split() for ln in lines] return [len(image), len(image[0])] else: if self._streams: self._streams.error( "FElement::__fetchShape() " "- Case with not supported rank = %s" % rank, std=False) raise XMLSettingSyntaxError( "Case with not supported rank = %s" % rank) @classmethod def _getExtra(cls, grows, extraD=False): """ provides growing dimension :param grows: growing dimension :type grows: :obj:`int` :param extraD: True if the object grows :type extraD: :obj:`bool` :returns: growing dimension :rtype: :obj:`int` """ if extraD: if grows and grows > 1: exDim = grows else: exDim = 1 else: exDim = 0 return exDim def _findShape(self, rank, lengths=None, extraD=False, grows=None, extends=False, checkData=False): """ creates shape object from rank and lengths variables raises XMLSettingSyntaxError: if shape cannot be found :param rank: rank of the object :type rank: :obj:`str` :param lengths: dictionary with dimensions as a string data , e.g. {"1":"34","2":"40"} :type lengths: :obj:`dict` <:obj:`str`, :obj:`str`> :param extraD: if the object grows :type extraD: :obj:`bool` :param grows: growing dimension :type grows: :obj:`int` :param extends: If True extends the shape up to rank value :type extends: :obj:`bool` :returns shape: of the object :rtype: :obj:`list` <:obj:`int` > """ self._scalar = False shape = [] exDim = self._getExtra(grows, extraD) if int(rank) > 0: try: for i in range(int(rank)): si = str(i + 1) if lengths and si in lengths.keys() \ and lengths[si] is not None: if int(lengths[si]) > 0: shape.append(int(lengths[si])) else: raise XMLSettingSyntaxError( "Dimensions not defined") if len(shape) < int(rank): raise XMLSettingSyntaxError( "Too small dimension number") if extraD: shape.insert(exDim - 1, 0) except Exception: if sys.version_info > (3,): val = ("".join(self.content)).strip() else: val = ("".join(self.content)).strip().encode() found = False if checkData and self.source and self.source.isValid(): data = self.source.getData() if isinstance(data, dict): dh = DataHolder(streams=self._streams, **data) shape = self._reshape(dh.shape, rank, extends, extraD, exDim) if shape is not None: found = True if val and not found: shape = self.__fetchShape(val, rank) if shape is not None: found = True if not found: nm = "unnamed" if "name" in self._tagAttrs.keys(): nm = self._tagAttrs["name"] + " " raise XMLSettingSyntaxError( "Wrongly defined %s shape: %s" % (nm, str(self.source) if self.source else val)) elif extraD: shape = [0] if shape == []: self._scalar = True return shape
[docs] def setMessage(self, exceptionMessage=None): """ creates the error message :param exceptionMessage: additional message of exception :type exceptionMessage: :obj:`str` :returns: error message :rtype: :obj:`str` """ if hasattr(self.h5Object, "path"): name = self.h5Object.path elif hasattr(self.h5Object, "name"): name = self.h5Object.name else: name = "unnamed object" if self.source: dsource = str(self.source) else: dsource = "unknown datasource" message = ("Data for %s not found. DATASOURCE:%s" % (name, dsource), exceptionMessage) return message
[docs]class FElementWithAttr(FElement): """ NeXuS runnable tag element with attributes tag element corresponding to one of H5 objects with attributes """ def __init__(self, name, attrs, last, h5object=None, streams=None, reloadmode=False): """ constructor :param name: tag name :type name: :obj:`str` :param attrs: dictionary of the tag attributes :type attrs: :obj:`dict` <:obj:`str`, :obj:`str`> :param last: the last element from the stack :type last: :class:`nxswriter.Element.Element` :param h5object: H5 file object :type h5object: :class:`nxswriter.FileWriter.FTObject` :param streams: tango-like steamset class :type streams: :class:`StreamSet` or :class:`tango.LatestDeviceImpl` :param reloadmode: reload mode :type reloadmode: :obj:`bool` """ FElement.__init__(self, name, attrs, last, h5object, streams=streams) #: (:obj:`dict` <:obj:`str`, (:obj:`str`, :obj:`str`, :obj:`tuple`)> \ #: or :obj:`dict` <:obj:`str`, (:obj:`str`, :obj:`str`) > ) \ #: dictionary with attribures from sepatare attribute tags #: written as (name, value, shape) self.tagAttributes = {} #: (:obj:`dict` <:obj:`str`, h5object>) h5 instances self.__h5Instances = {} #: (:obj:`bool`) reload mode self._reloadmode = reloadmode def _setValue(self, rank, val): """ creates DataHolder with given rank and value :param rank: data rank :type rank: :obj:`int` :param val: data value :type value: any :returns: data holder :rtype: :class:`nxswriter.DataHolder.DataHolder` """ dh = None if not rank or rank == 0: dh = DataHolder("SCALAR", val, "DevString", [1, 0], streams=self._streams) elif rank == 1: spec = val.split() dh = DataHolder("SPECTRUM", spec, "DevString", [len(spec), 0], streams=self._streams) elif rank == 2: lines = str(val).split("\n") image = [ln.split() for ln in lines] dh = DataHolder("IMAGE", image, "DevString", [len(image), len(image[0])], streams=self._streams) else: if self._streams: self._streams.error( "FElement::_createAttributes() - " "Case with not supported rank = %s" % rank, std=False) raise XMLSettingSyntaxError( "Case with not supported rank = %s", rank) return dh def _createAttributes(self): """ creates h5 attributes :brief: It creates attributes instances which have been stored in tagAttributes dictionary """ for key in self.tagAttributes.keys(): if key not in ["name", "type"]: if sys.version_info > (3,): ekey = key else: ekey = key.encode() if self._reloadmode: fat = None for at in self.h5Object.attributes: if at.name == key: self.__h5Instances[ekey] = fat = at break if fat: fat = None continue if len(self.tagAttributes[key]) < 3: if sys.version_info > (3,): if ekey not in self.__h5Instances: self.__h5Instances[ekey] \ = self.h5Object.attributes.create( ekey, NTP.nTnp[self.tagAttributes[key][0]]) dh = DataHolder( "SCALAR", self.tagAttributes[key][1].strip(), "DevString", [1, 0], streams=self._streams) else: if sys.version_info > (3,): if ekey not in self.__h5Instances: self.__h5Instances[ekey] \ = self.h5Object.attributes.create( ekey, NTP.nTnp[self.tagAttributes[key][0]]) dh = DataHolder( "SCALAR", self.tagAttributes[key][1].strip(), "DevString", [1, 0], streams=self._streams) else: if ekey not in self.__h5Instances: self.__h5Instances[ekey] \ = self.h5Object.attributes.create( ekey, NTP.nTnp[ self.tagAttributes[key][ 0]].encode()) dh = DataHolder( "SCALAR", self.tagAttributes[key][1].strip().encode(), "DevString", [1, 0], streams=self._streams) self.__h5Instances[ekey][...] = \ dh.cast(self.__h5Instances[ekey].dtype) else: shape = self.tagAttributes[key][2] if sys.version_info > (3,): if ekey not in self.__h5Instances.keys(): self.__h5Instances[ekey] \ = self.h5Object.attributes.create( ekey, NTP.nTnp[self.tagAttributes[key][0]], shape) val = self.tagAttributes[key][1].strip() else: if ekey not in self.__h5Instances.keys(): self.__h5Instances[ekey] \ = self.h5Object.attributes.create( ekey, NTP.nTnp[ self.tagAttributes[key][0]].encode(), shape) val = self.tagAttributes[key][1].strip().encode() if val: rank = len(shape) hsp = self.__h5Instances[ekey].shape if (hsp == ()) and \ self.__h5Instances[ekey].dtype in ['string', 'str']: dh = self._setValue(0, val) else: dh = self._setValue(rank, val) self.__h5Instances[ekey][...] = dh.cast( self.__h5Instances[ekey].dtype) def _setAttributes(self, excluded=None): """ creates attributes :brief: It creates attributes in h5Object :param excluded: names of excluded attributes :type excluded: :obj:`list` <:obj:`str`> """ excluded = excluded or [] for key in self._tagAttrs.keys(): if key not in excluded: if sys.version_info > (3,): ekey = key else: ekey = key.encode() if key in NTP.aTn.keys(): if sys.version_info > (3,): h5att = self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTn[key]].encode(), overwrite=True ) else: h5att = self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTn[key]], overwrite=True ) if sys.version_info < (3,) and \ hasattr(self._tagAttrs[key], "encode"): try: h5att[...] = NTP.convert[ self.h5Object.attributes[ ekey].dtype ](self._tagAttrs[key].strip().encode()) except Exception: h5att[...] = self._tagAttrs[key].strip().encode() else: try: h5att[...] = NTP.convert[ self.h5Object.attributes[ ekey].dtype ](self._tagAttrs[key]) except Exception: h5att[...] = self._tagAttrs[key] elif key in NTP.aTnv.keys(): try: dh = self._setValue(1, self._tagAttrs[key]) shape = (dh.shape[0],) if sys.version_info > (3,): self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTnv[key]], shape, overwrite=True )[...] = dh.cast(NTP.nTnp[NTP.aTnv[key]]) else: self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTnv[key]].encode(), shape, overwrite=True )[...] = dh.cast(NTP.nTnp[NTP.aTnv[key]].encode()) except Exception: sarr = self._tagAttrs[key].split() if sys.version_info > (3,): self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTnv[key]], (len(sarr),), overwrite=True )[...] = numpy.array(sarr) else: self.h5Object.attributes.create( ekey, NTP.nTnp[NTP.aTnv[key]].encode(), (len(sarr),), overwrite=True )[...] = numpy.array(sarr) else: if sys.version_info > (3,): self.h5Object.attributes.create( ekey, "string", overwrite=True)[...] \ = self._tagAttrs[key].strip() else: self.h5Object.attributes.create( ekey, "string", overwrite=True)[...] \ = self._tagAttrs[key].strip().encode()
[docs] def h5Attribute(self, name): """ provides attribute h5 object :param name: attribute name :type name: :obj:`str` :returns: instance of the attribute object if created :rtype: :class:`nxswriter.FileWriter.FTObject` """ return self.__h5Instances.get(name)