fixes
[ncclient] / ncclient / rpc / rpc.py
1 # Copyright 2009 Shikhar Bhushan
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 from threading import Event, Lock
16 from uuid import uuid1
17 from weakref import WeakValueDictionary
18
19 from ncclient.content import XMLConverter
20 from ncclient.content import qualify as _
21 from ncclient.content import unqualify as __
22 from ncclient.glue import Listener
23
24 from listener import RPCReplyListener
25 from reply import RPCReply
26
27 import logging
28 logger = logging.getLogger('ncclient.rpc')
29
30 class RPC(object):
31     
32     DEPENDS = []
33     REPLY_CLS = RPCReply
34     
35     def __init__(self, session, async=False, timeout=None):
36         if not session.can_pipeline:
37             raise UserWarning('Asynchronous mode not supported for this device/session')
38         self._session = session
39         try:
40             for cap in self.DEPENDS:
41                 self._assert(cap)
42         except AttributeError:
43             pass        
44         self._async = async
45         self._timeout = timeout
46         self._id = uuid1().urn
47         self._listener = RPCReplyListener(session)
48         self._listener.register(self._id, self)
49         self._reply = None
50         self._reply_event = Event()
51     
52     def _build(self, opspec, encoding='utf-8'):
53         "TODO: docstring"
54         spec = {
55             'tag': _('rpc'),
56             'attributes': {'message-id': self._id},
57             'children': opspec
58             }
59         return XMLConverter(spec).to_string(encoding)
60     
61     def _request(self, op):
62         req = self._build(op)
63         self._session.send(req)
64         if self._async:
65             return self._reply_event
66         else:
67             self._reply_event.wait(self._timeout)
68             if self._reply_event.isSet():
69                 self._reply.parse()
70                 return self._reply
71             else:
72                 raise ReplyTimeoutError
73     
74     def _delivery_hook(self):
75         'For subclasses'
76         pass
77     
78     def _assert(self, capability):
79         if capability not in self._session.server_capabilities:
80             raise MissingCapabilityError('Server does not support [%s]' % cap)
81     
82     def deliver(self, raw):
83         self._reply = self.REPLY_CLS(raw)
84         self._delivery_hook()
85         self._reply_event.set()
86     
87     @property
88     def has_reply(self):
89         return self._reply_event.isSet()
90     
91     @property
92     def reply(self):
93         return self._reply
94     
95     @property
96     def id(self):
97         return self._id
98     
99     @property
100     def session(self):
101         return self._session
102     
103     @property
104     def reply_event(self):
105         return self._reply_event
106     
107     def set_async(self, bool): self._async = bool
108     async = property(fget=lambda self: self._async, fset=set_async)
109     
110     def set_timeout(self, timeout): self._timeout = timeout
111     timeout = property(fget=lambda self: self._timeout, fset=set_timeout)