PyTango.Group.read_attribute() returns a list of sequence<GroupAttrReply>. If you try to read twice the value of one of this objects, the first time you'll get the right value, but the second time you'll get nothing. A sample code:
>>> import PyTango
>>> group = PyTango.Group("test")
>>> group.add("BO01/DI/BPM-01")
>>> res = group.read_attribute("XPosDD")[0]
>>> res.get_data().value
array([ 1.65694536, -3.06832737, -0.27192519, ..., -4.05291414,
2.45052059, 0.00959389])
>>> res.get_data().value
>>>
r = group.read_attribute() does not return a python list but a C++ std::vector. So r[0] is a different python object each time referring to the same internal C++ object.
r[0].get_data() returns a DeviceAttribute, a new different DeviceAttribute each time because in PyTango we do the extraction of 'value' and 'w_value'. The problem is internally we are doing a copy of DeviceAttribute with the constructor
DeviceAttribute::DeviceAttribute(const DeviceAttribute & source) and then we extract from this one.
But there is a lie there, 'source' is not really const! The copy constructor is stealing the value from the original DeviceAttribute! Which is nice in some cases for performance, but it is still a lie. I would expect having two constructors, one getting a "DeviceAttribute &" (or even better, in the future, "DeviceAttribute &&") that does what the current one does, and another "const DeviceAttribute &" which really does not modify the original. Then with each call to get_data() you would get a new copy of the DeviceAttribute.
Or we could make group.read_attribute() return a list, then we can do the get_data() once and store the result in the python GroupAttrReply object, so following calls to get_data() return the cached value. This however implies creating a new list each time an attribute is read and copying all the GroupAttrReply objects there, instead of just returning the raw 'C++' structure.
What does people think?
There's an even worse problem I had noticed that if you call the has_failed() function, the result dissapears. Here's a sample:
Python 2.6 (r26:66714, Feb 3 2009, 20:49:49)
[GCC 4.3.2 [gcc-4_3-branch revision 141291]] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import PyTango
>>> grupo = PyTango.Group("test")
>>> grupo.add("BO01/DI/BPM-01")
>>> grupo.add("BO03/DI/BPM-01")
>>> reply = grupo.read_attribute("DDTriggerCounter")
>>> reply[0].get_data().value
0
>>> reply[0].get_data().value
>>> reply = grupo.read_attribute("DDTriggerCounter")
>>> reply[0].has_failed()
False
>>> reply[0].get_data().value
>>> reply = grupo.read_attribute("DDTriggerCounter")
>>> reply[0].get_data().value
0
>>>
Mmm the second bug found by Jairo is worse...
Both fixed at svn revision 860, will be available in the next PyTango version.