Revision 4f650d54 ncclient/transport/session.py

b/ncclient/transport/session.py
22 22
logger = logging.getLogger('ncclient.transport.session')
23 23

  
24 24
class Session(Thread):
25
    "This is a base class for use by protocol implementations"
26
    
25
    "Base class for use by transport protocol implementations."
26

  
27 27
    def __init__(self, capabilities):
28 28
        Thread.__init__(self)
29
        self.set_daemon(True)
30
        self._listeners = set() # 3.0's weakset ideal
29
        self.setDaemon(True)
30
        self._listeners = set() # 3.0's weakset would be ideal
31 31
        self._lock = Lock()
32
        self.set_name('session')
32
        self.setName('session')
33 33
        self._q = Queue()
34 34
        self._client_capabilities = capabilities
35 35
        self._server_capabilities = None # yet
......
37 37
        self._connected = False # to be set/cleared by subclass implementation
38 38
        logger.debug('%r created: client_capabilities=%r' %
39 39
                     (self, self._client_capabilities))
40
    
40

  
41 41
    def _dispatch_message(self, raw):
42 42
        try:
43 43
            root = content.parse_root(raw)
......
52 52
                l.callback(root, raw)
53 53
            except Exception as e:
54 54
                logger.warning('[error] %r' % e)
55
    
55

  
56 56
    def _dispatch_error(self, err):
57 57
        with self._lock:
58 58
            listeners = list(self._listeners)
......
62 62
                l.errback(err)
63 63
            except Exception as e:
64 64
                logger.warning('error %r' % e)
65
    
65

  
66 66
    def _post_connect(self):
67 67
        "Greeting stuff"
68 68
        init_event = Event()
......
86 86
        self.remove_listener(listener)
87 87
        if error[0]:
88 88
            raise error[0]
89
        logger.info('initialized: session-id=%s | server_capabilities=%s' % (self._id, self._server_capabilities))
90
    
89
        logger.info('initialized: session-id=%s | server_capabilities=%s' %
90
                    (self._id, self._server_capabilities))
91

  
91 92
    def add_listener(self, listener):
92
        """Register a listener that will be notified of incoming messages and errors.
93
        
94
        :type listener: :class:`SessionListener`
93
        """Register a listener that will be notified of incoming messages and
94
        errors.
95

  
96
        :arg listener: :class:`SessionListener`
95 97
        """
96 98
        logger.debug('installing listener %r' % listener)
97 99
        if not isinstance(listener, SessionListener):
98 100
            raise SessionError("Listener must be a SessionListener type")
99 101
        with self._lock:
100 102
            self._listeners.add(listener)
101
    
103

  
102 104
    def remove_listener(self, listener):
103
        "Unregister some listener; ignoring if the listener was never registered."
105
        """Unregister some listener; ignore if the listener was never
106
        registered."""
104 107
        logger.debug('discarding listener %r' % listener)
105 108
        with self._lock:
106 109
            self._listeners.discard(listener)
107
    
110

  
108 111
    def get_listener_instance(self, cls):
109
        """If a listener of the specified type is registered, returns it. This is useful when it is desirable to have only one instance of a particular type per session, i.e. a multiton.
110
        
111
        :type cls: :class:`type`
112
        :rtype: :class:`SessionListener` or :const:`None`
112
        """If a listener of the sspecified type is registered, returns the
113
        instance. This is useful when it is desirable to have only one instance
114
        of a particular type per session, i.e. a multiton.
115

  
116
        :arg cls: class of the listener
113 117
        """
114 118
        with self._lock:
115 119
            for listener in self._listeners:
116 120
                if isinstance(listener, cls):
117 121
                    return listener
118
    
122

  
119 123
    def connect(self, *args, **kwds): # subclass implements
120 124
        raise NotImplementedError
121 125

  
122 126
    def run(self): # subclass implements
123 127
        raise NotImplementedError
124
    
128

  
125 129
    def send(self, message):
126
        """
127
        :param message: XML document
128
        :type message: string
130
        """Send the supplied *message* to NETCONF server.
131

  
132
        :arg message: an XML document
133

  
134
        :type message: :obj:`string`
129 135
        """
130 136
        logger.debug('queueing %s' % message)
131 137
        self._q.put(message)
132
    
138

  
133 139
    ### Properties
134 140

  
135 141
    @property
136 142
    def connected(self):
137
        ":rtype: bool"
143
        "Connection status of the session."
138 144
        return self._connected
139 145

  
140 146
    @property
141 147
    def client_capabilities(self):
142
        ":rtype: :class:`Capabilities`"
148
        "Client's :class:`Capabilities`"
143 149
        return self._client_capabilities
144
    
150

  
145 151
    @property
146 152
    def server_capabilities(self):
147
        ":rtype: :class:`Capabilities` or :const:`None`"
153
        "Server's :class:`Capabilities`"
148 154
        return self._server_capabilities
149
    
155

  
150 156
    @property
151 157
    def id(self):
152
        ":rtype: :obj:`string` or :const:`None`"
158
        """A :obj:`string` representing the `session-id`. If the session has not
159
        been initialized it will be :const:`None`"""
153 160
        return self._id
154
    
161

  
155 162
    @property
156 163
    def can_pipeline(self):
157
        ":rtype: :obj:`bool`"
164
        "Whether this session supports pipelining"
158 165
        return True
159 166

  
160 167

  
161 168
class SessionListener(object):
162
    
163
    """'Listen' to incoming messages on a NETCONF :class:`Session`
164
    
169

  
170
    """Base class for :class:`Session` listeners, which are notified when a new
171
    NETCONF message is received or an error occurs.
172

  
165 173
    .. note::
166
        Avoid computationally intensive tasks in the callbacks.
174
        Avoid time-intensive tasks in a callback's context.
167 175
    """
168
    
176

  
169 177
    def callback(self, root, raw):
170
        """Called when a new XML document is received. The `root` argument allows the callback to determine whether it wants to further process the document.
171
        
172
        :param root: tuple of (tag, attrs) where tag is the qualified name of the root element and attrs is a dictionary of its attributes (also qualified names)
173
        :param raw: XML document
174
        :type raw: string
178
        """Called when a new XML document is received. The `root` argument
179
        allows the callback to determine whether it wants to further process the
180
        document.
181

  
182
        :arg root: is a tuple of `(tag, attributes)` where `tag` is the qualified name of the root element and `attributes` is a dictionary of its attributes (also qualified names)
183

  
184
        :arg raw: XML document
185
        :type raw: :obj:`string`
175 186
        """
176 187
        raise NotImplementedError
177
    
188

  
178 189
    def errback(self, ex):
179 190
        """Called when an error occurs.
180
        
181
        :type ex: :class:`Exception`
191

  
192
        :type ex: :exc:`Exception`
182 193
        """
183 194
        raise NotImplementedError
184 195

  
185 196

  
186 197
class HelloHandler(SessionListener):
187
    
198

  
188 199
    def __init__(self, init_cb, error_cb):
189 200
        self._init_cb = init_cb
190 201
        self._error_cb = error_cb
191
    
202

  
192 203
    def callback(self, root, raw):
193 204
        if content.unqualify(root[0]) == 'hello':
194 205
            try:
......
197 208
                self._error_cb(e)
198 209
            else:
199 210
                self._init_cb(id, capabilities)
200
    
211

  
201 212
    def errback(self, err):
202 213
        self._error_cb(err)
203
    
214

  
204 215
    @staticmethod
205 216
    def build(capabilities):
206 217
        "Given a list of capability URI's returns <hello> message XML string"
......
213 224
                }]
214 225
            }
215 226
        return content.dtree2xml(spec)
216
    
227

  
217 228
    @staticmethod
218 229
    def parse(raw):
219 230
        "Returns tuple of (session-id (str), capabilities (Capabilities)"

Also available in: Unified diff