diff src/tmp/wokkel/rsm.py @ 1771:b77dc676a4df

tmp (wokkel/rsm): various improvments: - restored original tmp.wokkel.pubsub.PubSubService._toResponse_items - changed arguments order in RSMRequest.__init__ to have most common arguments first - added __str__ methods - better parsing/toElement - better handling of optional elements/attributes (count/index) - _toResponse_items handler RSM set elements without modifying original pubsub _toResponse_items - renamed parse to fromElement for coherency
author Goffi <goffi@goffi.org>
date Tue, 05 Jan 2016 23:20:20 +0100
parents 7debf3a4bf14
children 6e867caf4621
line wrap: on
line diff
--- a/src/tmp/wokkel/rsm.py	Sun Jan 03 18:36:41 2016 +0100
+++ b/src/tmp/wokkel/rsm.py	Tue Jan 05 23:20:20 2016 +0100
@@ -45,7 +45,7 @@
     """
 
 
-class RSMRequest():
+class RSMRequest(object):
     """
     A Result Set Management request.
 
@@ -62,28 +62,28 @@
     @itype before: C{unicode}
     """
 
-    def __init__(self, max_=10, index=None, after=None, before=None):
-        max_ = int(max_)
-        assert max_ >= 0
-        self.max = max_
+    def __init__(self, max_=10, after=None, before=None, index=None):
+        self.max = int(max_)
 
         if index is not None:
             assert after is None and before is None
             index = int(index)
-            assert index >= 0
         self.index = index
 
         if after is not None:
             assert before is None
-            assert isinstance(after, unicode)
+            assert isinstance(after, basestring)
         self.after = after
 
         if before is not None:
-            assert isinstance(before, unicode)
+            assert isinstance(before, basestring)
         self.before = before
 
+    def __str__(self):
+        return "RSM Request: max={0.max} after={0.after} before={0.before} index={0.index}".format(self)
+
     @classmethod
-    def parse(cls, element):
+    def fromElement(cls, element):
         """Parse the given request element.
 
         @param element: request containing a set element.
@@ -97,17 +97,43 @@
         except StopIteration:
             raise RSMNotFoundError()
 
-        request = RSMRequest()
-        for elt in set_elt.elements():
-            if elt.name in ('before', 'after'):
-                setattr(request, elt.name, ''.join(elt.children))
-            elif elt.name in ('max', 'index'):
-                setattr(request, elt.name, int(''.join(elt.children)))
+        try:
+            before_elt = set_elt.elements(NS_RSM, 'before').next()
+        except StopIteration:
+            before = None
+        else:
+            before = unicode(before_elt)
+
+        try:
+            after_elt = set_elt.elements(NS_RSM, 'after').next()
+        except StopIteration:
+            after = None
+        else:
+            after = unicode(after_elt)
 
-        if request.max is None:
+        try:
+            max_elt = set_elt.elements(NS_RSM, 'max').next()
+        except StopIteration:
+            # FIXME: even if it doesn't make a lot of sense without it
+            #        <max/> element is not mandatory in XEP-0059
             raise RSMError("RSM request is missing its 'max' element")
+        else:
+            try:
+                max_ = int(unicode(max_elt))
+            except ValueError:
+                raise RSMError("bad value for 'max' element")
 
-        return request
+        try:
+            index_elt = set_elt.elements(NS_RSM, 'index').next()
+        except StopIteration:
+            index = None
+        else:
+            try:
+                index = int(unicode(index_elt))
+            except ValueError:
+                raise RSMError("bad value for 'index' element")
+
+        return RSMRequest(max_, after, before, index)
 
     def toElement(self):
         """
@@ -116,19 +142,19 @@
         @rtype: L{domish.Element}
         """
         set_elt = domish.Element((NS_RSM, 'set'))
-        set_elt.addElement('max').addContent(unicode(self.max))
+        set_elt.addElement('max', content=unicode(self.max))
 
         if self.index is not None:
-            set_elt.addElement('index').addContent(unicode(self.index))
+            set_elt.addElement('index', content=unicode(self.index))
 
         if self.before is not None:
             if self.before == '':  # request the last page
                 set_elt.addElement('before')
             else:
-                set_elt.addElement('before').addContent(self.before)
+                set_elt.addElement('before', content=self.before)
 
         if self.after is not None:
-            set_elt.addElement('after').addContent(self.after)
+            set_elt.addElement('after', content=self.after)
 
         return set_elt
 
@@ -150,38 +176,45 @@
         return set_elt
 
 
-class RSMResponse():
+class RSMResponse(object):
     """
     A Result Set Management response.
 
-    @ivar count: total number of items.
-    @itype count: C{int}
-
-    @ivar index: starting index of the returned page.
-    @itype index: C{int}
-
     @ivar first: ID of the first element of the returned page.
     @itype first: C{unicode}
 
     @ivar last: ID of the last element of the returned page.
     @itype last: C{unicode}
+
+    @ivar index: starting index of the returned page.
+    @itype index: C{int}
+
+    @ivar count: total number of items.
+    @itype count: C{int}
+
     """
 
-    def __init__(self, count=0, index=None, first=None, last=None):
-        assert isinstance(count, int) and count >= 0
-        self.count = count
-        if index is not None:
-            assert isinstance(index, int) and index >= 0
-            assert isinstance(first, unicode)
-            assert isinstance(last, unicode)
-        else:
-            assert first is None and last is None
-        self.index = index
+    def __init__(self, first=None, last=None, index=None, count=None):
+        if first is None:
+            assert last is None and index is None
+        if last is None:
+            assert first is None
         self.first = first
         self.last = last
+        if count is not None:
+            self.count = int(count)
+        else:
+            self.count = None
+        if index is not None:
+            self.index = int(index)
+        else:
+            self.index = None
+
+    def __str__(self):
+        return "RSM Request: first={0.first} last={0.last} index={0.index} count={0.count}".format(self)
 
     @classmethod
-    def parse(cls, element):
+    def fromElement(cls, element):
         """Parse the given response element.
 
         @param element: response element.
@@ -195,19 +228,43 @@
         except StopIteration:
             raise RSMNotFoundError()
 
-        response = RSMResponse()
-        for elt in list(set_elt.elements()):
-            if elt.name in ('first', 'last'):
-                setattr(response, elt.name, ''.join(elt.children))
-                if elt.name == 'first':
-                    response.index = int(elt.getAttribute("index"))
-            elif elt.name == 'count':
-                response.count = int(''.join(elt.children))
+        try:
+            first_elt = set_elt.elements(NS_RSM, 'first').next()
+        except StopIteration:
+            first = None
+            index = None
+        else:
+            first = unicode(first_elt)
+            try:
+                index = int(first_elt['index'])
+            except KeyError:
+                index = None
+            except ValueError:
+                raise RSMError("bad index in RSM response")
 
-        if response.count is None:
-            raise RSMError("RSM response is missing its 'count' element")
+        try:
+            last_elt = set_elt.elements(NS_RSM, 'last').next()
+        except StopIteration:
+            if first is not None:
+                raise RSMError("RSM response is missing its 'last' element")
+            else:
+                last = None
+        else:
+            if first is None:
+                raise RSMError("RSM response is missing its 'first' element")
+            last = unicode(last_elt)
 
-        return response
+        try:
+            count_elt = set_elt.elements(NS_RSM, 'count').next()
+        except StopIteration:
+            count = None
+        else:
+            try:
+                count = int(unicode(count_elt))
+            except ValueError:
+                raise RSMError("invalid count in RSM response")
+
+        return RSMResponse(first, last, index, count)
 
     def toElement(self):
         """
@@ -216,14 +273,15 @@
         @rtype: L{domish.Element}
         """
         set_elt = domish.Element((NS_RSM, 'set'))
-        set_elt.addElement('count').addContent(unicode(self.count))
+        if self.first is not None:
+            first_elt = set_elt.addElement('first', content=self.first)
+            if self.index is not None:
+                first_elt['index'] = unicode(self.index)
 
-        if self.index is not None:
-            first_elt = set_elt.addElement('first')
-            first_elt.addContent(self.first)
-            first_elt['index'] = unicode(self.index)
+            set_elt.addElement('last', content=self.last)
 
-            set_elt.addElement('last').addContent(self.last)
+        if self.count is not None:
+            set_elt.addElement('count', content=unicode(self.count))
 
         return set_elt
 
@@ -247,7 +305,7 @@
         @rtype: C{dict} binding C{unicode} to C{unicode}
         """
         result = {}
-        for attr in ('count', 'index', 'first', 'last'):
+        for attr in ('first', 'last', 'index', 'count'):
             value = getattr(self, attr)
             if value is not None:
                 result[attr] = unicode(value)
@@ -270,7 +328,7 @@
 
     def _parse_rsm(self, verbElement):
         try:
-            self.rsm = RSMRequest.parse(verbElement.parent)
+            self.rsm = RSMRequest.fromElement(verbElement.parent)
         except RSMNotFoundError:
             self.rsm = None
 
@@ -331,7 +389,7 @@
 
             if request.rsm:
                 try:
-                    response = RSMResponse.parse(iq.pubsub)
+                    response = RSMResponse.fromElement(iq.pubsub)
                     if response is not None:
                         self._rsm_responses[ext_data['id']] = response
                 except RSMNotFoundError:  # target pubsub server doesn't support RSM
@@ -364,15 +422,18 @@
 
     _request_class = PubSubRequest
 
-    def _toResponse_items(self, result, resource, request):
-        response = pubsub.PubSubService._toResponse_items(self, result,
-                                                          resource, request)
-        for item in result:
-            if isinstance(item, tuple):
-                # XXX: see sat_pubsub.pgsql_storage.LeafNode.getItemsById return value
-                item = item[0]
-            if item.name == "set":
-                response.addChild(item)
+    def _toResponse_items(self, elts, resource, request):
+        # default method only manage <item/> elements
+        # but we need to add RSM set element
+        rsm_elt = None
+        for idx, elt in enumerate(reversed(elts)):
+            if elt.name == "set" and elt.uri == NS_RSM:
+                rsm_elt = elts.pop(-1-idx)
                 break
 
+        response = pubsub.PubSubService._toResponse_items(self, elts,
+                                                          resource, request)
+        if rsm_elt is not None:
+            response.addChild(rsm_elt)
+
         return response