def getValue(self): text = self.text() model_obj = self.getModelObj() if model_obj is None: return None val = self.validator() try: model_type = model_obj.type model_format = model_obj.data_format if model_type in [DataType.Integer, DataType.Float]: try: q = Quantity(text) # allow implicit units (assume wvalue.units implicitly) if q.dimensionless: q = Quantity(q.magnitude, val.units) return q except: return None elif model_type == DataType.Boolean: if model_format == DataFormat._0D: return bool(int(eval(text))) else: return numpy.array(eval(text), dtype=int).astype(bool) elif model_type == DataType.String: if model_format == DataFormat._0D: return str(text) else: return numpy.array(eval(text), dtype=str).tolist() elif model_type == DataType.Bytes: return bytes(text) else: raise TypeError('Unsupported model type "%s"', model_type) except Exception, e: self.warning('Cannot return value for "%s". Reason: %r', text, e) return None
def get_quantity(value, units=None, fmt=None): if value is None: return None res = Quantity(value, units=units) if fmt is not None: res.default_format = fmt + res.default_format return res
def _getDecodePyTangoAttr(self, attr_name, cfg): """Helper for decode the PyTango attribute infoex """ dev = PyTango.DeviceProxy(self.DEV_NAME) infoex = dev.get_attribute_config_ex(attr_name)[0] unit = unit_from_tango(infoex.unit) if cfg in ['range', 'alarms', 'warnings']: if cfg == 'range': low = infoex.min_value high = infoex.max_value elif cfg == 'alarms': low = infoex.alarms.min_alarm high = infoex.alarms.max_alarm elif cfg == 'warnings': low = infoex.alarms.min_warning high = infoex.alarms.max_warning if low == 'Not specified': low = '-inf' if high == 'Not specified': high = 'inf' return [Quantity(float(low), unit), Quantity(float(high), unit)] elif cfg == 'label': return infoex.label else: return None
def __init__(self, attr=None, pytango_dev_attr=None, config=None): # config parameter is kept for backwards compatibility only TaurusAttrValue.__init__(self) if config is not None: from taurus.core.util.log import deprecated deprecated(dep='"config" kwarg', alt='"attr"', rel='4.0') attr = config if attr is None: self._attrRef = None else: self._attrRef = weakref.proxy(attr) self.config = self._attrRef # bck-compat self._pytango_dev_attr = p = pytango_dev_attr if p is None: self._pytango_dev_attr = p = PyTango.DeviceAttribute() return if self._attrRef is None: return numerical = (PyTango.is_numerical_type(self._attrRef._tango_data_type, inc_array=True) or p.type == PyTango.CmdArgType.DevUChar) if p.has_failed: self.error = PyTango.DevFailed(*p.get_err_stack()) else: # spectra and images can be empty without failing if p.is_empty and self._attrRef.data_format != DataFormat._0D: dtype = FROM_TANGO_TO_NUMPY_TYPE.get( self._attrRef._tango_data_type) if self._attrRef.data_format == DataFormat._1D: shape = (0, ) elif self._attrRef.data_format == DataFormat._2D: shape = (0, 0) p.value = numpy.empty(shape, dtype=dtype) if not (numerical or self._attrRef.type == DataType.Boolean): # generate a nested empty list of given shape p.value = [] for _ in xrange(len(shape) - 1): p.value = [p.value] rvalue = p.value wvalue = p.w_value if numerical: units = self._attrRef._units if rvalue is not None: rvalue = Quantity(rvalue, units=units) if wvalue is not None: wvalue = Quantity(wvalue, units=units) elif isinstance(rvalue, PyTango._PyTango.DevState): rvalue = DevState[str(rvalue)] self.rvalue = rvalue self.wvalue = wvalue self.time = p.time # TODO: decode this into a TaurusTimeVal self.quality = quality_from_tango(p.quality)
def __decode_limit(self, l, h): units = self.__pv.units if l is None or numpy.isnan(l): l = None else: l = Quantity(l, units) if l is None or numpy.isnan(h): h = None else: h = Quantity(h, units) return [l, h]
def _value2Quantity(value, units): ''' Creates a Quantity from value and forces units if the vaule is unitless :param value: (int, float or str) a number or a string from which a quantity can be created :param units: (str or Pint units) Units to use if the value is unitless :return: (Quantity) ''' q = Quantity(value) if q.unitless: q = Quantity(q, units) return q
def applyTransformation(self): if self._transformation is None: return try: evaluator = self.getParentObj() rvalue = evaluator.eval(self._transformation) value_dimension = len(numpy.shape(rvalue)) value_dformat = DataFormat(value_dimension) self.data_format = value_dformat self.type = self._encodeType(rvalue, value_dformat) if self.type is None: raise TypeError("Unsupported returned type, %r" % rvalue) if self.type in [DataType.Integer, DataType.Float] and\ not isinstance(rvalue, Quantity): self.debug("Transformation converted to Quantity") rvalue = Quantity(rvalue) elif self.type == DataType.Boolean and value_dimension > 1: self.debug("Transformation converted to numpy.array") rvalue = numpy.array(rvalue) self._value.rvalue = rvalue self._value.time = TaurusTimeVal.now() self._value.quality = AttrQuality.ATTR_VALID except Exception, e: self._value.quality = AttrQuality.ATTR_INVALID msg = " the function '%s' could not be evaluated. Reason: %s" \ % (self._transformation, repr(e)) self.warning(msg)
def test1(): n = 'eval:@c=taurus.core.evaluation.test.res.mymod.MyClass(987)/c.foo' a = taurus.Attribute(n) print "READ 1: ", a.read() # print a.range print "WRITE+READ", a.write(Quantity(999, "m")) print "READ 2: ", a.read(cache=False)
def setAlarms(self, *limits): if isinstance(limits[0], list): limits = limits[0] low, high = limits low = Quantity(low) high = Quantity(high) TaurusAttribute.setAlarms(self, [low, high]) infoex = self._pytango_attrinfoex if low.magnitude != float("-inf"): infoex.alarms.min_alarm = str(low.to(self._units).magnitude) else: infoex.alarms.min_alarm = "Not specified" if high.magnitude != float("inf"): infoex.alarms.max_alarm = str(high.to(self._units).magnitude) else: infoex.alarms.max_alarm = "Not specified" self._applyConfig()
def setTop(self, top): """ Set maximum limit :param top: (Quantity or None) maximum acceptable value or None if it is not to be enforced """ self._top = Quantity(top)
def setBottom(self, bottom): """ Set minimum limit :param bottom: (Quantity or None) minimum acceptable value or None if it is not to be enforced """ self._bottom = Quantity(bottom)
def _getSingleStepQuantity(self): """ :return: (Quantity) returns a single step with the units of the current value """ value = self.getValue() if value is None: return None return Quantity(self.getSingleStep(), value.units)
def getValue(self): text = self.text() model_obj = self.getModelObj() if model_obj is None: return None val = self.validator() try: model_type = model_obj.type model_format = model_obj.data_format if model_type in [DataType.Integer, DataType.Float]: try: # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # workaround for https://github.com/hgrecco/pint/issues/614 text = text.lstrip('0') or '0' # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ q = Quantity(text) # allow implicit units (assume wvalue.units implicitly) if q.unitless: q = Quantity(q.magnitude, val.units) return q except: return None elif model_type == DataType.Boolean: if model_format == DataFormat._0D: return bool(int(eval(text))) else: return numpy.array(eval(text), dtype=int).astype(bool) elif model_type == DataType.String: if model_format == DataFormat._0D: return str(text) else: return numpy.array(eval(text), dtype=str).tolist() elif model_type == DataType.Bytes: return bytes(text) else: raise TypeError('Unsupported model type "%s"' % model_type) except Exception as e: msg = 'Cannot return value for "%s". Reason: %r' if text in (str(None), self.getNoneValue()): self.debug(msg, text, e) else: self.warning(msg, text, e) return None
def setAlarms(self, *limits): if isinstance(limits[0], list): limits = limits[0] low, high = limits low = Quantity(low) if low.unitless: low = Quantity(low.magnitude, self._units) high = Quantity(high) if high.unitless: high = Quantity(high.magnitude, self._units) TaurusAttribute.setAlarms(self, [low, high]) infoex = self._pytango_attrinfoex if low.magnitude != float('-inf'): infoex.alarms.min_alarm = str(low.to(self._units).magnitude) else: infoex.alarms.min_alarm = 'Not specified' if high.magnitude != float('inf'): infoex.alarms.max_alarm = str(high.to(self._units).magnitude) else: infoex.alarms.max_alarm = 'Not specified' self._applyConfig()
def getFreeSpace(self, dir): """ return free space (in bytes). (Recipe adapted from `http://stackoverflow.com/questions/51658`) """ if platform.system() == 'Windows': free_bytes = ctypes.c_ulonglong(0) ctypes.windll.kernel32.GetDiskFreeSpaceExW( ctypes.c_wchar_p(dir), None, None, ctypes.pointer(free_bytes)) ret = free_bytes.value else: s = os.statvfs(dir) ret = s.f_bsize * s.f_bavail return Quantity(ret, 'B')
def _validate(self, input, pos): """Reimplemented from :class:`QValidator` to validate if the input string is a representation of a quantity within the set bottom and top limits """ try: q = Quantity(input) except: return Qt.QValidator.Intermediate, input, pos if self._implicit_units is not None: if q.dimensionless: # "cast" to implicit units q = Quantity(q.magnitude, self.units) # check coherence with implicit units elif self._implicit_units.dimensionality != q.dimensionality: return Qt.QValidator.Intermediate, input, pos try: if self.bottom is not None and q < self.bottom: return Qt.QValidator.Intermediate, input, pos if self.top is not None and q > self.top: return Qt.QValidator.Intermediate, input, pos except DimensionalityError: return Qt.QValidator.Intermediate, input, pos return Qt.QValidator.Acceptable, input, pos
def decode(self, pv): """Decodes an epics PV object into a TaurusValue, and also updates other properties of the Attribute object """ attr_value = TaurusAttrValue() if not pv.connected: attr_value.error = ChannelAccessException('PV "%s" not connected' % pv.pvname) return attr_value v = pv.value # type try: self.type = Dbr2TaurusType[pv.ftype] except KeyError: raise ValueError('Unsupported epics type "%s"' % pv.type) # writable self.writable = pv.write_access # data_format if numpy.isscalar(v): self.data_format = DataFormat._0D else: self.data_format = DataFormat(len(numpy.shape(v))) # units and limits support if self.type in (DataType.Integer, DataType.Float): v = Quantity(v, pv.units) self._range = self.__decode_limit(pv.lower_ctrl_limit, pv.upper_ctrl_limit) self._alarm = self.__decode_limit(pv.lower_alarm_limit, pv.upper_alarm_limit) self._warning = self.__decode_limit(pv.lower_warning_limit, pv.upper_warning_limit) # rvalue attr_value.rvalue = v # wvalue if pv.write_access: attr_value.wvalue = v # time if pv.timestamp is None: attr_value.time = TaurusTimeVal.now() else: attr_value.time = TaurusTimeVal.fromtimestamp(pv.timestamp) # quality if pv.severity > 0: attr_value.quality = AttrQuality.ATTR_ALARM else: attr_value.quality = AttrQuality.ATTR_VALID return attr_value
def _set_value(self, value): '''for backwards compat with taurus < 4''' debug('Setting %r to %s' % (value, self.name)) if self.rvalue is None: # we do not have a previous rvalue import numpy dtype = numpy.array(value).dtype if numpy.issubdtype(dtype, int) or numpy.issubdtype(dtype, float): msg = 'Refusing to set ambiguous value (deprecated .value API)' raise ValueError(msg) else: self.rvalue = value elif hasattr(self.rvalue, 'units'): # we do have it and is a Quantity self.rvalue = Quantity(value, units=self.rvalue.units) else: # we do have a previous value and is not a quantity self.rvalue = value
def __assertValidValue(self, exp, got, msg): # if we are dealing with quantities, use the magnitude for comparing if isinstance(got, Quantity): got = got.to(Quantity(exp).units).magnitude if isinstance(exp, Quantity): exp = exp.magnitude try: # for those values that can be handled by numpy.allclose() chk = numpy.allclose(got, exp) except: # for the rest if isinstance(got, numpy.ndarray): got = got.tolist() chk = bool(got == exp) self.assertTrue(chk, msg)
def _decodeAttrInfoEx(self, pytango_attrinfoex=None): if pytango_attrinfoex is None: self._pytango_attrinfoex = PyTango.AttributeInfoEx() else: self._pytango_attrinfoex = i = pytango_attrinfoex self.writable = i.writable != PyTango.AttrWriteType.READ self._label = i.label self.data_format = data_format_from_tango(i.data_format) desc = description_from_tango(i.description) if desc != "": self._description = desc self.type = data_type_from_tango(i.data_type) ############################################################### # changed in taurus4: range, alarm and warning now return # quantities if appropriate units = self._unit_from_tango(i.unit) if PyTango.is_numerical_type(i.data_type, inc_array=True): Q_ = partial(quantity_from_tango_str, units=units, dtype=i.data_type) ninf, inf = float('-inf'), float('inf') min_value = Q_(i.min_value) or Quantity(ninf, units) max_value = Q_(i.max_value) or Quantity(inf, units) min_alarm = Q_(i.alarms.min_alarm) or Quantity(ninf, units) max_alarm = Q_(i.alarms.max_alarm) or Quantity(inf, units) min_warning = Q_(i.alarms.min_warning) or Quantity(ninf, units) max_warning = Q_(i.alarms.max_warning) or Quantity(inf, units) self._range = [min_value, max_value] self._warning = [min_warning, max_warning] self._alarm = [min_alarm, max_alarm] ############################################################### # The following members will be accessed via __getattr__ # self.standard_unit # self.display_unit # self.disp_level ############################################################### # Tango-specific extension of TaurusConfigValue self.display_level = display_level_from_tango(i.disp_level) self.tango_writable = i.writable self.max_dim = i.max_dim_x, i.max_dim_y ############################################################### fmt = standard_display_format_from_tango(i.data_type, i.format) self.format_spec = fmt.lstrip('%') # format specifier match = re.search("[^\.]*\.(?P<precision>[0-9]+)[eEfFgG%]", fmt) if match: self.precision = int(match.group(1)) # self._units and self._display_format is to be used by # TangoAttrValue for performance reasons. Do not rely on it in other # code self._units = units
def decode(self, data_frame): """ Decode the DataFrame to the corresponding python attribute :param data_frame: (pandas.DataFrame) :return: taurus valid type """ columns_count = len(data_frame.columns) attr_value_np = data_frame.as_matrix() if columns_count < 2: attr_value_np = attr_value_np.reshape(-1) if len(attr_value_np) == 1: self.data_format = DataFormat._0D attr_value_np = attr_value_np[0] else: self.data_format = DataFormat._1D else: if len(attr_value_np) == 1: self.data_format = DataFormat._1D attr_value_np = attr_value_np[0] else: self.data_format = DataFormat._2D try: npdtype = attr_value_np.dtype.kind self.type = self.npdtype2taurusdtype.get(npdtype) except AttributeError: self.type = self.npdtype2taurusdtype.get("O") if self.isNumeric(): value = Quantity(attr_value_np, units="dimensionless") elif self.data_format == DataFormat._0D: value = attr_value_np elif self.type is DataType.String or self.type is DataType.Boolean: value = attr_value_np.tolist() if self.type == self.npdtype2taurusdtype["O"] and columns_count >= 2: for row_idx in range(len(value)): for item_idx in range(len(value[row_idx])): value[row_idx][item_idx] = str(value[row_idx][item_idx]) return value
def setRange(self, *limits): if isinstance(limits[0], list): limits = limits[0] low, high = limits low = Quantity(low) high = Quantity(high) TaurusAttribute.setRange(self, [low, high]) infoex = self._pytango_attrinfoex if low.magnitude != float('-inf'): infoex.min_value = str(low.to(self._units).magnitude) else: infoex.min_value = 'Not specified' if high.magnitude != float('inf'): infoex.max_value = str(high.to(self._units).magnitude) else: infoex.max_value = 'Not specified' self._applyConfig()
def applyTransformation(self): if self._transformation is None: return try: evaluator = self.getParentObj() rvalue = evaluator.eval(self._transformation) # --------------------------------------------------------- # Workaround for https://github.com/hgrecco/pint/issues/509 # The numpy.shape method over a Quantity mutates # the type of its magnitude. # TODO: remove "if" when the bug is solved in pint if hasattr(rvalue, "magnitude"): value_dimension = len(numpy.shape(rvalue.magnitude)) else: value_dimension = len(numpy.shape(rvalue)) # --------------------------------------------------------- value_dformat = DataFormat(value_dimension) self.data_format = value_dformat self.type = self._encodeType(rvalue, value_dformat) if self.type is None: raise TypeError("Unsupported returned type, %r" % rvalue) if self.type in [DataType.Integer, DataType.Float] and\ not isinstance(rvalue, Quantity): self.debug("Transformation converted to Quantity") rvalue = Quantity(rvalue) elif self.type == DataType.Boolean and value_dimension > 1: self.debug("Transformation converted to numpy.array") rvalue = numpy.array(rvalue) self._value.rvalue = rvalue self._value.time = TaurusTimeVal.now() self._value.quality = AttrQuality.ATTR_VALID except Exception, e: self._value.quality = AttrQuality.ATTR_INVALID msg = " the function '%s' could not be evaluated. Reason: %s" \ % (self._transformation, repr(e)) self.warning(msg)
"""Test for epicsattributes...""" import os import numpy import subprocess from taurus.external import unittest from taurus.external.pint import Quantity import taurus from taurus.test import insertTest, getResourcePath from taurus.core.taurusbasetypes import DataType, AttrQuality, DataFormat from taurus.core.taurusbasetypes import TaurusAttrValue @insertTest(helper_name='write_read_attr', attrname='ca:test:a', setvalue=Quantity('1000mm'), expected=dict(rvalue=Quantity('1m'), type=DataType.Float, writable=True, data_format=DataFormat._0D, range=[Quantity('-10m'), Quantity('10m')], alarms=[None, None], warnings=[None, None], ), expected_attrv=dict(rvalue=Quantity('1m'), wvalue=Quantity('1m'), quality=AttrQuality.ATTR_VALID, error=None, ), ) class AttributeTestCase(unittest.TestCase):
import numpy import taurus from taurus.core import TaurusAttrValue, DataType from taurus.external import unittest from taurus.external.pint import Quantity from taurus.test import insertTest BASE_DIR = os.path.dirname(os.path.realpath(__file__)) # ========================================================================= # Tests of return types # ========================================================================= @insertTest(helper_name="read_attr", attr_fullname='pds-csv:{}/res/file-csv::["int1"]'.format(BASE_DIR), expected=dict(rvalue=Quantity([1, 2, 3], 'dimensionless'), type=DataType.Integer), expected_dim=1) @insertTest(helper_name="read_attr", attr_fullname='pds-csv:%s/res/file-csv::{"usecols":["int1"]}' % BASE_DIR, expected=dict(rvalue=Quantity([1, 2, 3], 'dimensionless'), type=DataType.Integer), expected_dim=1) @insertTest(helper_name="read_attr", attr_fullname='pds-csv:%s/res/file-csv::["int1"],' '{"usecols":["int1"]}' % BASE_DIR, expected=dict(rvalue=Quantity([1, 2, 3], 'dimensionless'), type=DataType.Integer), expected_dim=1) @insertTest(helper_name="read_attr",
import os import sys import numpy import subprocess from taurus.external import unittest from taurus.external.pint import Quantity import taurus from taurus.test import insertTest, getResourcePath from taurus.core.taurusbasetypes import DataType, AttrQuality, DataFormat from taurus.core.taurusbasetypes import TaurusAttrValue @insertTest(helper_name='write_read_attr', attrname='ca:test:a', setvalue=Quantity('1000mm'), expected=dict( rvalue=Quantity('1m'), type=DataType.Float, writable=True, data_format=DataFormat._0D, range=[Quantity('-10m'), Quantity('10m')], alarms=[None, None], warnings=[None, None], ), expected_attrv=dict( rvalue=Quantity('1m'), wvalue=Quantity('1m'), quality=AttrQuality.ATTR_VALID, error=None, ))
_FLOAT_SPE = _INT_SPE * .1 _BOOL_IMG = numpy.array([[True, False], [False, True]]) _BOOL_SPE = [True, False] _STR = 'foo BAR |-+#@!?_[]{}' _UINT8_IMG = numpy.array([[1, 2], [3, 4]], dtype='uint8') _UINT8_SPE = _UINT8_IMG[1, :] # ============================================================================== # Test writing fragment values @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu', cfg='range', value=[float('-inf'), float('inf')], expected=[Quantity(float('-inf')), Quantity(float('inf'))]) @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu', cfg='range', value=[Quantity(float('-inf')), Quantity(float('inf'))], expected=[Quantity(float('-inf')), Quantity(float('inf'))]) @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu', cfg='range', value=[100, 300], expected=[Quantity(100), Quantity(300)]) @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
expected=dict(rvalue=8, value=8, wvalue=None, w_value=None, type=DataType.Integer)) @insertTest(helper_name='read_attr', name='eval:"split.split".split("split")', expected=dict(rvalue=['', '.', ''], value=['', '.', ''], wvalue=None, w_value=None, label='"split.split".split("split")', type=DataType.String)) @insertTest(helper_name='read_attr', name='eval:Quantity(1.0)', expected=dict(rvalue=Quantity(1.0), value=1.0, w_value=None, wvalue=None, label='Quantity(1.0)', type=DataType.Float, data_format=DataFormat._0D)) @insertTest(helper_name='read_attr', name='eval:Quantity("1km")', expected=dict(rvalue=Quantity(1000, 'm'), value=1, w_value=None, wvalue=None, label='Quantity("1km")', type=DataType.Integer, data_format=DataFormat._0D))
def __init__(self, attr=None, pytango_dev_attr=None, config=None): # config parameter is kept for backwards compatibility only TaurusAttrValue.__init__(self) if config is not None: from taurus.core.util.log import deprecated deprecated(dep='"config" kwarg', alt='"attr"', rel="4.0") attr = config if attr is None: self._attrRef = None else: self._attrRef = weakref.proxy(attr) self.config = self._attrRef # bck-compat self._pytango_dev_attr = p = pytango_dev_attr if p is None: self._pytango_dev_attr = p = PyTango.DeviceAttribute() return if self._attrRef is None: return numerical = PyTango.is_numerical_type(self._attrRef._tango_data_type, inc_array=True) if p.has_failed: self.error = PyTango.DevFailed(*p.get_err_stack()) else: if p.is_empty: # spectra and images can be empty without failing dtype = FROM_TANGO_TO_NUMPY_TYPE.get(self._attrRef._tango_data_type) if self._attrRef.data_format == DataFormat._1D: shape = (0,) elif self._attrRef.data_format == DataFormat._2D: shape = (0, 0) p.value = numpy.empty(shape, dtype=dtype) if not (numerical or self._attrRef.type == DataType.Boolean): # generate a nested empty list of given shape p.value = [] for _ in xrange(len(shape) - 1): p.value = [p.value] rvalue = p.value wvalue = p.w_value if numerical: units = self._attrRef._units if rvalue is not None: rvalue = Quantity(rvalue, units=units) if wvalue is not None: wvalue = Quantity(wvalue, units=units) elif isinstance(rvalue, PyTango._PyTango.DevState): rvalue = DevState[str(rvalue)] elif p.type == PyTango.CmdArgType.DevUChar: if self._attrRef.data_format == DataFormat._0D: rvalue = chr(rvalue) wvalue = chr(wvalue) else: rvalue = rvalue.view("S1") wvalue = wvalue.view("S1") self.rvalue = rvalue self.wvalue = wvalue self.time = p.time # TODO: decode this into a TaurusTimeVal self.quality = quality_from_tango(p.quality)
# __all__ = [] import numpy from taurus.external import unittest from taurus.external.pint import Quantity import taurus from taurus.test import insertTest from taurus.core.taurusbasetypes import DataType, DataFormat, AttrQuality from taurus.core.evaluation.evalattribute import EvaluationAttrValue @insertTest(helper_name='read_attr', attr_fullname='eval:1', expected=dict( rvalue=Quantity(1, 'dimensionless'), type=DataType.Integer, label='1', writable=False, ), expected_attrv=dict(rvalue=Quantity([], 'dimensionless'), wvalue=None), expectedshape=None) @insertTest(helper_name='read_attr', attr_fullname='eval:Quantity("1m")+Quantity("2m")', expected=dict( rvalue=Quantity(3, 'm'), type=DataType.Integer, label='Quantity("1m")+Quantity("2m")', writable=False, ),
def _stepBy(self, v): value = self.getValue() self.setValue(value + Quantity(v, value.units))
def __init__(self, foomag=123): self._foo = Quantity(foomag, "m")