Revision 583c11f6 ncclient/transport/session.py

b/ncclient/transport/session.py
15 15
from Queue import Queue
16 16
from threading import Thread, Lock, Event
17 17

  
18
from ncclient import content
18 19
from ncclient.capabilities import Capabilities
19
from ncclient.content import parse_root
20

  
21
from hello import HelloHandler
22 20

  
23 21
import logging
24 22
logger = logging.getLogger('ncclient.transport.session')
25 23

  
26

  
27 24
class Session(Thread):
28 25
    
29
    "TODO: docstring"
30
    
31 26
    def __init__(self, capabilities):
32
        "Subclass constructor should call this"
33 27
        Thread.__init__(self)
34
        self.setDaemon(True)
35
        self._listeners = set() # TODO(?) weakref
28
        self.set_daemon(True)
29
        self._listeners = set() # 3.0's weakset ideal
36 30
        self._lock = Lock()
37
        self.setName('session')
31
        self.set_name('session')
38 32
        self._q = Queue()
39 33
        self._client_capabilities = capabilities
40 34
        self._server_capabilities = None # yet
......
46 40
    def _dispatch_message(self, raw):
47 41
        "TODO: docstring"
48 42
        try:
49
            root = parse_root(raw)
43
            root = content.parse_root(raw)
50 44
        except Exception as e:
51 45
            logger.error('error parsing dispatch message: %s' % e)
52 46
            return
......
77 71
        # callbacks
78 72
        def ok_cb(id, capabilities):
79 73
            self._id = id
80
            self._server_capabilities = Capabilities(capabilities)
74
            self._server_capabilities = capabilities
81 75
            init_event.set()
82 76
        def err_cb(err):
83 77
            error[0] = err
......
97 91
                     (self._id, self._server_capabilities))
98 92
    
99 93
    def add_listener(self, listener):
100
        "TODO: docstring"
101 94
        logger.debug('installing listener %r' % listener)
95
        if not isinstance(listener, SessionListener):
96
            raise SessionError("Listener must be a SessionListener type")
102 97
        with self._lock:
103 98
            self._listeners.add(listener)
104 99
    
105 100
    def remove_listener(self, listener):
106
        "TODO: docstring"
107 101
        logger.debug('discarding listener %r' % listener)
108 102
        with self._lock:
109 103
            self._listeners.discard(listener)
110 104
    
111 105
    def get_listener_instance(self, cls):
112
        '''This is useful when we want to maintain one listener of a particular
113
        type per subject i.e. a multiton.
114
        '''
115 106
        with self._lock:
116 107
            for listener in self._listeners:
117 108
                if isinstance(listener, cls):
118 109
                    return listener
119 110
    
120 111
    def connect(self, *args, **kwds):
121
        "Subclass implements"
122 112
        raise NotImplementedError
123 113

  
124 114
    def run(self):
125
        "Subclass implements"
126 115
        raise NotImplementedError
127 116
    
128 117
    def send(self, message):
129
        "TODO: docstring"
130 118
        logger.debug('queueing %s' % message)
131 119
        self._q.put(message)
132 120
    
......
146 134
    
147 135
    @property
148 136
    def id(self):
137
        "`session-id` if session is initialized, :const:`None` otherwise"
149 138
        return self._id
150 139
    
151 140
    @property
152 141
    def can_pipeline(self):
153 142
        return True
143

  
144

  
145
class SessionListener(object):
146
    
147
    def callback(self, root, raw):
148
        raise NotImplementedError
149
    
150
    def errback(self, ex):
151
        raise NotImplementedError
152

  
153

  
154
class HelloHandler(SessionListener):
155
    
156
    def __init__(self, init_cb, error_cb):
157
        self._init_cb = init_cb
158
        self._error_cb = error_cb
159
    
160
    def callback(self, root, raw):
161
        if content.unqualify(root[0]) == 'hello':
162
            try:
163
                id, capabilities = HelloHandler.parse(raw)
164
            except Exception as e:
165
                self._error_cb(e)
166
            else:
167
                self._init_cb(id, capabilities)
168
    
169
    def errback(self, err):
170
        self._error_cb(err)
171
    
172
    @staticmethod
173
    def build(capabilities):
174
        "Given a list of capability URI's returns <hello> message XML string"
175
        spec = {
176
            'tag': content.qualify('hello'),
177
            'subtree': [{
178
                'tag': 'capabilities',
179
                'subtree': # this is fun :-)
180
                    [{'tag': 'capability', 'text': uri} for uri in capabilities]
181
                }]
182
            }
183
        return content.dtree2xml(spec)
184
    
185
    @staticmethod
186
    def parse(raw):
187
        "Returns tuple of (session-id (str), capabilities (Capabilities)"
188
        sid, capabilities = 0, []
189
        root = content.xml2ele(raw)
190
        for child in root.getchildren():
191
            tag = content.unqualify(child.tag)
192
            if tag == 'session-id':
193
                sid = child.text
194
            elif tag == 'capabilities':
195
                for cap in child.getchildren():
196
                    if content.unqualify(cap.tag) == 'capability':
197
                        capabilities.append(cap.text)
198
        return sid, Capabilities(capabilities)

Also available in: Unified diff