rpc, rpcreply, listener... worked out now; still to test
authorShikhar Bhushan <shikhar@schmizz.net>
Wed, 29 Apr 2009 06:13:25 +0000 (06:13 +0000)
committerShikhar Bhushan <shikhar@schmizz.net>
Wed, 29 Apr 2009 06:13:25 +0000 (06:13 +0000)
git-svn-id: http://ncclient.googlecode.com/svn/trunk@83 6dbcf712-26ac-11de-a2f3-1373824ab735

ncclient/operations/listeners.py [new file with mode: 0644]
ncclient/operations/notification.py
ncclient/operations/reply.py
ncclient/operations/rpc.py
ncclient/operations/session.py
ncclient/operations/subscribe.py

diff --git a/ncclient/operations/listeners.py b/ncclient/operations/listeners.py
new file mode 100644 (file)
index 0000000..e69de29
index 94b71c5..2611586 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from glue import Listener
 
 class Notification:
     
     pass
-
-class NotificationListener(Listener):
-    
-    pass
index e052acb..7bf81cb 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-def parse():
-    
-    pass
+from xml.etree import cElementTree as ET
 
+from ncclient.content import multiqualify as _
+from ncclient.content import unqualify as __
 
 class RPCReply:
     
-    def __init__(self, event):
+    def __init__(self, raw):
         self._raw = None
-        self._errs = None
+        self._parsed = False
+        self._errors = []
     
-    def __str__(self):
+    def __repr__(self):
         return self._raw
     
     def parse(self):
-        if not self._parsed:
-            ok = RPCReplyParser.parse(self._raw)
-            for err in errs:
-                self._errs.append(RPCError(*err))
-            self._parsed = True
+        root = ET.fromstring(raw) # <rpc-reply> element
+        
+        # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
+        oktags = _('ok')
+        for oktag in oktags:
+            if root.find(oktag) is not None:
+                return
+        
+        # create RPCError objects from <rpc-error> elements
+        errtags = _('rpc-error')
+        for errtag in errtags:
+            for err in root.getiterator(errtag): # a particular <rpc-error>
+                d = {}
+                for err_detail in err.getchildren(): # <error-type> etc..
+                    d[__(err_detail)] = err_detail.text
+                self._errors.append(RPCError(d))
+            if self._errors:
+                break
     
     @property
     def raw(self):
         return self._raw
     
     @property
-    def parsed(self):
-        return self._parsed
-    
-    @property
     def ok(self):
-        return True if self._parsed and not self._errs else False
+        if not self._parsed:
+            self.parse()
+        return bool(self._errors) # empty list = false
     
     @property
     def errors(self):
-        return self._errs
+        'List of RPCError objects. Will be empty if no <rpc-error> elements in reply.'
+        if not self._parsed:
+            self.parse()
+        return self._errors
 
 
 class RPCError(Exception): # raise it if you like
     
-    def __init__(self, raw, err_dict):
-        self._raw = raw
+    def __init__(self, err_dict):
         self._dict = err_dict
-    
-    def __str__(self):
-        # TODO
-        return self._raw
-    
-    def __dict__(self):
-        return self._dict
+        if self.message is not None:
+            Exception.__init__(self, self.message)
+        else:
+            Exception.__init__(self)
     
     @property
     def raw(self):
-        return self._raw
+        return self._element.tostring()
     
     @property
     def type(self):
-        return self._dict.get('type', None)
+        return self.get('error-type', None)
     
     @property
     def severity(self):
-        return self._dict.get('severity', None)
+        return self.get('error-severity', None)
     
     @property
     def tag(self):
-        return self._dict.get('tag', None)
+        return self.get('error-tag', None)
     
     @property
     def path(self):
-        return self._dict.get('path', None)
+        return self.get('error-path', None)
     
     @property
     def message(self):
-        return self._dict.get('message', None)
+        return self.get('error-message', None)
     
     @property
     def info(self):
-        return self._dict.get('info', None)
-
+        return self.get('error-info', None)
 
-class RPCReplyListener(Listener):
+    ## dictionary interface
     
-    # TODO - determine if need locking
+    __getitem__ = lambda self, key: self._dict.__getitem__(key)
     
-    # one instance per subject    
-    def __new__(cls, subject):
-        instance = subject.get_listener_instance(cls)
-        if instance is None:
-            instance = object.__new__(cls)
-            instance._id2rpc = WeakValueDictionary()
-            instance._errback = None
-            subject.add_listener(instance)
-        return instance
+    __iter__ = lambda self: self._dict.__iter__()
     
-    def __str__(self):
-        return 'RPCReplyListener'
+    __contains__ = lambda self, key: self._dict.__contains__(key)
     
-    def set_errback(self, errback):
-        self._errback = errback
-
-    def register(self, msgid, rpc):
-        self._id2rpc[msgid] = rpc
-    
-    def callback(self, root, raw):
-        tag, attrs = root
-        if __(tag) != 'rpc-reply':
-            return
-        for key in attrs:
-            if __(key) == 'message-id':
-                id = attrs[key]
-                try:
-                    rpc = self._id2rpc[id]
-                    rpc.deliver(raw)
-                except:
-                    logger.warning('RPCReplyListener.callback: no RPC '
-                                   + 'registered for message-id: [%s]' % id)
-                break
-        else:
-            logger.warning('<rpc-reply> without message-id received: %s' % raw)
+    keys = lambda self: self._dict.keys()
+    
+    get = lambda self, key, default: self._dict.get(key, default)
+        
+    iteritems = lambda self: self._dict.iteritems()
+    
+    iterkeys = lambda self: self._dict.iterkeys()
+    
+    itervalues = lambda self: self._dict.itervalues()
+    
+    values = lambda self: self._dict.values()
+    
+    items = lambda self: self._dict.items()
     
-    def errback(self, err):
-        logger.error('RPCReplyListener.errback: %r' % err)
-        if self._errback is not None:
-            self._errback(err)
+    __repr__ = lambda self: repr(self._dict)
\ No newline at end of file
index 131f704..8cf978e 100644 (file)
 from threading import Event, Lock
 from uuid import uuid1
 
-from . import logger
 from ncclient.content import TreeBuilder, BASE_NS
-from reply import RPCReply, RPCReplyListener
+from ncclient.glue import Listener
+
+from . import logger
+from reply import RPCReply
+
 
 class RPC(object):
     
@@ -26,37 +29,30 @@ class RPC(object):
         self._id = uuid1().urn
         self._listener = RPCReplyListener(session)
         self._listener.register(self._id, self)
-        self._reply = RPCReply()
+        self._reply = None
         self._reply_event = Event()
     
     def _build(self, op, encoding='utf-8'):
         if isinstance(op, dict):
             return self.build_from_spec(self._id, op, encoding)
-        elif isinstance(op, basestring): 
-            return self.build_from_string(self._id, op, encoding)
         else:
-            raise ValueError('Inappropriate value of tree spec.')
+            return self.build_from_string(self._id, op, encoding)
     
     def _request(self, op):
         req = self._build(op)
         self._session.send(req)
-        if reply_event is not None: # if we were provided an Event to use
-            self._reply_event = reply_event
-        else: # otherwise, block till response received and return it
-            self._reply_event = Event()
+        if async:
             self._reply_event.wait()
             self._reply.parse()
-        return self._reply
+            return self._reply
     
-    def request(self, *args, **kwds):
-        raise NotImplementedError
+    def deliver(self, raw):
+        self._reply = RPCReply(raw)
+        self._reply_event.set()
     
     @property
     def has_reply(self):
-        try:
-            return self._reply_event.isSet()
-        except TypeError: # reply_event is None
-            return False
+        return self._reply_event.isSet()
     
     @property
     def reply(self):
@@ -70,6 +66,10 @@ class RPC(object):
     def session(self):
         return self._session
     
+    @property
+    def reply_event(self):
+        return self._reply_event
+    
     @staticmethod
     def build_from_spec(msgid, opspec, encoding='utf-8'):
         "TODO: docstring"
@@ -87,3 +87,49 @@ class RPC(object):
         doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
                (msgid, BASE_NS, opstr)).encode(encoding)
         return '%s%s' % (decl, doc)
+
+
+class RPCReplyListener(Listener):
+    
+    # TODO - determine if need locking
+    
+    # one instance per subject    
+    def __new__(cls, subject):
+        instance = subject.get_listener_instance(cls)
+        if instance is None:
+            instance = object.__new__(cls)
+            instance._id2rpc = WeakValueDictionary()
+            instance._errback = None
+            subject.add_listener(instance)
+        return instance
+    
+    def __str__(self):
+        return 'RPCReplyListener'
+    
+    def set_errback(self, errback):
+        self._errback = errback
+
+    def register(self, msgid, rpc):
+        self._id2rpc[msgid] = rpc
+    
+    def callback(self, root, raw):
+        tag, attrs = root
+        if __(tag) != 'rpc-reply':
+            return
+        for key in attrs:
+            if __(key) == 'message-id':
+                id = attrs[key]
+                try:
+                    rpc = self._id2rpc[id]
+                    rpc.deliver(raw)
+                except:
+                    logger.warning('RPCReplyListener.callback: no RPC '
+                                   + 'registered for message-id: [%s]' % id)
+                break
+        else:
+            logger.warning('<rpc-reply> without message-id received: %s' % raw)
+    
+    def errback(self, err):
+        logger.error('RPCReplyListener.errback: %r' % err)
+        if self._errback is not None:
+            self._errback(err)
\ No newline at end of file
index da624a3..49c85bb 100644 (file)
@@ -25,9 +25,8 @@ class CloseSession(RPC):
     
     def deliver(self, reply):
         RPC.deliver(self, reply)
-        # can't be too huge a reply, should be ok to parse in callback
-        self._reply.parse()
-        if self._reply.ok:
+        # can't be too huge, should be ok to parse in callback
+        if self._reply.ok: # (implicitly parse)
             self._session.expect_close()
         self._session.close()
     
index f06309b..675808a 100644 (file)
 
 from rpc import RPC
 
+from ncclient.glue import Listener
+
+from notification import Notification
+
 class CreateSubscription(RPC):    
+    
     pass
 
+
+class NotificationListener(Listener):
+    
+    pass
\ No newline at end of file