Commits

Emmanuel Cazenave committed 1a2bbcd

Add an xpath modify action in the modifiers.

Comments (0)

Files changed (2)

pyf/services/core/modifiers.py

 from pyf.componentized import ET
 
 
+
+class XPATHModifier(object):
+    """Modifies a target node using the deep modifiers syntax which lets you replace, delete, or add childs
+    to an arbirary sub node specified with an xpath expression.
+    """
+    
+    def __init__(self, modifier, target_tree):
+        self.modifier = modifier
+        self.target_tree = target_tree
+
+    def __get_modifer_params(self):
+        operation = self.modifier.find('operation').text
+        pattern = self.modifier.find('pattern').text
+        new_node = self.modifier.find('replace')[0]
+        return operation, pattern, new_node
+
+    def modify(self):
+        operation, pattern, new_node = self.__get_modifer_params()
+        target_node = self.target_tree.xpath(pattern)[0]
+        parent, target_node = get_parent_and_target(self.target_tree, target_node)
+        if operation == 'replace':
+            position = parent.index(target_node)
+            parent[position] = new_node
+    
+
 def get_target(name, target_tree):
     for parent in target_tree.getiterator():
         for child in parent:
             if child.get('name') == name:
                 return child
 
+def iterparent(tree):
+    for parent in tree.getiterator():
+        for child in parent:
+            yield parent, child
+
+def get_parent_and_target(tree, target):
+    for parent, child in iterparent(tree):
+        if child is target:
+            return parent, child
+
+
+
 
 def apply_modifier(modifier, target_tree):
     
         new_child = modifier[0]
         children.insert(index, new_child)
 
+    elif action == 'xpathmodify':
+        if ET.__package__ != 'lxml':
+            raise NotImplementedError, 'deepmodify action is only available if lxml package is installed in your environnement'
+        deep_modifier = XPATHModifier(modifier, target)
+        deep_modifier.modify()
+
     else:
         raise NotImplementedError,\
             'Modifier type "%s" is not implemented (yet ?)' % action

pyf/services/core/tests/test_modifers.py

         assert new_child is not None
         new_child = get_child('dummy_consumer1', children)
         assert new_child is not None
+
+
+class DeepModifyReplace(ModifierTest):
+
+    target_str = """<config>
+  <process name="main">
+    <node from="code" type="producer" name="dummy_extractor">
+      <joiner pluginname="linear"/>
+      <code>
+        <![CDATA[
+def toto():
+    yield "toto"
+        ]]>
+      </code>
+      <children>
+        <link name="dummy_consumer1"/>
+        <link name="dummy_consumer2"/>
+      </children>
+    </node>
+    <node from ="plugin" type="consumer" name="dummy_consumer1" pluginname="dummy_consumer_plugin">
+    </node>
+    <node from="plugin" type="consumer" name="dummy_consumer2" pluginname="dummy_consumer_plugin">
+    </node>
+  </process>
+</config>
+"""
+
+    modifier_str = """
+<modifier target="dummy_extractor" action="xpathmodify">
+   <operation>replace</operation>
+   <pattern>./children/link[@name='dummy_consumer1']</pattern>
+   <replace>
+      <link name="new_consumer"/>
+   </replace>
+</modifier>
+"""
+
+    def test(self):
+        apply_modifier(self.modifier, self.target)
+        parent, target_node = get_target('dummy_extractor', self.target)
+        children = target_node.find('children')
+        assert 2 == len(children)
+        assert 'new_consumer' == children[0].get('name')
+        assert 'dummy_consumer2' == children[1].get('name')