[pytango] 414/483: Add experimental API

Sandor Bodo-Merle sbodomerle-guest at moszumanska.debian.org
Thu Sep 28 19:15:07 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 5f4d71dc2eab248bf44088147c25e3b0bff2fe63
Author: tiagocoutinho <tiagocoutinho at 4e9c00fd-8f2e-0410-aa12-93ce3db5e235>
Date:   Tue Sep 30 12:25:33 2014 +0000

    Add experimental API
    
    git-svn-id: http://svn.code.sf.net/p/tango-cs/code/bindings/PyTango/trunk@26598 4e9c00fd-8f2e-0410-aa12-93ce3db5e235
---
 src/boost/python/client.py | 232 ++++++++++++++-
 src/boost/python/codec.py  |  22 ++
 src/boost/python/server.py | 712 +++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 911 insertions(+), 55 deletions(-)

diff --git a/src/boost/python/client.py b/src/boost/python/client.py
index de55252..69a093a 100644
--- a/src/boost/python/client.py
+++ b/src/boost/python/client.py
@@ -9,10 +9,232 @@
 # See LICENSE.txt for more info.
 # ------------------------------------------------------------------------------
 
-"""High Level API for writting Tango clients"""
+"""
+High Level API for writting Tango clients
 
+This is an experimental module. Not part of the official API.
+"""
+
+import functools
+
+import PyTango
 from PyTango import DeviceProxy as Device
-from PyTango import AttributeProxy as Attribute
-from PyTango import Database
-from PyTango import Group
-from PyTango import DeviceAttribute
+from PyTango import CmdArgType
+from PyTango.codec import loads
+from PyTango.codec import dumps as _dumps
+
+_FMT = "pickle"
+
+dumps = functools.partial(_dumps, _FMT)
+
+def _command(device, cmd_info, *args, **kwargs):
+    name = cmd_info.cmd_name
+    if cmd_info.in_type == CmdArgType.DevEncoded:
+        result = device.command_inout(name, dumps((args, kwargs)))
+    else:
+        result = device.command_inout(name, *args, **kwargs)
+    if cmd_info.out_type == CmdArgType.DevEncoded:
+        result = loads(*result)
+    return result
+
+
+class _DeviceHelper(object):
+
+    __CMD_FILTER = set(("init", "state", "status"))
+    __ATTR_FILTER = set(("state", "status"))
+
+    def __init__(self, dev_name, *args, **kwargs):
+        self.dev_name = dev_name
+        self.device = Device(dev_name, *args, **kwargs)
+
+    def get_attr_cache(self, refresh=False):
+        try:
+            cache = self.__attr_cache
+            if not cache:
+                refresh = True
+        except AttributeError:
+            refresh = True
+        if refresh:
+            cache = {}
+            dev = self.device
+            try:
+                for attr_info in dev.attribute_list_query_ex():
+                    attr_name = attr_info.name
+                    if attr_name.lower() in self.__ATTR_FILTER:
+                        continue
+                    cache[attr_name] = attr_info
+            except PyTango.DevFailed:
+                pass
+            self.__attr_cache = cache
+        return cache
+
+    def get_attr_info(self, name):
+        cache = self.get_attr_cache()
+        result = cache.get(name)
+        if result:
+            return result
+        else:
+            cache = self.get_attr_cache(refresh=True)
+            return cache.get(name)
+
+    def get_cmd_cache(self, refresh=False):
+        try:
+            cache = self.__cmd_cache
+            if not cache:
+                refresh = True
+        except AttributeError:
+            refresh = True
+        if refresh:
+            cache = {}
+            dev = self.device
+            try:
+                for cmd_info in dev.command_list_query():
+                    cmd_name = cmd_info.cmd_name
+                    if cmd_name.lower() in self.__CMD_FILTER:
+                        continue
+                    cmd_func = functools.partial(_command, dev, cmd_info)
+                    cmd_func.__name__ = cmd_name
+                    cmd_func.__doc__ = cmd_info.in_type_desc
+                    cmd_info.func = cmd_func
+                    cache[cmd_name] = cmd_info
+            except PyTango.DevFailed:
+                pass
+            self.__cmd_cache = cache
+        return cache        
+
+    def get_cmd_info(self, name):
+        cache = self.get_cmd_cache()
+        result = cache.get(name)
+        if result:
+            return result
+        else:
+            cache = self.get_cmd_cache(refresh=True)
+            return cache.get(name)
+
+    def is_cmd(self, name):
+        return name.lower() in self.get_cmd_cache()
+
+    def members(self):
+        result = self.get_attr_cache().keys()
+        result.extend(self.get_cmd_cache().keys())
+        return result
+
+    def get(self, name):
+        dev = self.device
+        result = self.get_attr_info(name)
+        if result:
+            result = dev.read_attribute(name)
+            value = result.value
+            if result.type == PyTango.DevEncoded:
+                result = loads(*value)
+            else:
+                result = value
+            return result
+        result = self.get_cmd_info(name)
+        if result is None:
+            raise KeyError("Unknown %s" % name)
+        return result
+
+    def set(self, name, value):
+        result = self.get_attr_info(name)
+        if result is None:
+            raise KeyError("Unknown attribute %s" % name)
+        if result.data_type == PyTango.DevEncoded:
+            self.device.write_attribute(name, dumps(value))
+        else:
+            self.device.write_attribute(name, value)
+        
+    def get_info(self):
+        try:
+            return self.__info
+        except AttributeError:
+            pass
+        try:
+            info = self.device.info()
+            self.__dict__["__info"] = info
+            return info
+        except PyTango.DevFailed:
+            return None
+    
+    def __str__(self):
+        return self.dstr()
+
+    def __repr__(self):
+        return str(self)
+
+    def dstr(self):
+        info = self.get_info()
+        klass = "Device"
+        if info:
+            klass = info.dev_class
+        return "{0}({1})".format(klass, self.dev_name)
+
+class _Device(object):
+    """Tango object"""
+    
+    def __init__(self, dev_name, *args, **kwargs):
+        helper = _DeviceHelper(dev_name, *args, **kwargs)
+        self.__dict__["_helper"] = helper
+    
+    def __getattr__(self, name):
+        r = self._helper.get(name)
+        if isinstance(r, PyTango.CommandInfo):
+            self.__dict__[name] = r.func
+            return r.func
+        return r
+
+    def __setattr__(self, name, value):
+        try:
+            return self._helper.set(name, value)
+        except KeyError:
+            object.__setattr__(self, name, value)
+
+    def __str__(self):
+        return str(self._helper)
+
+    def __repr__(self):
+        return repr(self._helper)
+
+    def __dir__(self):
+        return self._helper.members()
+
+
+def Object(*args, **kwargs):
+    """Experimental class. Not part of the official API"""
+    return _Device(*args, **kwargs)
+
+
+def get_object_proxy(obj):
+    """Experimental function. Not part of the official API"""    
+    return obj._helper.device
+
+
+def get_object_db(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).get_device_db()
+
+
+def get_object_name(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).get_name()
+
+
+def get_object_info(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).info()
+
+
+def get_attributes_config(obj, refresh=False):
+    """Experimental function. Not part of the official API"""    
+    return obj._helper.get_attr_cache(refresh=refresh)
+
+
+def get_commands_config(obj, refresh=False):
+    """Experimental function. Not part of the official API"""
+    return obj._helper.get_cmd_cache(refresh=refresh)
+
+
+def connect(obj, signal, slot, event_type=None):
+    """Experimental function. Not part of the official API"""    
+    raise NotImplementedError
+    
diff --git a/src/boost/python/codec.py b/src/boost/python/codec.py
new file mode 100644
index 0000000..5f99d49
--- /dev/null
+++ b/src/boost/python/codec.py
@@ -0,0 +1,22 @@
+__all__ = ["loads", "dumps"]
+
+def loads(fmt, data):
+    if fmt.startswith("pickle"):
+        import pickle
+        loads = pickle.loads
+    elif fmt.startswith("json"):
+        import json
+        loads = json.loads
+    else:
+        raise TypeError("Format '{0}' not supported".format(fmt))
+    return loads(data)
+
+def dumps(fmt, obj):
+    if fmt.startswith("pickle"):
+        import pickle
+        ret = fmt, pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
+        return ret
+    elif fmt.startswith("json"):
+        import json
+        return fmt, json.dumps(obj)
+    raise TypeError("Format '{0}' not supported".format(fmt))
diff --git a/src/boost/python/server.py b/src/boost/python/server.py
index ea993eb..d31dc8a 100644
--- a/src/boost/python/server.py
+++ b/src/boost/python/server.py
@@ -13,23 +13,30 @@
 
 from __future__ import with_statement
 from __future__ import print_function
+from __future__ import absolute_import
 
 __all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
            "command", "device_property", "class_property",
-           "run", "server_run"]
+           "run", "server_run", "Server"]
 
+import os
 import sys
 import inspect
+import logging
+import weakref
 import operator
 import functools
 import traceback
 
-from ._PyTango import CmdArgType, AttrDataFormat, AttrWriteType
-from ._PyTango import DevFailed, constants
+from ._PyTango import (CmdArgType, AttrDataFormat, AttrWriteType,
+                       DevFailed, Except, GreenMode, constants,
+                       Database, DbDevInfo, DevState, CmdArgType,
+                       Attr)
 from .attr_data import AttrData
 from .device_class import DeviceClass
-from .utils import get_tango_device_classes, is_seq, is_non_str_seq
-from .utils import scalar_to_array_type
+from .utils import (get_tango_device_classes, is_seq, is_non_str_seq,
+                    scalar_to_array_type)
+from .codec import loads, dumps
 
 API_VERSION = 2
 
@@ -100,7 +107,7 @@ def __build_to_tango_type():
 TO_TANGO_TYPE = __build_to_tango_type()
 
 
-def get_tango_type_format(dtype=None, dformat=None):
+def _get_tango_type_format(dtype=None, dformat=None):
     if dformat is None:
         dformat = AttrDataFormat.SCALAR
         if is_non_str_seq(dtype):
@@ -153,8 +160,7 @@ def set_complex_value(attr, value):
             attr.set_value(value)
 
 
-def check_dev_klass_attr_read_method(tango_device_klass,
-                                                   attribute):
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
     """
     Checks if method given by it's name for the given DeviceImpl
     class has the correct signature. If a read/write method doesn't
@@ -180,15 +186,26 @@ def check_dev_klass_attr_read_method(tango_device_klass,
     if len(read_args.args) < 2:
         @functools.wraps(read_method)
         def read_attr(self, attr):
-            ret = read_method(self)
+            runner = _get_runner()
+            if runner:
+                ret = runner.execute(read_method, self)
+            else:
+                ret = read_method(self)
             if not attr.get_value_flag() and ret is not None:
                 set_complex_value(attr, ret)
             return ret
-        method_name = "__read_{0}_wrapper__".format(
-            attribute.attr_name)
-        attribute.read_method_name = method_name
     else:
-        read_attr = read_method
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            runner = _get_runner()
+            if runner:
+                ret = runner.execute(read_method, self, attr)
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
 
     setattr(tango_device_klass, method_name, read_attr)
 
@@ -217,7 +234,12 @@ def check_dev_klass_attr_write_method(tango_device_klass, attribute):
     @functools.wraps(write_method)
     def write_attr(self, attr):
         value = attr.get_write_value()
-        return write_method(self, value)
+        runner = _get_runner()
+        if runner:
+            ret = runner.execute(write_method, self, value)
+        else:
+            ret = write_method(self, value)
+        return ret
     setattr(tango_device_klass, method_name, write_attr)
 
 
@@ -249,6 +271,15 @@ class _DeviceClass(DeviceClass):
         DeviceClass.__init__(self, name)
         self.set_type(name)
 
+    def _new_device(self, klass, dev_class, dev_name):
+        runner = _get_runner()
+        if runner:
+            return runner.execute(DeviceClass._new_device, self,
+                                  klass, dev_class, dev_name)
+        else:
+            return DeviceClass._new_device(self, klass, dev_class,
+                                           dev_name)
+
     def dyn_attr(self, dev_list):
         """Invoked to create dynamic attributes for the given devices.
         Default implementation calls
@@ -522,7 +553,7 @@ class attribute(AttrData):
         super(attribute, self).__init__(name, class_name)
         if 'dtype' in kwargs:
             kwargs['dtype'], kwargs['dformat'] = \
-                get_tango_type_format(kwargs['dtype'],
+                _get_tango_type_format(kwargs['dtype'],
                                       kwargs.get('dformat'))
         self.build_from_dict(kwargs)
 
@@ -625,21 +656,30 @@ def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
             doc_out=doc_out)
     name = f.__name__
 
-    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
-    dtype_out, dformat_out = get_tango_type_format(dtype_out,
-                                                   dformat_out)
+    dtype_in, dformat_in = _get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = _get_tango_type_format(dtype_out,
+                                                    dformat_out)
 
     din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
     dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
-    f.__tango_command__ = name, [din, dout]
-    return f
+
+    @functools.wraps(f)
+    def cmd(self, value):
+        runner = _get_runner()
+        if runner:
+            ret = runner.execute(f, self, value)
+        else:
+            ret = f(self, value)
+        return ret
+    cmd.__tango_command__ = name, [din, dout]
+    return cmd
 
 
 class _property(object):
 
     def __init__(self, dtype, doc='', default_value=None):
         self.__value = None
-        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        dtype = from_typeformat_to_type(*_get_tango_type_format(dtype))
         self.dtype = dtype
         self.doc = doc
         self.default_value = default_value
@@ -675,6 +715,7 @@ class device_property(_property):
     """
     pass
 
+
 class class_property(_property):
     """
     Declares a new tango class property in a :class:`Device`. To be
@@ -704,7 +745,7 @@ def __to_cb(post_init_callback):
     err_msg = "post_init_callback must be a callable or " \
               "sequence <callable [, args, [, kwargs]]>"
     if operator.isCallable(post_init_callback):
-        return post_init_callback
+        f = post_init_callback
     elif is_non_str_seq(post_init_callback):
         length = len(post_init_callback)
         if length < 1 or length > 3:
@@ -717,27 +758,15 @@ def __to_cb(post_init_callback):
             args = post_init_callback[1]
         if length > 2:
             kwargs = post_init_callback[2]
-        return functools.partial(cb, *args, **kwargs)
-
-    raise TypeError(err_msg)
-
-
-def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
-                 event_loop=None, post_init_callback=None):
-    import PyTango
-    if msg_stream is None:
-        write = lambda msg: None
+        f = functools.partial(cb, *args, **kwargs)
     else:
-        write = msg_stream.write
-
-    if args is None:
-        args = sys.argv
+        raise TypeError(err_msg)
 
-    post_init_callback = __to_cb(post_init_callback)
+    return f
 
-    if util is None:
-        util = PyTango.Util(args)
 
+def _to_classes(classes):
+    uclasses = []
     if is_seq(classes):
         for klass_info in classes:
             if is_seq(klass_info):
@@ -753,7 +782,7 @@ def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
                 klass_klass = klass_info.TangoClassClass
                 klass_name = klass_info.TangoClassName
                 klass = klass_info
-            util.add_class(klass_klass, klass, klass_name)
+            uclasses.append((klass_klass, klass, klass_name))
     else:
         for klass_name, klass_info in classes.items():
             if is_seq(klass_info):
@@ -769,21 +798,75 @@ def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
                 klass_klass = klass_info.TangoClassClass
                 klass_name = klass_info.TangoClassName
                 klass = klass_info
-            util.add_class(klass_klass, klass, klass_name)
+            uclasses.append((klass_klass, klass, klass_name))
+    return uclasses
+
+
+def _add_classes(util, classes):
+    for class_info in _to_classes(classes):
+        util.add_class(*class_info)
+
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 green_mode=None):
+    if green_mode is None:
+        from PyTango import get_green_mode
+        green_mode = get_green_mode()
+    gevent_mode = green_mode == GreenMode.Gevent
+
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
 
+    if util is None:
+        util = PyTango.Util(args)
     u_instance = PyTango.Util.instance()
+
+    if gevent_mode:
+        runner = _create_runner()
+        if event_loop:
+            event_loop = functools.partial(runner.execute, event_loop)
+
     if event_loop is not None:
         u_instance.server_set_event_loop(event_loop)
-    u_instance.server_init()
-    post_init_callback()
-    write("Ready to accept request\n")
-    u_instance.server_run()
-    return util
 
+    log = logging.getLogger("PyTango")
+
+    def tango_loop(runner=None):
+        _add_classes(util, classes)
+        u_instance.server_init()
+        if runner:
+            runner.execute(post_init_callback)
+        else:
+            post_init_callback()
+        write("Ready to accept request\n")
+        u_instance.server_run()
+        if runner:
+            runner.stop()
+        log.debug("Tango loop exit")
+        
+    if gevent_mode:
+        runner = _create_runner()
+        start_new_thread = runner._threading.start_new_thread
+        tango_thread_id = start_new_thread(tango_loop, (runner,))
+        runner.run()
+        log.debug("Runner finished")
+    else:
+        tango_loop()
+        
+    return util
 
 def run(classes, args=None, msg_stream=sys.stdout,
         verbose=False, util=None, event_loop=None,
-        post_init_callback=None):
+        post_init_callback=None, green_mode=None):
     """
     Provides a simple way to run a tango server. It handles exceptions
     by writting a message to the msg_stream.
@@ -908,7 +991,8 @@ def run(classes, args=None, msg_stream=sys.stdout,
     try:
         return __server_run(classes, args=args, msg_stream=msg_stream,
                             util=util, event_loop=event_loop,
-                            post_init_callback=post_init_callback)
+                            post_init_callback=post_init_callback,
+                            green_mode=green_mode)
     except KeyboardInterrupt:
         write("Exiting: Keyboard interrupt\n")
     except DevFailed as df:
@@ -925,7 +1009,7 @@ def run(classes, args=None, msg_stream=sys.stdout,
 
 def server_run(classes, args=None, msg_stream=sys.stdout,
         verbose=False, util=None, event_loop=None,
-        post_init_callback=None):
+        post_init_callback=None, green_mode=None):
     """
     Since PyTango 8.1.2 it is just an alias to
     :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
@@ -951,5 +1035,533 @@ def server_run(classes, args=None, msg_stream=sys.stdout,
     """
     return run(classes, args=args, msg_stream=msg_stream,
                verbose=verbose, util=util, event_loop=event_loop,
-               post_init_callback=post_init_callback)
+               post_init_callback=post_init_callback,
+               green_mode=green_mode)
+
+
+__RUNNER = None
+
+def _get_runner():
+    return __RUNNER
+
+def _create_runner():
+    global __RUNNER
+    if __RUNNER:
+        return __RUNNER
+    
+    try:
+        from queue import Queue
+    except:
+        from Queue import Queue
+
+    import gevent
+    import gevent.event
+
+    class Runner:
+
+        from gevent import _threading
+
+        class Task:
+
+            def __init__(self, event, func, *args, **kwargs):
+                self.__event = event
+                self.__func = func
+                self.__args = args
+                self.__kwargs = kwargs
+                self.value = None
+                self.exception = None
+
+            def __call__(self):
+                func = self.__func
+                if func:
+                    try:
+                        self.value = func(*self.__args, **self.__kwargs)
+                    except:
+                        self.exception = sys.exc_info()
+                self.__event.set()
+
+            def run(self):
+                return gevent.spawn(self)
+
+        def __init__(self, max_queue_size=0):
+            self.__tasks = Queue(max_queue_size)
+            self.__stop_event = gevent.event.Event()
+            self.__watcher = gevent.get_hub().loop.async()
+            self.__watcher.start(self.__step)
+
+        def __step(self):
+            task = self.__tasks.get()
+            return task.run()
+
+        def run(self, timeout=None):
+            return gevent.wait(objects=(self.__stop_event,),
+                               timeout=timeout)
+
+        def execute(self, func, *args, **kwargs):
+            event = self._threading.Event() 
+            task = self.Task(event, func, *args, **kwargs)
+            self.__tasks.put(task)
+            self.__watcher.send()
+            event.wait()
+            if task.exception:
+                Except.throw_python_exception(*task.exception)
+            return task.value
+
+        def stop(self):
+            task = self.Task(self.__stop_event, None)
+            self.__tasks.put(task)
+            self.__watcher.send()
+
+    __RUNNER = Runner()
+    return __RUNNER
+
+
+_CLEAN_UP_TEMPLATE = """
+import sys
+from PyTango import Database
+
+db = Database()
+server_instance = '{server_instance}'
+try:
+    devices = db.get_device_class_list(server_instance)[::2]
+    for device in devices:
+        db.delete_device(device)
+        try:
+            db.delete_device_alias(db.get_alias(device))
+        except:
+            pass
+except:
+    print ('Failed to cleanup!')
+"""
+
+import numpy
+
+def __to_tango_type_fmt(value):
+    dfmt = AttrDataFormat.SCALAR
+    value_t = type(value)
+    dtype = TO_TANGO_TYPE.get(value_t)
+    max_dim_x, max_dim_y = 1, 0
+    if dtype is None:
+        if isinstance(value, numpy.ndarray):
+            dtype = TO_TANGO_TYPE.get(value.dtype.name)
+            shape_l = len(value.shape)
+            if shape_l == 1:
+                dfmt = AttrDataFormat.SPECTRUM
+                max_dim_x = max(2**16, value.shape[0])
+            elif shape_l == 2:
+                dfmt = AttrDataFormat.IMAGE
+                max_dim_x = max(2**16, value.shape[0])
+                max_dim_y = max(2**16, value.shape[1])      
+        else:
+            dtype = CmdArgType.DevEncoded
+    return dtype, dfmt, max_dim_x, max_dim_y
+
+
+def create_tango_class(obj, tango_class_name=None):
+
+    obj_klass = obj.__class__
+    obj_klass_name = obj_klass.__name__
 
+    if tango_class_name is None:
+        tango_class_name = obj_klass_name
+
+    class DeviceDispatcher(Device):
+        __metaclass__ = DeviceMeta
+
+        TangoClassName = tango_class_name
+
+        def __init__(self, tango_class_obj, name):
+            Device.__init__(self, tango_class_obj, name)
+            self.__tango_obj = Server().get_tango_object(self.get_name())
+            self.__tango_obj.device = self
+
+        def init_device(self):
+            Device.init_device(self)
+            self.set_state(DevState.ON)
+
+    DeviceDispatcher.__name__ = tango_class_name
+    DeviceDispatcherClass = DeviceDispatcher.TangoClassClass
+
+    for name in dir(obj):
+        if name.startswith("_"):
+            continue
+#        logging.info("inspecting %s.%s", obj_klass_name, name)
+        try:
+            member = getattr(obj, name)
+        except:
+            logging.warning("Failed to inspect member '%s.%s'",
+                            obj_klass_name, name)
+            logging.debug("Details:", exc_info=1)
+        if inspect.isclass(member) or inspect.ismodule(member):
+            continue
+
+        if inspect.isroutine(member):
+            func = member
+            func_name = name
+            def _command(obj, func_name, param):
+                server = Server()
+                runner = server.runner
+                args, kwargs = loads(*param)
+                f = getattr(obj, func_name)
+                if runner:
+                    result = runner.execute(f, *args, **kwargs)
+                else:
+                    result = f(*args, **kwargs)
+                return server.dumps(result)
+            cmd = functools.partial(_command, obj, func_name)
+            cmd.__name__ = name
+            doc = func.__doc__
+            if doc is None:
+                doc = ""
+            cmd.__doc__ = doc
+            setattr(DeviceDispatcher, func_name, cmd)
+            DeviceDispatcherClass.cmd_list[name] = \
+                [[CmdArgType.DevEncoded, doc],
+                 [CmdArgType.DevEncoded, ""]]
+        else:
+            read_only = False
+            if hasattr(obj_klass, name):
+                kmember = getattr(obj_klass, name)
+                if inspect.isdatadescriptor(kmember):
+                    if kmember.fset is None:
+                        read_only = True
+                else:
+                    continue
+            value = member
+            dtype, fmt, x, y = __to_tango_type_fmt(value)
+            if dtype is None or dtype == CmdArgType.DevEncoded:
+                dtype = CmdArgType.DevEncoded
+                fmt = AttrDataFormat.SCALAR
+                def read(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()
+                    if runner:
+                        value = runner.execute(getattr, obj, name)
+                    else:
+                        value = getattr(obj, name)
+                    attr.set_value(*server.dumps(value))
+                def write(dev, attr):
+                    name = attr.get_name()
+                    value = attr.get_write_value()
+                    value = loads(*value)
+                    server = Server()
+                    runner = server.runner
+                    if runner:
+                        runner.execute(setattr, obj, name, value)
+                    else:
+                        setattr(obj, name, value)
+            else:
+                def read(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()
+                    if runner:
+                        value = runner.execute(getattr, obj, name)
+                    else:
+                        value = getattr(obj, name)                        
+                    attr.set_value(value)
+                def write(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()                        
+                    value = attr.get_write_value()
+                    if runner:
+                        runner.execute(setattr, obj, name, value)
+                    else:
+                        setattr(obj, name, value)                        
+            read.__name__ = "_read_" + name
+            setattr(DeviceDispatcher, read.__name__, read)
+
+            pars = dict(name=name, dtype=dtype, dformat=fmt,
+                        max_dim_x=x, max_dim_y=y, fget=read)
+            if read_only:
+                write = None
+            else:
+                write.__name__ = "_write" + name
+                pars['fset'] = write
+                setattr(DeviceDispatcher, write.__name__, write)
+            attr_data = AttrData.from_dict(pars)
+            DeviceDispatcherClass.attr_list[name] = attr_data
+    return DeviceDispatcher
+
+
+class _Server:
+
+    class TangoObject:
+
+        def __init__(self, obj, full_name, alias=None,
+                     tango_class_name=None):
+            self.full_name = full_name
+            self.alias = alias
+            self.class_name = obj.__class__.__name__
+            if tango_class_name is None:
+                tango_class_name = self.class_name
+            self.tango_class_name = tango_class_name
+            self.__obj = weakref.ref(obj)
+            self.__device = None
+
+        @property
+        def device(self):
+            if self.__device is None:
+                return None
+            return self.__device()
+
+        @device.setter
+        def device(self, dev):
+            self.__device = weakref.ref(dev)
+
+        @property
+        def obj(self):
+            return self.__obj()
+    
+    def __init__(self, server_name, server_type=None, port=None,
+                 event_loop_callback=None, post_init_callback=None,
+                 auto_clean=True, green_mode=None, tango_classes=None,
+                 protocol="pickle"):
+        self.__server_name = server_name
+        self.__server_type = server_type
+        self.__port = port
+        self.__event_loop_callback = event_loop_callback
+        self.__post_init_callback = post_init_callback
+        self.__util = None
+        self.__objects = {}
+        self.__running = False
+        self.__auto_clean = auto_clean
+        self.__green_mode = green_mode
+        self.__protocol = protocol
+        self.__tango_classes = _to_classes(tango_classes or [])
+        self.__tango_devices = []
+        if self.gevent_mode:
+            self.__runner = _create_runner()
+        else:
+            self.__runner = None
+        self.log = logging.getLogger("PyTango")
+    
+    def __build_args(self):
+        args = [self.server_type, self.__server_name]
+        if self.__port is not None:
+            args.extend(["-ORBendPoint",
+                         "giop:tcp::{0}".format(self.__port)])
+        return args
+
+    @property
+    def server_type(self):
+        server_type = self.__server_type
+        if server_type is None:
+            server_file = os.path.basename(sys.argv[0])
+            server_type = os.path.splitext(server_file)[0]
+        return server_type
+
+    @property
+    def server_instance(self):
+        return "{0}/{1}".format(self.server_type, self.__server_name)
+
+    @property
+    def tango_util(self):
+        if self.__util is None:
+            import PyTango
+            self.__util = PyTango.Util(self.__build_args())
+        return self.__util
+
+    @property
+    def green_mode(self):
+        gm = self.__green_mode
+        if gm is None:
+            from PyTango import get_green_mode            
+            gm = get_green_mode()
+        return gm
+
+    @green_mode.setter
+    def green_mode(self, gm):
+        if gm == self.__green_mode:
+            return
+        if self.__running:
+            raise RuntimeError("Cannot change green mode while "
+                               "server is running")
+        self.__green_mode = gm
+
+    @property
+    def gevent_mode(self):
+        return self.green_mode == GreenMode.Gevent
+
+    @property
+    def runner(self):
+        return self.__runner
+
+    def dumps(self, obj):
+        return dumps(self.__protocol, obj)
+
+    def get_tango_object(self, name):
+        return self.__objects.get(name.lower())
+    
+    def get_tango_class(self, tango_class_name):
+        for klass in self.__tango_classes:
+            if klass.TangoClassName == tango_class_name:
+                return klass
+
+    def __find_tango_class(self, key):
+        pass
+
+    def register_tango_device(self, klass, name):
+        if inspect.isclass(klass):
+            if isinstance(klass, Device):
+                kk, k, kname = Device.TangoClassClass, Device, Device.TangoClassName
+            else:
+                raise ValueError
+        else:
+            raise NotImplementedError
+        
+    def register_tango_class(self, klass):
+        if self.__running:
+            raise RuntimeError("Cannot create new Tango class while "
+                               "while server is running")
+        self.__tango_classes.append(klass)
+
+    def register_object(self, obj, name, tango_class_name=None):
+        slash_count = name.count("/")
+        if slash_count == 0:
+            alias = name
+            full_name = "{0}/{1}".format(self.server_instance, name)
+        elif slash_count == 2:
+            alias = None
+            full_name = name
+        else:
+            raise
+        tango_object = self.TangoObject(obj, full_name, alias,
+                                        tango_class_name=tango_class_name)
+        tango_class_name = tango_object.tango_class_name
+        tango_class = self.get_tango_class(tango_class_name)
+        if tango_class is None:
+            tango_class = create_tango_class(obj, tango_class_name)
+            self.register_tango_class(tango_class)
+        self.__objects[full_name.lower()] = tango_object
+        return tango_object
+
+    def _post_init_callback(self):
+        cb = self.__post_init_callback
+        if not cb:
+            return
+        if self.gevent_mode:
+            self.__runner.execute(cb)
+        else:
+            cb()            
+
+    def __clean_up(self):
+        self.log.debug("clean up")
+        server_instance = self.server_instance
+        db = Database()
+        if server_instance in db.get_server_list():
+            dserver_name = "dserver/{0}".format(server_instance)
+            if db.import_device(dserver_name).exported:
+                import PyTango
+                dserver = PyTango.DeviceProxy(dserver_name)
+                try:
+                    dserver.ping()
+                    raise Exception("Server already running")
+                except:
+                    logging.warning("Last time server was not properly shutdown!")
+            devices = db.get_device_class_list(server_instance)[::2]
+            for device in devices:
+                db.delete_device(device)
+                try:
+                    db.delete_device_alias(db.get_alias(device))
+                except:
+                    pass
+
+    def __clean_up_process(self):
+        if not self.__auto_clean:
+            return 
+        clean_up = _CLEAN_UP_TEMPLATE.format(server_instance=self.server_instance)
+        import subprocess
+        res = subprocess.call([sys.executable, "-c", clean_up])
+        if res:
+            self.log.error("Failed to cleanup")
+                
+    def __prepare(self):
+        self.log.debug("prepare")
+        self.__clean_up()
+        server_instance = self.server_instance
+        db = Database()
+        db_dev_infos = []
+        for obj_name, obj in self.__objects.items():
+            db_dev_info = DbDevInfo()
+            db_dev_info.server = server_instance
+            db_dev_info._class = obj.tango_class_name
+            db_dev_info.name = obj.full_name
+            db_dev_infos.append(db_dev_info)
+            db.add_device(db_dev_info)
+            if obj.alias:
+                db.put_device_alias(obj.full_name, obj.alias)
+
+    def __initialize(self):
+        self.log.debug("initialize")        
+        gevent_mode = self.gevent_mode
+        event_loop = self.__event_loop_callback
+        
+        util = self.tango_util
+        u_instance = util.instance()
+        
+        if gevent_mode:
+            if event_loop:
+                event_loop = functools.partial(self.__runner.execute,
+                                               event_loop)
+        if event_loop:
+            u_instance.server_set_event_loop(event_loop)
+
+        _add_classes(util, self.__tango_classes)
+        
+        if gevent_mode:
+            start_new_thread = self.__runner._threading.start_new_thread
+            tango_thread_id = start_new_thread(self.__tango_loop, ())
+
+    def __run(self, timeout=None):
+        if self.gevent_mode:
+            return self.__runner.run(timeout=timeout)
+        else:
+            self.__tango_loop()
+        
+    def __tango_loop(self):
+        self.log.debug("tango_loop")
+        self.__running = True
+        u_instance = self.tango_util.instance()
+        u_instance.server_init()
+        self._post_init_callback()
+        self.log.info("Ready to accept request")
+        u_instance.server_run()
+        if self.gevent_mode:
+            self.__runner.stop()
+        if self.__auto_clean:
+            self.__clean_up_process()
+        self.log.debug("Tango loop exit")        
+        
+    def run(self, timeout=None):
+        self.log.debug("run")        
+        gevent_mode = self.gevent_mode
+        running = self.__running
+        if not running:
+            self.__prepare()
+            self.__initialize()
+        else:
+            if not gevent_mode:
+                raise RuntimeError("Server is already running")
+        self.__run(timeout=timeout)
+
+
+__SERVER = None
+def Server(server_name=None, server_type=None, port=None,
+           event_loop_callback=None, post_init_callback=None,
+           auto_clean=True, green_mode=None):
+    """Experimental server class. Not part of the official API"""
+    
+    global __SERVER
+    if __SERVER is None:
+        if server_name is None:
+            raise ValueError("Must give a valid server name")
+        __SERVER = _Server(server_name,
+                           server_type=server_type, port=port,
+                           event_loop_callback=event_loop_callback,
+                           post_init_callback=post_init_callback,
+                           auto_clean=auto_clean,
+                           green_mode=green_mode)
+    return __SERVER

-- 
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