High-level read of enum attribute with AttrQuality.ATTR_INVALID raises exception
It is impossible to set the quality of an enum attribute to ATTR_INVALID.
Setting the quality of an attribute to ATTR_INVALID also sets its value to None. If the attribute has an enum type, pytango will try to convert that None to an enum value, and throw an error.
For example,
@tango.server.attribute(dtype=PowerState)
def power(self, attr):
attr.set_value(PowerState.ON)
attr.set_quality(tango.AttrQuality.ATTR_INVALID)Here, the attr.set_quality(tango.AttrQuality.ATTR_INVALID) overrides the attribute value to None. Pytango then tries to convert that None to a PowerState, resulting in an error:
/usr/local/lib/python3.10/dist-packages/ska_low_mccs_common/device_proxy.py:426: in __getattr__
return getattr(self._device, name, default_value)
/usr/local/lib/python3.10/dist-packages/tango/device_proxy.py:321: in __DeviceProxy__getattr
return __get_attribute_value(self, attr_info, name)
/usr/local/lib/python3.10/dist-packages/tango/device_proxy.py:285: in __get_attribute_value
return enum_class(attr_value)
/usr/lib/python3.10/enum.py:385: in __call__
return cls.__new__(cls, value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = <enum 'powerState'>, value = None
def __new__(cls, value):
# all enum instances are actually created during class construction
# without calling this method; this method is called by the metaclass'
# __call__ (i.e. Color(3) ), and by pickle
if type(value) is cls:
# For lookups like Color(Color.RED)
return value
# by-value search for a matching enum member
# see if it's in the reverse mapping (for hashable values)
try:
return cls._value2member_map_[value]
except KeyError:
# Not found, no need to do long O(n) search
pass
except TypeError:
# not there, now do long search -- O(n) behavior
for member in cls._member_map_.values():
if member._value_ == value:
return member
# still not found -- try _missing_ hook
try:
exc = None
result = cls._missing_(value)
except Exception as e:
exc = e
result = None
try:
if isinstance(result, cls):
return result
else:
ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))
if result is None and exc is None:
> raise ve_exc
E ValueError: None is not a valid power
/usr/lib/python3.10/enum.py:710: ValueError(By the way, I don't think it is a good idea for the attribute value to be nulled by set_quality(INVALID). There are situations when I might want to return a value and yet mark it INVALID: e.g. if my Tango device's connection to its hardware has temporarily dropped out, I might want to return the last known value, marked as INVALID.)