[pytango] 41/483: better dynamic attribute creation

Sandor Bodo-Merle sbodomerle-guest at moszumanska.debian.org
Thu Sep 28 19:14:22 UTC 2017


This is an automated email from the git hooks/post-receive script.

sbodomerle-guest pushed a commit to annotated tag bliss_8.10
in repository pytango.

commit 21313b622862e86431a97a8b4d47039d081698ea
Author: tiagocoutinho <tiagocoutinho at 4e9c00fd-8f2e-0410-aa12-93ce3db5e235>
Date:   Thu Sep 8 12:50:35 2011 +0000

    better dynamic attribute creation
    
    git-svn-id: http://svn.code.sf.net/p/tango-cs/code/bindings/PyTango/trunk@17834 4e9c00fd-8f2e-0410-aa12-93ce3db5e235
---
 PyTango/__init__.py      |   1 +
 PyTango/attr_data.py     | 233 +++++++++++++++++++++++++++++++++++++++++++++++
 PyTango/device_class.py  | 161 ++++----------------------------
 PyTango/device_server.py |  38 ++++++--
 src/device_proxy.cpp     |   1 -
 5 files changed, 279 insertions(+), 155 deletions(-)

diff --git a/PyTango/__init__.py b/PyTango/__init__.py
index 4f89022..1c3ee2c 100644
--- a/PyTango/__init__.py
+++ b/PyTango/__init__.py
@@ -58,6 +58,7 @@ __version_description__ = Release.version_description
 __doc__ = Release.long_description
 
 import pytango_init
+from attr_data import *
 from log4tango import *
 from device_server import *
 from attribute_proxy import *
diff --git a/PyTango/attr_data.py b/PyTango/attr_data.py
new file mode 100644
index 0000000..d483c4f
--- /dev/null
+++ b/PyTango/attr_data.py
@@ -0,0 +1,233 @@
+################################################################################
+##
+## This file is part of PyTango, a python binding for Tango
+## 
+## http://www.tango-controls.org/static/PyTango/latest/doc/html/index.html
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+## 
+## PyTango is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+## 
+## PyTango 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 Lesser General Public License for more details.
+## 
+## You should have received a copy of the GNU Lesser General Public License
+## along with PyTango.  If not, see <http://www.gnu.org/licenses/>.
+##
+################################################################################
+
+"""
+This is an internal PyTango module.
+"""
+
+__all__ = [ "AttrData" ]
+
+__docformat__ = "restructuredtext"
+
+import operator
+
+from _PyTango import Except, CmdArgType, AttrDataFormat, AttrWriteType, \
+    DispLevel, UserDefaultAttrProp, Attr, SpectrumAttr, ImageAttr
+
+
+class AttrData(object):
+    """A helper class that contains the same information one of the items in
+    DeviceClass.attr_list but in object form"""
+    
+    def __init__(self, name, class_name, attr_info=None):
+        self.class_name = class_name
+        self.attr_name = name
+        self.attr_type = CmdArgType.DevVoid
+        self.attr_format = AttrDataFormat.SCALAR
+        self.attr_write = AttrWriteType.READ
+        self.dim_x = 1
+        self.dim_y = 0
+        self.display_level = DispLevel.OPERATOR
+        self.polling_period = -1
+        self.memorized = False
+        self.hw_memorized = False
+        self.read_method_name = "read_%s" % name
+        self.write_method_name = "write_%s" % name
+        self.is_allowed_name = "is_%s_allowed" % name
+        self.attr_class = None
+        self.attr_args = []
+        self.att_prop = None
+        if attr_info is not None:
+            self.from_attr_info(attr_info)
+
+    def __throw_exception(self, msg, meth="create_attribute()"):
+        Except.throw_exception("PyDs_WrongAttributeDefinition", msg, meth)
+
+    def __create_user_default_attr_prop(self, attr_name, extra_info):
+        """for internal usage only"""
+        p = UserDefaultAttrProp()
+        for k, v in extra_info.iteritems():
+            k_lower = k.lower()
+            method_name = "set_%s" % k_lower.replace(' ','_')
+            if hasattr(p, method_name):
+                method = getattr(p, method_name)
+                method(str(v))
+            elif k == 'delta_time':
+                p.set_delta_t(str(v))
+            elif not k_lower in ('display level', 'polling period', 'memorized'):
+                name = self.get_name()
+                msg = "Wrong definition of attribute %s in " \
+                      "class %s\nThe object extra information '%s' " \
+                      "is not recognized!" % (attr_name, name, k)
+                self.__throw_create_attribute_exception(msg)
+        return p
+
+    def from_attr_info(self, attr_info):
+        name = self.class_name
+        attr_name = self.attr_name
+        throw_ex = self.__throw_exception
+        # check for well defined attribute info
+        
+        # check parameter
+        if not operator.isSequenceType(attr_info):
+            throw_ex("Wrong data type for value for describing attribute %s in "
+                     "class %s\nMust be a sequence with 1 or 2 elements" 
+                     % (attr_name, name))
+
+        if len(attr_info) < 1 or len(attr_info) > 2:
+            throw_ex("Wrong number of argument for describing attribute %s in "
+                     "class %s\nMust be a sequence with 1 or 2 elements"
+                     % (attr_name, name))
+        
+        extra_info = {}
+        if len(attr_info) == 2:
+            # attr_info[1] must be a dictionary
+            # extra_info = attr_info[1], with all the keys lowercase
+            for k, v in attr_info[1].iteritems():
+                extra_info[k.lower()] = v
+        
+        attr_info = attr_info[0]
+        
+        attr_info_len = len(attr_info)
+        # check parameter
+        if not operator.isSequenceType(attr_info) or \
+           attr_info_len < 3 or attr_info_len > 5:
+            throw_ex("Wrong data type for describing mandatory information for "
+                     "attribute %s in class %s\nMust be a sequence with 3, 4 "
+                     "or 5 elements" % (attr_name, name))
+        
+        # get data type
+        try:
+            self.attr_type = CmdArgType(attr_info[0])
+        except:
+            throw_ex("Wrong data type in attribute argument for attribute %s "
+                     "in class %s\nAttribute data type (first element in first "
+                     "sequence) must be a PyTango.CmdArgType"
+                     % (attr_name, name))
+        
+        # get format
+        try:
+            self.attr_format = AttrDataFormat(attr_info[1])
+        except:
+            throw_ex("Wrong data format in attribute argument for attribute %s "
+                     "in class %s\nAttribute data format (second element in "
+                     "first sequence) must be a PyTango.AttrDataFormat"
+                     % (attr_name, name))
+        
+        if self.attr_format == AttrDataFormat.SCALAR:
+            if attr_info_len != 3:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\nSequence describing mandatory "
+                         "attribute parameters for scalar attribute must have "
+                         "3 elements" % (attr_name, name))
+        elif self.attr_format == AttrDataFormat.SPECTRUM:
+            if attr_info_len != 4:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\nSequence describing mandatory "
+                         "attribute parameters for spectrum attribute must "
+                         "have 4 elements" % (attr_name, name))
+            try:
+                self.dim_x = int(attr_info[3])
+            except:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\n4th element in sequence describing "
+                         "mandatory dim_x attribute parameter for spectrum "
+                         "attribute must be an integer" % (attr_name, name))
+        elif self.attr_format == AttrDataFormat.IMAGE:
+            if attr_info_len != 5:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\nSequence describing mandatory "
+                         "attribute parameters for image attribute must have "
+                         "5 elements" % (attr_name, name))
+            try:
+                self.dim_x = int(attr_info[3])
+            except:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\n4th element in sequence describing "
+                         "mandatory dim_x attribute parameter for image "
+                         "attribute must be an integer"  % (attr_name, name))
+            try:
+                self.dim_y = int(attr_info[4])
+            except:
+                throw_ex("Wrong data type in attribute argument for attribute "
+                         "%s in class %s\n5th element in sequence describing "
+                         "mandatory dim_y attribute parameter for image "
+                         "attribute must be an integer" % (attr_name, name))
+        
+        #get write type
+        try:
+            self.attr_write = AttrWriteType(attr_info[2])
+        except:
+            throw_ex("Wrong data write type in attribute argument for "
+                     "attribute %s in class %s\nAttribute write type (third "
+                     "element in first sequence) must be a "
+                     "PyTango.AttrWriteType" % (attr_name, name))
+        try:
+            self.display_level = DispLevel(extra_info.get("display level", 
+                                                          DispLevel.OPERATOR))
+        except:
+            throw_ex("Wrong display level in attribute information for "
+                     "attribute %s in class %s\nAttribute information for "
+                     "display level is not a PyTango.DispLevel"
+                     % (attr_name, name))
+        try:
+            self.polling_period = int(extra_info.get("polling period", -1))
+        except:
+            throw_ex("Wrong polling period in attribute information for "
+                     "attribute %s in class %s\nAttribute information for "
+                     "polling period is not an integer" % (attr_name, name))
+        
+        self.memorized = extra_info.get("memorized", "false")
+        if self.memorized == "true":
+            self.memorized, self.hw_memorized = True, True
+        elif self.memorized == "true_without_hard_applied":
+            self.memorized = True
+        else:
+            self.memorized = False
+        
+        self.attr_class = extra_info.get("klass", self.DftAttrClassMap[self.attr_format])
+        self.attr_args.extend((self.attr_name, self.attr_type, self.attr_write))
+        if not self.attr_format == AttrDataFormat.SCALAR:
+            self.attr_args.append(self.dim_x)
+            if not self.attr_format == AttrDataFormat.SPECTRUM:
+                self.attr_args.append(self.dim_y)
+                
+        att_prop = None
+        if extra_info:
+            self.att_prop = self.__create_user_default_attr_prop(attr_name, extra_info)
+    
+    def to_attr(self):
+        attr = self.attr_class(*self.attr_args)
+        if self.att_prop is not None:
+            attr.set_default_properties(self.att_prop)
+        attr.set_disp_level(self.display_level)
+        if self.memorized:
+            attr.set_memorized()
+            attr.set_memorized_init(self.hw_memorized)
+        if self.polling_period > 0:
+            attr.set_polling_period(self.polling_period)
+        return attr
+        
+    DftAttrClassMap = { AttrDataFormat.SCALAR : Attr,
+                        AttrDataFormat.SPECTRUM: SpectrumAttr,
+                        AttrDataFormat.IMAGE : ImageAttr }
\ No newline at end of file
diff --git a/PyTango/device_class.py b/PyTango/device_class.py
index e494694..326a801 100644
--- a/PyTango/device_class.py
+++ b/PyTango/device_class.py
@@ -31,12 +31,13 @@ __docformat__ = "restructuredtext"
 
 import types
 import operator
+import collections
 
 from _PyTango import Except, DevFailed
 from _PyTango import _DeviceClass, Database
 from _PyTango import CmdArgType, AttrDataFormat, AttrWriteType, DispLevel
 from _PyTango import UserDefaultAttrProp
-
+from _PyTango import Attr, SpectrumAttr, ImageAttr
 from pyutil import Util
 
 from utils import seqStr_2_obj, obj_2_str, is_array
@@ -44,6 +45,7 @@ from utils import document_method as __document_method
 
 from globals import get_class, get_class_by_class
 from globals import get_constructed_class_by_class
+from attr_data import AttrData
 
 class PropUtil:
     """An internal Property util class"""
@@ -271,6 +273,7 @@ class PropUtil:
         """internal helper method"""
         return obj_2_str(argin, argout_type)
 
+
 class DeviceClass(_DeviceClass):
     """Base class for all TANGO device-class class.
        A TANGO device-class class is a class where is stored all
@@ -317,152 +320,20 @@ class DeviceClass(_DeviceClass):
         """for internal usage only"""
 
         for attr_name, attr_info in self.attr_list.iteritems():
-            self.__create_attribute(attr_list, attr_name, attr_info)
+            attr_data = AttrData(attr_name, self.get_name(), attr_info)
+            self.__create_attribute(attr_list, attr_data)
 
-    def __create_attribute(self, attr_list, attr_name, attr_info):
+    def __create_attribute(self, attr_list, attr_data):
         """for internal usage only"""
-        name = self.get_name()
-
-        # check for well defined attribute info
-
-        # check parameter
-        if not operator.isSequenceType(attr_info):
-            msg = "Wrong data type for value for describing attribute %s in " \
-                  "class %s\nMust be a sequence with 1 or 2 elements" % (attr_name, name)
-            self.__throw_create_attribute_exception(msg)
-
-        if len(attr_info) < 1 or len(attr_info) > 2:
-            msg = "Wrong number of argument for describing attribute %s in " \
-                  "class %s\nMust be a sequence with 1 or 2 elements" % (attr_name, name)
-            self.__throw_create_attribute_exception(msg)
-
-        extra_info = {}
-        if len(attr_info) == 2:
-            # attr_info[1] must be a dictionary
-            # extra_info = attr_info[1], with all the keys lowercase
-            for k, v in attr_info[1].iteritems():
-                extra_info[k.lower()] = v
-
-        attr_info = attr_info[0]
-
-        attr_info_len = len(attr_info)
-        # check parameter
-        if not operator.isSequenceType(attr_info) or attr_info_len < 3 or attr_info_len > 5:
-            msg = "Wrong data type for describing mandatory information for attribute %s " \
-                  "in class %s\nMust be a sequence with 3, 4 or 5 elements" % (attr_name, name)
-            self.__throw_create_attribute_exception(msg)
-
-        # get data type
-        attr_type = CmdArgType.DevVoid
-        try:
-            attr_type = CmdArgType(attr_info[0])
-        except:
-            msg = "Wrong data type in attribute argument for attribute %s in " \
-                  "class %s\nAttribute data type (first element in first " \
-                  "sequence) must be a PyTango.CmdArgType"
-            self.__throw_create_attribute_exception(msg)
-
-        # get format
-        attr_format = AttrDataFormat.SCALAR
-        try:
-            attr_format = AttrDataFormat(attr_info[1])
-        except:
-            msg = "Wrong data format in attribute argument for attribute %s in " \
-                  "class %s\nAttribute data format (second element in first " \
-                  "sequence) must be a PyTango.AttrDataFormat"
-            self.__throw_create_attribute_exception(msg)
-
-        dim_x, dim_y = 1, 0
-        if attr_format == AttrDataFormat.SCALAR:
-            if attr_info_len != 3:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\nSequence describing mandatory attribute parameters " \
-                      "for scalar attribute must have 3 elements"
-                self.__throw_create_attribute_exception(msg)
-        elif attr_format == AttrDataFormat.SPECTRUM:
-            if attr_info_len != 4:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\nSequence describing mandatory attribute parameters " \
-                      "for spectrum attribute must have 4 elements"
-                self.__throw_create_attribute_exception(msg)
-            try:
-                dim_x = int(attr_info[3])
-            except:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\n4th element in sequence describing mandatory dim_x " \
-                      "attribute parameter for spectrum attribute must be an integer"
-                self.__throw_create_attribute_exception(msg)
-        elif attr_format == AttrDataFormat.IMAGE:
-            if attr_info_len != 5:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\nSequence describing mandatory attribute parameters " \
-                      "for image attribute must have 5 elements"
-                self.__throw_create_attribute_exception(msg)
-            try:
-                dim_x = int(attr_info[3])
-            except:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\n4th element in sequence describing mandatory dim_x " \
-                      "attribute parameter for image attribute must be an integer"
-                self.__throw_create_attribute_exception(msg)
-            try:
-                dim_y = int(attr_info[4])
-            except:
-                msg = "Wrong data type in attribute argument for attribute %s in " \
-                      "class %s\n5th element in sequence describing mandatory dim_y " \
-                      "attribute parameter for image attribute must be an integer"
-                self.__throw_create_attribute_exception(msg)
-
-        #get write type
-        attr_write = AttrWriteType.READ
-        try:
-            attr_write = AttrWriteType(attr_info[2])
-        except:
-            msg = "Wrong data write type in attribute argument for attribute %s in " \
-                  "class %s\nAttribute write type (third element in first " \
-                  "sequence) must be a PyTango.AttrWriteType"
-            self.__throw_create_attribute_exception(msg)
-
-        # check that the method(s) to be executed exists
-        read_method_name = "read_%s" % attr_name
-        write_method_name = "write_%s" % attr_name
-        is_allowed_name = "is_%s_allowed" % attr_name
-
-        try:
-            display_level = DispLevel(extra_info.get("display level", DispLevel.OPERATOR))
-        except:
-            msg = "Wrong display level in attribute information for attribute %s in " \
-                  "class %s\nAttribute information for display level is not a " \
-                  "PyTango.DispLevel" % (attr_name, name)
-            self.__throw_create_attribute_exception(msg)
-
-        try:
-            polling_period = int(extra_info.get("polling period", -1))
-        except:
-            msg = "Wrong polling period in attribute information for attribute %s in " \
-                  "class %s\nAttribute information for polling period is not an " \
-                  "integer" % (attr_name, name)
-            self.__throw_create_attribute_exception(msg)
-
-        memorized, hw_memorized = extra_info.get("memorized", "false"), False
-        if memorized == "true":
-            memorized, hw_memorized = True, True
-        elif memorized == "true_without_hard_applied":
-            memorized = True
-        else:
-            memorized = False
-
-        att_prop = None
-        if extra_info:
-            att_prop = self.__create_user_default_attr_prop(attr_name, extra_info)
-
-        self._create_attribute(attr_list, attr_name, attr_type, attr_format,
-                               attr_write, dim_x, dim_y,
-                               display_level, polling_period,
-                               memorized, hw_memorized,
-                               read_method_name, write_method_name,
-                               is_allowed_name,
-                               att_prop)
+        self._create_attribute(attr_list, attr_data.attr_name,
+                               attr_data.attr_type, attr_data.attr_format,
+                               attr_data.attr_write, attr_data.dim_x,
+                               attr_data.dim_y, attr_data.display_level,
+                               attr_data.polling_period, attr_data.memorized,
+                               attr_data.hw_memorized,
+                               attr_data.read_method_name,
+                               attr_data.write_method_name,
+                               attr_data.is_allowed_name, attr_data.att_prop)
 
     def __create_user_default_attr_prop(self, attr_name, extra_info):
         """for internal usage only"""
diff --git a/PyTango/device_server.py b/PyTango/device_server.py
index 4460b9e..0e9bf1a 100644
--- a/PyTango/device_server.py
+++ b/PyTango/device_server.py
@@ -43,6 +43,7 @@ from _PyTango import UserDefaultAttrProp
 
 from utils import document_method as __document_method
 from utils import copy_doc
+from attr_data import AttrData
 
 import log4tango
 
@@ -269,7 +270,7 @@ def __DeviceImpl__get_device_properties(self, ds_class = None):
         raise e
 
 def __DeviceImpl__add_attribute(self, attr, r_meth=None, w_meth=None, is_allo_meth=None):
-    """add_attribute(self, attr, r_meth=None, w_meth=None, is_allo_meth=None) -> None
+    """add_attribute(self, attr, r_meth=None, w_meth=None, is_allo_meth=None) -> Attr
 
             Add a new attribute to the device attribute list. Please, note that if you add
             an attribute to a device at device creation time, this attribute will be added
@@ -277,33 +278,50 @@ def __DeviceImpl__add_attribute(self, attr, r_meth=None, w_meth=None, is_allo_me
             same class created after this attribute addition will also have this attribute.
 
         Parameters :
-            attr : (Attr) the new attribute to be added to the list.
+            attr : (Attr or AttrData) the new attribute to be added to the list.
             r_meth : (callable) the read method to be called on a read request
-            w_meth : (callable) the write method to be called on a write request (if attr is writable)
-            is_allo_meth: (callable) the method that is called to check if it is possible to access the attribute or not
+            w_meth : (callable) the write method to be called on a write request
+                     (if attr is writable)
+            is_allo_meth: (callable) the method that is called to check if it
+                          is possible to access the attribute or not
         
-        Return     : None
+        Return     : (Attr) the newly created attribute.
         
         Throws     : DevFailed"""
+    
+    attr_data = None
+    if isinstance(attr, AttrData):
+        attr_data = attr
+        attr = attr.to_attr()
+    
     att_name = attr.get_name()
 
     add_name_in_list = False
     if r_meth is not None:
-        r_meth_name = 'read_%s' % att_name
+        if attr_data is None:
+            r_meth_name = 'read_%s' % att_name
+        else:
+            r_meth_name = attr_data.read_method_name
         if not hasattr(self.__class__, r_meth_name):
             setattr(self.__class__, r_meth_name, r_meth)
             add_name_in_list = True
 
     if w_meth is not None:
-        w_meth_name = 'write_%s' % att_name
+        if attr_data is None:
+            w_meth_name = 'write_%s' % att_name
+        else:
+            w_meth_name = attr_data.write_method_name
         if not hasattr(self.__class__, w_meth_name):
             setattr(self.__class__, w_meth_name, w_meth)
             add_name_in_list = True
 
     if is_allo_meth is not None:
-        allo_meth_name = 'is_%s_allowed' % att_name
+        if attr_data is None:
+            allo_meth_name = 'is_%s_allowed' % att_name
+        else:
+            allo_meth_name = attr_data.is_allowed_name
         if not hasattr(self.__class__, allo_meth_name):
-            setattr(self.__class__, allo_meth_name,is_allo_meth)
+            setattr(self.__class__, allo_meth_name, is_allo_meth)
             add_name_in_list = True
 
     try:
@@ -314,6 +332,8 @@ def __DeviceImpl__add_attribute(self, attr, r_meth=None, w_meth=None, is_allo_me
     except:
         if add_name_in_list:
             self._remove_attr_meth(att_name)
+        raise
+    return attr
 
 def __DeviceImpl__remove_attribute(self, attr_name):
     """
diff --git a/src/device_proxy.cpp b/src/device_proxy.cpp
index d8c6a49..092711f 100644
--- a/src/device_proxy.cpp
+++ b/src/device_proxy.cpp
@@ -127,7 +127,6 @@ namespace PyDeviceProxy
 
     static inline void write_attribute(Tango::DeviceProxy& self, const Tango::AttributeInfo & attr_info, object py_value)
     {
-
         Tango::DeviceAttribute da;
         PyDeviceAttribute::reset(da, attr_info, py_value);
         AutoPythonAllowThreads guard;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pytango.git



More information about the debian-science-commits mailing list