Revision a14c36f9 ncclient/glue.py

b/ncclient/glue.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
"TODO: docstring"
16

  
17
from cStringIO import StringIO
18
from threading import Thread, Lock
19
from xml.etree import cElementTree as ET
20

  
21
import logging
22
logger = logging.getLogger('ncclient.glue')
23

  
24

  
25
def parse_root(raw):
26
    '''Internal use.
27
    Parse the top-level element from XML string.
28
    
29
    Returns a `(tag, attributes)` tuple, where `tag` is a string representing
30
    the qualified name of the root element and `attributes` is an
31
    `{attribute: value}` dictionary.
32
    '''
33
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
34
    for event, element in ET.iterparse(fp, events=('start',)):
35
        return (element.tag, element.attrib)
36

  
37

  
38
class Subject(Thread):
39
    
40
    'Meant for subclassing by transport.Session'
41

  
42
    def __init__(self):
43
        "TODO: docstring"
44
        Thread.__init__(self)
45
        self.setDaemon(True)
46
        self._listeners = set() # TODO(?) weakref
47
        self._lock = Lock()
48
    
49
    def _dispatch_message(self, raw):
50
        "TODO: docstring"
51
        try:
52
            root = parse_root(raw)
53
        except Exception as e:
54
            logger.error('error parsing dispatch message: %s' % e)
55
            return
56
        with self._lock:
57
            listeners = list(self._listeners)
58
        for l in listeners:
59
            logger.debug('dispatching message to %r' % l)
60
            try:
61
                l.callback(root, raw)
62
            except Exception as e:
63
                logger.warning('[error] %r' % e)
64
    
65
    def _dispatch_error(self, err):
66
        "TODO: docstring"
67
        with self._lock:
68
            listeners = list(self._listeners)
69
        for l in listeners:
70
            logger.debug('dispatching error to %r' % l)
71
            try:
72
                l.errback(err)
73
            except Exception as e:
74
                logger.warning('error %r' % e)
75
    
76
    def add_listener(self, listener):
77
        "TODO: docstring"
78
        logger.debug('installing listener %r' % listener)
79
        with self._lock:
80
            self._listeners.add(listener)
81
    
82
    def remove_listener(self, listener):
83
        "TODO: docstring"
84
        logger.debug('discarding listener %r' % listener)
85
        with self._lock:
86
            self._listeners.discard(listener)
87
    
88
    def get_listener_instance(self, cls):
89
        '''This is useful when we want to maintain one listener of a particular
90
        type per subject i.e. a multiton.
91
        '''
92
        with self._lock:
93
            for listener in self._listeners:
94
                if isinstance(listener, cls):
95
                    return listener
96

  
97

  
98
class Listener(object):
99
    
100
    "TODO: docstring"
101
    
102
    def callback(self, root, raw):
103
        raise NotImplementedError
104
    
105
    def errback(self, err):
106
        raise NotImplementedError

Also available in: Unified diff