Commit f3188d86 authored by Tatuya Kamada's avatar Tatuya Kamada

- replace 4SuiteXML with lxml

- replace distutill with setuptool 
- append test-cases using docstring
- append buildout setting
- append interface
- eggify
parent 1ba251e9
...@@ -20,19 +20,7 @@ ...@@ -20,19 +20,7 @@
# #
############################################################################## ##############################################################################
try: from lxml import etree
from Ft.Xml import Parse as parse
from Ft.Xml.Domlette import NonvalidatingReader, PrettyPrint
parseString = NonvalidatingReader.parseString
from Ft.Xml.Domlette import implementation
from Ft.Xml import EMPTY_NAMESPACE
def getDOMImplementation():
return implementation
except ImportError:
from xml.dom.minidom import parse, parseString
from xml.dom.minidom import getDOMImplementation
from xml.dom.ext import PrettyPrint
EMPTY_NAMESPACE = None
import sys import sys
import getopt import getopt
...@@ -40,6 +28,9 @@ import os ...@@ -40,6 +28,9 @@ import os
from StringIO import StringIO from StringIO import StringIO
import re import re
import codecs import codecs
from copy import deepcopy
from interfaces.erp5diff import IERP5Diff
import zope.interface
class ERP5Diff: class ERP5Diff:
""" """
...@@ -56,6 +47,10 @@ class ERP5Diff: ...@@ -56,6 +47,10 @@ class ERP5Diff:
5. Ignore some types of nodes, such as EntityReference and Comment, because they are not 5. Ignore some types of nodes, such as EntityReference and Comment, because they are not
used in ERP5 XML documents. used in ERP5 XML documents.
""" """
# Declarative interfaces
zope.interface.implements(IERP5Diff,)
def __init__(self): def __init__(self):
""" """
Initialize itself. Initialize itself.
...@@ -84,9 +79,10 @@ class ERP5Diff: ...@@ -84,9 +79,10 @@ class ERP5Diff:
doc_list = [] doc_list = []
for a in args: for a in args:
if type(a) == type(''): if type(a) == type(''):
doc_list.append(parseString(a)) doc_list.append(etree.XML(a))
else: else:
doc_list.append(parse(a)) element_tree = etree.parse(a)
doc_list.append(element_tree.getroot())
return doc_list return doc_list
def _concatPath(self, p1, p2, separator='/'): def _concatPath(self, p1, p2, separator='/'):
...@@ -102,151 +98,148 @@ class ERP5Diff: ...@@ -102,151 +98,148 @@ class ERP5Diff:
""" """
Return the root element of the result document. Return the root element of the result document.
""" """
return self._result.documentElement return self._result
#return self._result.getroottree()
def _hasChildren(self, element):
"""
Check whether the element has any children
"""
if len(element) == 0:
return False
return True
def _xupdateAppendAttributes(self, dict, path): def _xupdateAppendAttributes(self, dict, path):
""" """
Append attributes to the element at 'path'. Append attrib to the element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS append_element = etree.Element('{%s}append' % self._ns, nsmap=root.nsmap)
createTextNode = self._result.createTextNode append_element.attrib['select'] = path
append_element = createElement(self._ns, 'xupdate:append')
append_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
for name, val in dict.iteritems(): for name, val in dict.iteritems():
attr_element = createElement(self._ns, 'xupdate:attribute') attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', name) attr_element.attrib['name'] = name
text_node = createTextNode(val) attr_element.text = val
attr_element.appendChild(text_node) append_element.append(attr_element)
append_element.appendChild(attr_element) root.append(append_element)
root.appendChild(append_element)
def _xupdateRemoveAttribute(self, name, path): def _xupdateRemoveAttribute(self, name, path):
""" """
Remove an attribute from the element at 'path'. Remove an attribute from the element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS remove_element = etree.Element('{%s}remove' % self._ns, nsmap=root.nsmap)
remove_element = createElement(self._ns, 'xupdate:remove') remove_element.attrib['select'] = self._concatPath(path, 'attribute::' + name)
remove_element.setAttributeNS(EMPTY_NAMESPACE, 'select', self._concatPath(path, 'attribute::' + name)) root.append(remove_element)
root.appendChild(remove_element)
def _xupdateUpdateAttribute(self, name, val, path): def _xupdateUpdateAttribute(self, name, val, path):
""" """
Update the value of an attribute of the element at 'path'. Update the value of an attribute of the element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS update_element = etree.Element('{%s}update' % self._ns, nsmap=root.nsmap)
createTextNode = self._result.createTextNode update_element.attrib['select'] = self._concatPath(path, 'attribute::' + name)
update_element = createElement(self._ns, 'xupdate:update') update_element.text = val
update_element.setAttributeNS(EMPTY_NAMESPACE, 'select', self._concatPath(path, 'attribute::' + name)) root.append(update_element)
text_node = createTextNode(val)
update_element.appendChild(text_node)
root.appendChild(update_element)
def _xupdateRenameElement(self, name, path): def _xupdateRenameElement(self, name, path):
""" """
Rename an existing element at 'path'. Rename an existing element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS rename_element = etree.Element('{%s}rename' % self._ns, nsmap=root.nsmap)
createTextNode = self._result.createTextNode rename_element.attrib['select'] = path
rename_element = createElement(self._ns, 'xupdate:rename') rename_element.text = name
rename_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path) root.append(rename_element)
text_node = createTextNode(name)
rename_element.appendChild(text_node)
root.appendChild(rename_element)
def _xupdateUpdateElement(self, element, path): def _xupdateUpdateElement(self, element, path):
""" """
Update the contents of an element at 'path' to that of 'element'. Update the contents of an element at 'path' to that of 'element'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS update_element = etree.Element('{%s}update' % self._ns, nsmap=root.nsmap)
update_element = createElement(self._ns, 'xupdate:update') update_element.attrib['select'] = path
update_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path) if self._hasChildren(element):
for node in element.childNodes: for child in element:
#self._p("node is %s" % repr(node)) clone_node = deepcopy(child)
clone_node = node.cloneNode(1) update_element.append(clone_node)
update_element.appendChild(clone_node) else:
root.appendChild(update_element) update_element.text = element.text
root.append(update_element)
def _xupdateRemoveElement(self, path): def _xupdateRemoveElement(self, path):
""" """
Remove an element at 'path'. Remove an element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS remove_element = etree.Element('{%s}remove' % self._ns, nsmap=root.nsmap)
remove_element = createElement(self._ns, 'xupdate:remove') remove_element.attrib['select'] = path
remove_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path) root.append(remove_element)
root.appendChild(remove_element)
def _xupdateInsertBefore(self, element_list, path): def _xupdateInsertBefore(self, element_list, path):
""" """
Insert elements before the element at 'path'. Insert elements before the element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS insert_element = etree.Element('{%s}insert-before' % self._ns, nsmap=root.nsmap)
createTextNode = self._result.createTextNode insert_element.attrib['select'] = path
insert_element = createElement(self._ns, 'xupdate:insert-before')
insert_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
for element in element_list: for element in element_list:
child_element = createElement(self._ns, 'xupdate:element') child_element = etree.Element('{%s}element' % self._ns, nsmap=root.nsmap)
child_element.setAttributeNS(EMPTY_NAMESPACE, 'name', element.tagName) child_element.attrib['name'] = element.tag
attr_map = element.attributes attr_map = element.attrib
for attr in attr_map.values(): for name, value in attr_map.items():
attr_element = createElement(self._ns, 'xupdate:attribute') attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', attr.name) attr_element.attrib['name'] = name
text_node = createTextNode(attr.nodeValue) attr_element.text = value
attr_element.appendChild(text_node) child_element.append(attr_element)
child_element.appendChild(attr_element) for child in element:
for child in element.childNodes: clone_node = deepcopy(child)
clone_node = child.cloneNode(1) child_element.append(clone_node)
child_element.appendChild(clone_node) if self._hasChildren(child_element):
insert_element.appendChild(child_element) child_element[-1].tail = element.text
root.appendChild(insert_element) insert_element.append(child_element)
root.append(insert_element)
def _xupdateAppendElements(self, element_list, path): def _xupdateAppendElements(self, element_list, path):
""" """
Append elements to the element at 'path'. Append elements to the element at 'path'.
""" """
root = self._getResultRoot() root = self._getResultRoot()
createElement = self._result.createElementNS append_element = etree.Element('{%s}append' % self._ns, nsmap=root.nsmap)
createTextNode = self._result.createTextNode append_element.attrib['select'] = path
append_element = createElement(self._ns, 'xupdate:append')
append_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
for element in element_list: for element in element_list:
child_element = createElement(self._ns, 'xupdate:element') child_element = etree.Element('{%s}element' % self._ns, nsmap=root.nsmap)
child_element.setAttributeNS(EMPTY_NAMESPACE, 'name', element.tagName) child_element.attrib['name'] = element.tag
attr_map = element.attributes attr_map = element.attrib
for attr in attr_map.values(): for name, value in attr_map.items():
attr_element = createElement(self._ns, 'xupdate:attribute') attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', attr.name) attr_element.attrib['name'] = name
text_node = createTextNode(attr.nodeValue) attr_element.text = value
attr_element.appendChild(text_node) child_element.append(attr_element)
child_element.appendChild(attr_element) for child in element:
for child in element.childNodes: clone_node = deepcopy(child)
clone_node = child.cloneNode(1) child_element.append(clone_node)
child_element.appendChild(clone_node) if self._hasChildren(child_element):
append_element.appendChild(child_element) child_element[-1].tail = element.text
root.appendChild(append_element) append_element.append(child_element)
root.append(append_element)
def _testElements(self, element1, element2): def _testElements(self, element1, element2):
""" """
Test if two given elements are matching. Matching does not mean that they are identical. Test if two given elements are matching. Matching does not mean that they are identical.
""" """
# Make sure that they are elements. # Make sure that they are elements.
if element1.nodeType != element2.nodeType or element1.nodeType != element1.ELEMENT_NODE: if type(element1) != type(element2) or type(element1) != etree._Element:
return 0 return 0
if element1.tagName != element2.tagName: if element1.tag != element2.tag:
return 0 return 0
id_list = [] id_list = []
for attr_map in (element1.attributes, element2.attributes): for attr_map in (element1.attrib, element2.attrib):
for attr in attr_map.values(): for name, value in attr_map.items():
if attr.name == 'id': if name == 'id':
id_list.append(attr.nodeValue) id_list.append(value)
break break
if len(id_list) == 0: if len(id_list) == 0:
...@@ -257,18 +250,18 @@ class ERP5Diff: ...@@ -257,18 +250,18 @@ class ERP5Diff:
def _testAttributes(self, element1, element2, path): def _testAttributes(self, element1, element2, path):
""" """
Test attributes of two given elements. Add differences, if any. Test attrib of two given elements. Add differences, if any.
""" """
# Make a list of dictionaries of the attributes. # Make a list of dictionaries of the attributes.
dict_list = [] dict_list = []
for attr_map in (element1.attributes, element2.attributes): for attr_map in (element1.attrib, element2.attrib):
dict = {} d = {}
for attr in attr_map.values(): for name, value in attr_map.items():
dict[attr.name] = attr.nodeValue d[name] = value
dict_list.append(dict) dict_list.append(d)
dict1, dict2 = dict_list dict1, dict2 = dict_list
# Find all added or removed or changed attributes. # Find all added or removed or changed attrib.
for name1, val1 in dict1.iteritems(): for name1, val1 in dict1.iteritems():
if name1 in dict2: if name1 in dict2:
if val1 != dict2[name1]: if val1 != dict2[name1]:
...@@ -279,20 +272,22 @@ class ERP5Diff: ...@@ -279,20 +272,22 @@ class ERP5Diff:
else: else:
# This attribute is removed. # This attribute is removed.
self._xupdateRemoveAttribute(name1, path) self._xupdateRemoveAttribute(name1, path)
dict = {} d = {}
for name2, val2 in dict2.iteritems(): for name2, val2 in dict2.iteritems():
if val2 is not None: if val2 is not None:
# This attribute is added. # This attribute is added.
dict[name2] = val2 d[name2] = val2
if dict != {}: if d != {}:
self._xupdateAppendAttributes(dict, path) self._xupdateAppendAttributes(d, path)
def _checkEmptiness(self, element): def _checkEmptiness(self, element):
""" """
Check if an element has child values. Check if an element has child values.
""" """
for child in element.childNodes: for child in element:
if child.nodeType == child.ELEMENT_NODE or child.nodeType == child.TEXT_NODE: if type(child) == etree._Element:
return 0
if element.text is not None:
return 0 return 0
return 1 return 1
...@@ -301,8 +296,8 @@ class ERP5Diff: ...@@ -301,8 +296,8 @@ class ERP5Diff:
Determine if text should be ignored by heuristics, Determine if text should be ignored by heuristics,
because ERP5 does not define any schema at the moment. because ERP5 does not define any schema at the moment.
""" """
for child in element.childNodes: for child in element:
if child.nodeType == child.ELEMENT_NODE: if type(child) == etree._Element:
return 1 return 1
return 0 return 0
...@@ -313,33 +308,33 @@ class ERP5Diff: ...@@ -313,33 +308,33 @@ class ERP5Diff:
num_map = {} num_map = {}
count_map = {} count_map = {}
for element in element_list: for element in element_list:
if element.tagName in num_map: if element.tag in num_map:
num_map[element.tagName] += 1 num_map[element.tag] += 1
else: else:
num_map[element.tagName] = 1 num_map[element.tag] = 1
count_map[element.tagName] = 0 count_map[element.tag] = 0
path_list = [] path_list = []
for element in element_list: for element in element_list:
# Check if this element has an attribute 'id'. # Check if this element has an attribute 'id'.s
id_val = None id_val = None
attr_map = element.attributes attr_map = element.attrib
for attr in attr_map.values(): for name, value in attr_map.items():
if attr.name == 'id': if name == 'id':
id_val = attr.nodeValue id_val = value
break break
if id_val is not None: if id_val is not None:
# If an attribute 'id' is present, uses the attribute for convenience. # If an attribute 'id' is present, uses the attribute for convenience.
path_list.append("%s[@id='%s']" % (element.tagName, id_val)) path_list.append("%s[@id='%s']" % (element.tag, id_val))
# Increase the count, for a case where other elements with the same tag name do not have # Increase the count, for a case where other elements with the same tag name do not have
# 'id' attributes. # 'id' attrib.
count_map[element.tagName] += 1 count_map[element.tag] += 1
elif num_map[element.tagName] > 1: elif num_map[element.tag] > 1:
path_list.append('%s[%d]' % (element.tagName, count_map[element.tagName])) path_list.append('%s[%d]' % (element.tag, count_map[element.tag]))
count_map[element.tagName] += 1 count_map[element.tag] += 1
else: else:
path_list.append(element.tagName) path_list.append(element.tag)
return path_list return path_list
...@@ -348,8 +343,8 @@ class ERP5Diff: ...@@ -348,8 +343,8 @@ class ERP5Diff:
Aggregate child elements of an element into a list. Aggregate child elements of an element into a list.
""" """
element_list = [] element_list = []
for child in element.childNodes: for child in element:
if child.nodeType == child.ELEMENT_NODE: if type(child) == etree._Element:
element_list.append(child) element_list.append(child)
return element_list return element_list
...@@ -358,9 +353,11 @@ class ERP5Diff: ...@@ -358,9 +353,11 @@ class ERP5Diff:
Aggregate child text nodes of an element into a single string. Aggregate child text nodes of an element into a single string.
""" """
text = '' text = ''
for child in element.childNodes: if not self._hasChildren(element):
if child.nodeType == child.TEXT_NODE: return element.text
text += child.nodeValue for child in element:
if type(child) == etree._Element:
text += child.text
return text return text
def _compareChildNodes(self, old_element, new_element, path): def _compareChildNodes(self, old_element, new_element, path):
...@@ -433,40 +430,36 @@ class ERP5Diff: ...@@ -433,40 +430,36 @@ class ERP5Diff:
Otherwise, it is assumed to be a file object which contains a XML document. Otherwise, it is assumed to be a file object which contains a XML document.
""" """
old_doc, new_doc = self._makeDocList(old_xml, new_xml) old_doc, new_doc = self._makeDocList(old_xml, new_xml)
old_root_element = old_doc.documentElement old_root_element = old_doc #.getroottree() #old_doc.documentElement
new_root_element = new_doc.documentElement new_root_element = new_doc #.getroottree() #new_doc.documentElement
try: try:
impl = getDOMImplementation()
# XXX this namespace argument won't be handled correctly in minidom.
# XXX So work around that problem when outputting the result.
if self._result is not None: if self._result is not None:
self._result.close() self._result = None
self._result = impl.createDocument(self._ns, 'xupdate:modifications', None) self._result = etree.Element('{%s}modifications' % self._ns, nsmap={'xupdate':self._ns})
attr_version = self._result.createAttributeNS(EMPTY_NAMESPACE, 'version') self._result.set('version', '1.0')
attr_version.value = '1.0'
self._result.documentElement.setAttributeNodeNS(attr_version)
if self._testElements(old_root_element, new_root_element): if self._testElements(old_root_element, new_root_element):
self._testAttributes(old_root_element, new_root_element, '/') self._testAttributes(old_root_element, new_root_element, '/')
self._compareChildNodes(old_root_element, new_root_element, '/') self._compareChildNodes(old_root_element, new_root_element, '/')
else: else:
# These XML documents seem to be completely different... # These XML documents seem to be completely different...
if old_root_element.tagName != new_root_element.tagName: if old_root_element.tag != new_root_element.tag:
self._xupdateRenameElement(new_root_element.tagName, '/') self._xupdateRenameElement(new_root_element.tag, '/')
self._testAttributes(old_root_element, new_root_element, '/') self._testAttributes(old_root_element, new_root_element, '/')
self._xupdateUpdateElement(new_root_element, '/') self._xupdateUpdateElement(new_root_element, '/')
finally: finally:
del old_doc del old_doc
del new_doc del new_doc
def output(self, file=None): def output(self, output_file=None):
""" """
Output the result of parsing XML documents to 'file'. Output the result of parsing XML documents to 'output_file'.
If it is not specified, stdout is assumed. If it is not specified, stdout is assumed.
""" """
if file is None: if output_file is None:
file = sys.stdout output_file = sys.stdout
# stream
PrettyPrint(self._result.documentElement, stream=file, encoding='UTF-8') xml = etree.tostring(self._result, encoding='utf-8', pretty_print=True)
output_file.write(xml)
def outputString(self): def outputString(self):
""" """
......
This is a XUpdate Generator for ERP5. This is a XUpdate Generator for ERP5.
See <http://www.xmldb.org/xupdate/index.html> for information on See <http://xmldb-org.sourceforge.net/xupdate/> for information on
XUpdate. XUpdate.
See <http://erp5.org/> for information on ERP5. See <http://erp5.org/> for information on ERP5.
...@@ -14,4 +14,673 @@ See the manpage erp5diff(1) or "erp5diff --help" for more information. ...@@ -14,4 +14,673 @@ See the manpage erp5diff(1) or "erp5diff --help" for more information.
Also, you can use the module ERP5Diff from your Python script. Also, you can use the module ERP5Diff from your Python script.
Do "pydoc ERP5Diff" for more information. Do "pydoc ERP5Diff" for more information.
ERP5Diff Usage and its output example
=====================================
1. update the texts of the three elements
>>> from ERP5Diff import ERP5Diff
>>> erp5diff = ERP5Diff()
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description1 --- $sdfr&#231;_sdfs&#231;df_oisfsopf</description>
... <first_name type="string">Kamada</first_name>
... <last_name type="string">Kamada</last_name>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:24.700 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description3 &#231;sdf__sdf&#231;&#231;&#231;_df___&amp;amp;&amp;amp;&#233;]]]&#176;&#176;&#176;&#176;&#176;&#176;</description>
... <first_name type="string">Tatuya</first_name>
... <last_name type="string">Kamada</last_name>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:24.703 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/description">description3 çsdf__sdfççç_df___&amp;amp;&amp;amp;é]]]°°°°°°</xupdate:update>
<xupdate:update select="/object[@id='313730']/first_name">Tatuya</xupdate:update>
<xupdate:update select="/object[@id='313730']/workflow_action[@id='edit_workflow']/time">2009/08/28 19:12:24.703 GMT+9</xupdate:update>
</xupdate:modifications>
2. update one element
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description2&#233;&#224;@ $*&amp;lt; &amp;lt; -----</description>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description3&#233;&#224;@ $*&amp;lt; &amp;lt; -----</description>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/description">description3éà@ $*&amp;lt; &amp;lt; -----</xupdate:update>
</xupdate:modifications>
3. same
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <title type="string">Tatuya Kamada</title>
... <subject_list type="lines">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;list id="i2"&gt;&lt;/list&gt;&lt;/marshal&gt;</subject_list>
... <first_name type="string">Kamada</first_name>
... <last_name type="string">Tatuya</last_name>
... <workflow_action id="edit_workflow">
... <actor type="string">tatuya</actor>
... <time type="date">2009/08/28 19:12:26.631 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <title type="string">Tatuya Kamada</title>
... <subject_list type="lines">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;list id="i2"&gt;&lt;/list&gt;&lt;/marshal&gt;</subject_list>
... <first_name type="string">Kamada</first_name>
... <last_name type="string">Tatuya</last_name>
... <workflow_action id="edit_workflow">
... <actor type="string">tatuya</actor>
... <time type="date">2009/08/28 19:12:26.631 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0"/>
4. update the texts of the elements and remove an element
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description2&#233;&#224;@ $*&amp;lt; &amp;lt;&amp;lt;&amp;lt; -----</description>
... <language type="string">en</language>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description1 --- $sdfr&#231;_sdfs&#231;df_oisfsopf</description>
... <language type="None"/>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/description">description1 --- $sdfrç_sdfsçdf_oisfsopf</xupdate:update>
<xupdate:update select="/object[@id='313730']/language/attribute::type">None</xupdate:update>
<xupdate:update select="/object[@id='313730']/language"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[@id='edit_workflow']"/>
</xupdate:modifications>
5. update two elements includes some symbols
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description2&#233;&#224;@ $*&amp;lt;&amp;lt;-----&amp;gt;&amp;gt;</description>
... <language type="string">jp</language>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <description type="text">description4 sdflkmooo^^^^]]]]]{{{{{{{</description>
... <language type="string">ca</language>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/description">description4 sdflkmooo^^^^]]]]]{{{{{{{</xupdate:update>
<xupdate:update select="/object[@id='313730']/language">ca</xupdate:update>
</xupdate:modifications>
6. update two date element which have same id
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.550 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.903 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.907 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.550 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.905 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:40.910 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/workflow_action[@id='edit_workflow']/time">2009/08/28 19:12:40.905 GMT+9</xupdate:update>
<xupdate:update select="/object[@id='313730']/workflow_action[@id='edit_workflow']/time">2009/08/28 19:12:40.910 GMT+9</xupdate:update>
</xupdate:modifications>
7. insert and remove elements
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313731">
... <local_role type="tokens" id="tk">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... <local_permission type="tokens" id="Access contents information">&lt;?xml version="1.0"?&gt;</local_permission>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313731">
... <local_role type="tokens" id="tatuya">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... <local_permission type="tokens" id="Access contents information">&lt;?xml version="1.0"?&gt;</local_permission>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313731']/local_role[@id='tk']"/>
<xupdate:insert-before select="/object[@id='313731']/local_permission[@id='Access contents information']">
<xupdate:element name="local_role"><xupdate:attribute name="type">tokens</xupdate:attribute><xupdate:attribute name="id">tatuya</xupdate:attribute>&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</xupdate:element>
</xupdate:insert-before>
</xupdate:modifications>
8. update xml in xml
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313731">
... <local_permission type="tokens" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313731">
... <local_permission type="tokens" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313731']/local_permission[@id='View']">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</xupdate:update>
</xupdate:modifications>
9. rename element
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name type="string">Tatuya</first_name>
... <last_name type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <given_name type="string">Tatuya</given_name>
... <family_name type="string">Kamada</family_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/first_name"/>
<xupdate:remove select="/object[@id='313730']/last_name"/>
<xupdate:append select="/object[@id='313730']">
<xupdate:element name="given_name"><xupdate:attribute name="type">string</xupdate:attribute>Tatuya</xupdate:element>
<xupdate:element name="family_name"><xupdate:attribute name="type">string</xupdate:attribute>Kamada</xupdate:element>
</xupdate:append>
</xupdate:modifications>
10. rename root element
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <id type="string">313730</id>
... <title type="string">Tatuya Kamada</title>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp6>
... <object portal_type="Person" id="313730">
... <id type="string">313730</id>
... <title type="string">Tatuya Kamada</title>
... </object>
... </erp6>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:rename select="/">erp6</xupdate:rename>
<xupdate:update select="/"><object portal_type="Person" id="313730">
<id type="string">313730</id>
<title type="string">Tatuya Kamada</title>
</object>
</xupdate:update>
</xupdate:modifications>
11. Update one attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <local_role type="tokens" id="fab">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <local_role type="ccc" id="fab">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/local_role[@id='fab']/attribute::type">ccc</xupdate:update>
</xupdate:modifications>
12. Update two attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <local_permission attr_a='aaa' type="tokens" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <local_permission attr_a='ccc' type="ccc" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/local_permission[@id='View']/attribute::attr_a">ccc</xupdate:update>
<xupdate:update select="/object[@id='313730']/local_permission[@id='View']/attribute::type">ccc</xupdate:update>
</xupdate:modifications>
13. Update three attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <title attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya Kamada</title>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <title attribute_a="nnn" attribute_b="nnn" attribute_c="nnn" type="string">Tatuya Kamada</title>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/title/attribute::attribute_b">nnn</xupdate:update>
<xupdate:update select="/object[@id='313730']/title/attribute::attribute_c">nnn</xupdate:update>
<xupdate:update select="/object[@id='313730']/title/attribute::attribute_a">nnn</xupdate:update>
</xupdate:modifications>
14. Remove one attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name attribute_a="aaa" attribute_b="bbb" type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_c"/>
</xupdate:modifications>
15. Remove two attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name attribute_a="aaa" type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_b"/>
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_c"/>
</xupdate:modifications>
16. Remove three attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <first_name type="string">Tatuya</first_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_b"/>
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_c"/>
<xupdate:remove select="/object[@id='313730']/first_name/attribute::attribute_a"/>
</xupdate:modifications>
17. Append one attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name attribute_a="aaa" type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:append select="/object[@id='313730']/last_name">
<xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
</xupdate:append>
</xupdate:modifications>
18. Append two attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name attribute_a="aaa" attribute_b="bbb" type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:append select="/object[@id='313730']/last_name">
<xupdate:attribute name="attribute_b">bbb</xupdate:attribute>
<xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
</xupdate:append>
</xupdate:modifications>
19. Append three attribute
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <last_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Kamada</last_name>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:append select="/object[@id='313730']/last_name">
<xupdate:attribute name="attribute_b">bbb</xupdate:attribute>
<xupdate:attribute name="attribute_c">ccc</xupdate:attribute>
<xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
</xupdate:append>
</xupdate:modifications>
20. Remove some elements that have same id
This is an unexpected case for current ERP5Diff alogrithm. So current ERP5Diff
does not work as bellow example. This is a known bug.
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.434 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.430 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.428 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.426 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.430 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.428 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.426 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/workflow_action[2]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[3]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[4]"/>
</xupdate:modifications>
21. Modify two elements that have same id
As well as No.20. This a known bug, too.
>>> old_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.434 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.436 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Person" id="313730">
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/29 19:12:34.432 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/30 19:12:34.434 GMT+9</time>
... </workflow_action>
... <workflow_action id="edit_workflow">
... <time type="date">2009/08/31 19:12:34.436 GMT+9</time>
... </workflow_action>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[@id='313730']/workflow_action[2]/time">2009/08/29 19:12:34.432 GMT+9</xupdate:update>
<xupdate:update select="/object[@id='313730']/workflow_action[3]/time">2009/08/30 19:12:34.434 GMT+9</xupdate:update>
<xupdate:update select="/object[@id='313730']/workflow_action[4]/time">2009/08/31 19:12:34.436 GMT+9</xupdate:update>
</xupdate:modifications>
22. Modify attributes of sequencial objects
ERP5Diff creates target index from 0 as a XPath string, but according to the
definition of the XPath specification <http://www.w3.org/TR/xpath>, it is wrong.
It should be start from 1. This is a known problem.
>>> old_xml = """
... <erp5>
... <object portal_type="Test">
... <title>A</title>
... </object>
... <object portal_type="Test">
... <title>A</title>
... </object>
... <object portal_type="Test">
... <title>A</title>
... </object>
... </erp5>
... """
>>> new_xml = """
... <erp5>
... <object portal_type="Test">
... <title>A</title>
... </object>
... <object portal_type="Test">
... <title>B</title>
... </object>
... <object portal_type="Test">
... <title>C</title>
... </object>
... </erp5>
... """
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:update select="/object[2]/title">B</xupdate:update>
<xupdate:update select="/object[3]/title">C</xupdate:update>
</xupdate:modifications>
- 2003-12-04, Yoshinori OKUJI <yo@nexedi.com> - 2003-12-04, Yoshinori OKUJI <yo@nexedi.com>
- 2009-09-15, Tatuya Kamada <tatuya@nexedi.com>
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
$Id: bootstrap.py 77225 2007-06-29 09:20:13Z dobe $
"""
import os, shutil, sys, tempfile, urllib2
tmpeggs = tempfile.mkdtemp()
try:
import pkg_resources
except ImportError:
ez = {}
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
).read() in ez
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
import pkg_resources
cmd = 'from setuptools.command.easy_install import main; main()'
if sys.platform == 'win32':
cmd = '"%s"' % cmd # work around spawn lamosity on windows
ws = pkg_resources.working_set
assert os.spawnle(
os.P_WAIT, sys.executable, sys.executable,
'-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
dict(os.environ,
PYTHONPATH=
ws.find(pkg_resources.Requirement.parse('setuptools')).location
),
) == 0
ws.add_entry(tmpeggs)
ws.require('zc.buildout')
import zc.buildout.buildout
zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
shutil.rmtree(tmpeggs)
[buildout]
develop = .
parts = test
[test]
recipe = zc.recipe.testrunner
eggs = erp5diff
from zope.interface import Interface
class IERP5Diff(Interface):
"""
Make a difference between two XML documents using XUpdate.
Use some assumptions in ERP5's data representation.
The strategy is:
1. Find a matching element among elements of the other XML document at the same depth.
2. Use the first matching element, even if there can be other better elements.
3. Assume that two elements are matching, if the tag names are identical. If either of
them has an attribute 'id', the values of the attrib 'id' also must be identical.
4. Don't use xupdate:rename for elements. It should be quite rare to rename tag names
in ERP5, and it is too complicated to support this renaming.
5. Ignore some types of nodes, such as EntityReference and Comment, because they are not
used in ERP5 XML documents.
"""
def compare(self, old_xml, new_xml):
"""
Compare two given XML documents.
If an argument is a string, it is assumed to be a XML document itself.
Otherwise, it is assumed to be a file object which contains a XML document.
"""
def output(self, output_file=None):
"""
Output the result of parsing XML documents to 'output_file'.
If it is not specified, stdout is assumed.
"""
def outputString(self):
"""
Return the result as a string object.
"""
def main():
"""
The main routine of ERP5Diff.
"""
\ No newline at end of file
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
#! /usr/bin/env python #! /usr/bin/env python
from distutils.core import setup from setuptools import setup, find_packages
setup(name="erp5diff", setup(name="erp5diff",
version="0.1", version="0.1",
...@@ -9,7 +9,11 @@ setup(name="erp5diff", ...@@ -9,7 +9,11 @@ setup(name="erp5diff",
author_email="yo@nexedi.com", author_email="yo@nexedi.com",
url="http://nexedi.com", url="http://nexedi.com",
license="GPL", license="GPL",
packages=find_packages(),
py_modules=["ERP5Diff"], py_modules=["ERP5Diff"],
scripts=["erp5diff"], scripts=["erp5diff"],
data_files=[('share/man/man1', ['erp5diff.1'])] data_files=[('share/man/man1', ['erp5diff.1'])],
install_requires=[ 'zope.interface', 'lxml'],
include_package_data=True,
zip_safe=False,
) )
from zope import interface
import zope.testing
import unittest
OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
zope.testing.doctest.NORMALIZE_WHITESPACE)
def test_suite():
doctests = ('README',)
globs = dict(interface=interface)
return unittest.TestSuite((
zope.testing.doctest.DocFileSuite(doctest,
optionflags=OPTIONFLAGS,
globs=globs,
) for doctest in doctests
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment