changeset 394:dc83b1d837cf

backend: fixed recursion error on getFullItem: getFullItem was doing a deepcopy of the item which could lead to infinite recursion if element had children. This has been fixed by doing a specialised copy function. For the same reason, an itemDataCopy function has also been implemented.
author Goffi <goffi@goffi.org>
date Mon, 18 Feb 2019 19:12:04 +0100
parents 728b08c0d000
children 9bc3fbf3e685
files src/backend.py
diffstat 1 files changed, 38 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/backend.py	Fri Feb 15 22:06:19 2019 +0100
+++ b/src/backend.py	Mon Feb 18 19:12:04 2019 +0100
@@ -61,6 +61,7 @@
 publish-subscribe protocol.
 """
 
+import copy
 import uuid
 
 from zope.interface import implements
@@ -84,8 +85,6 @@
 from sat_pubsub import const
 from sat_pubsub import container
 
-from copy import deepcopy
-
 
 def _getAffiliation(node, entity):
     d = node.getAffiliation(entity)
@@ -93,6 +92,37 @@
     return d
 
 
+def elementCopy(element):
+    """Make a copy of a domish.Element
+
+    The copy will have its own children list, so other elements
+    can be added as direct children without modifying orignal one.
+    Children are not deeply copied, so if an element is added to a child or grandchild,
+    it will also affect original element.
+    @param element(domish.Element): Element to clone
+    """
+    new_elt = domish.Element(
+        (element.uri, element.name),
+        defaultUri = element.defaultUri,
+        attribs = element.attributes,
+        localPrefixes = element.localPrefixes)
+    new_elt.parent = element.parent
+    new_elt.children = element.children[:]
+    return new_elt
+
+
+def itemDataCopy(item_data):
+    """Make a copy of an item_data
+
+    deep copy every element of the tuple but item
+    do an elementCopy of item_data.item
+    @param item_data(ItemData): item data to copy
+    @return (ItemData): copied data
+    """
+    return container.ItemData(*[elementCopy(item_data.item)]
+                              + [copy.deepcopy(d) for d in item_data[1:]])
+
+
 class BackendService(service.Service, utility.EventDispatcher):
     """
     Generic publish-subscribe backend service.
@@ -1301,10 +1331,12 @@
         # TODO: a test should check that only the owner get the item configuration back
 
         item, item_config = item_data.item, item_data.config
-        new_item = deepcopy(item)
         if item_config:
+            new_item = elementCopy(item)
             new_item.addChild(item_config.toElement())
-        return new_item
+            return new_item
+        else:
+            return item
 
     @defer.inlineCallbacks
     def _notifyPublish(self, data):
@@ -1423,9 +1455,9 @@
             allowed_items = [] #we keep only item which subscriber can access
 
             if schema is not None:
-                # we have to deepcopy items because different subscribers may receive
+                # we have to copy items_data because different subscribers may receive
                 # different items (e.g. read restriction in schema)
-                items_data = deepcopy(items_data)
+                items_data = itemDataCopy(items_data)
                 self.backend.filterItemsWithSchema(items_data, schema, False)
 
             for item_data in items_data: