From a7d56f2d31ecfc809a25bc772ea9e9d30c0478e8 Mon Sep 17 00:00:00 2001
From: Tony Mack <tmack@cs.princeton.edu>
Date: Tue, 16 Feb 2010 17:38:40 +0000
Subject: [PATCH] adding xmlbuilder

---
 xmlbuilder-0.9/LICENSE                        |  21 +++
 xmlbuilder-0.9/MANIFEST.in                    |   2 +
 xmlbuilder-0.9/PKG-INFO                       |  80 +++++++++
 xmlbuilder-0.9/README.txt                     |   1 +
 xmlbuilder-0.9/setup.cfg                      |   5 +
 xmlbuilder-0.9/setup.py                       |  43 +++++
 xmlbuilder-0.9/xmlbuilder.egg-info/PKG-INFO   |  80 +++++++++
 .../xmlbuilder.egg-info/SOURCES.txt           |  11 ++
 .../xmlbuilder.egg-info/dependency_links.txt  |   1 +
 .../xmlbuilder.egg-info/top_level.txt         |   1 +
 xmlbuilder-0.9/xmlbuilder/__init__.py         | 153 ++++++++++++++++++
 xmlbuilder-0.9/xmlbuilder/docs/long_descr.rst |  68 ++++++++
 xmlbuilder-0.9/xmlbuilder/tests/__init__.py   |  99 ++++++++++++
 13 files changed, 565 insertions(+)
 create mode 100644 xmlbuilder-0.9/LICENSE
 create mode 100644 xmlbuilder-0.9/MANIFEST.in
 create mode 100644 xmlbuilder-0.9/PKG-INFO
 create mode 100644 xmlbuilder-0.9/README.txt
 create mode 100644 xmlbuilder-0.9/setup.cfg
 create mode 100644 xmlbuilder-0.9/setup.py
 create mode 100644 xmlbuilder-0.9/xmlbuilder.egg-info/PKG-INFO
 create mode 100644 xmlbuilder-0.9/xmlbuilder.egg-info/SOURCES.txt
 create mode 100644 xmlbuilder-0.9/xmlbuilder.egg-info/dependency_links.txt
 create mode 100644 xmlbuilder-0.9/xmlbuilder.egg-info/top_level.txt
 create mode 100644 xmlbuilder-0.9/xmlbuilder/__init__.py
 create mode 100644 xmlbuilder-0.9/xmlbuilder/docs/long_descr.rst
 create mode 100644 xmlbuilder-0.9/xmlbuilder/tests/__init__.py

diff --git a/xmlbuilder-0.9/LICENSE b/xmlbuilder-0.9/LICENSE
new file mode 100644
index 00000000..0d0f57c4
--- /dev/null
+++ b/xmlbuilder-0.9/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2008 Konstantin Danilov aka koder
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/xmlbuilder-0.9/MANIFEST.in b/xmlbuilder-0.9/MANIFEST.in
new file mode 100644
index 00000000..207725e9
--- /dev/null
+++ b/xmlbuilder-0.9/MANIFEST.in
@@ -0,0 +1,2 @@
+include xmlbuild/docs *.rst
+include . LICENSE
\ No newline at end of file
diff --git a/xmlbuilder-0.9/PKG-INFO b/xmlbuilder-0.9/PKG-INFO
new file mode 100644
index 00000000..d931c15a
--- /dev/null
+++ b/xmlbuilder-0.9/PKG-INFO
@@ -0,0 +1,80 @@
+Metadata-Version: 1.0
+Name: xmlbuilder
+Version: 0.9
+Summary: Pythonic way to create xml files
+Home-page: http://pypi.python.org/pypi/xmlbuilder
+Author: koder
+Author-email: koder_dot_mail@gmail_dot_com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/xmlbuilder
+Description: Example of usage:
+        -----------------
+        
+        
+        from __future__ import with_statement
+        from xmlbuilder import XMLBuilder
+        x = XMLBuilder(format=True)
+        with x.root(a = 1):
+        with x.data:
+        [x &lt;&lt; ('node',{'val':i}) for i in range(10)]
+        
+        print str(x)
+        
+        will print
+        
+        &lt;root a="1"&gt;
+        &lt;data&gt;
+        &lt;node val="0" /&gt;
+        &lt;node val="1" /&gt;
+        &lt;node val="2" /&gt;
+        &lt;node val="3" /&gt;
+        &lt;node val="4" /&gt;
+        &lt;node val="5" /&gt;
+        &lt;node val="6" /&gt;
+        &lt;node val="7" /&gt;
+        &lt;node val="8" /&gt;
+        &lt;node val="9" /&gt;
+        &lt;/data&gt;
+        &lt;/root&gt;
+        
+        Mercurial repo:http://hg.assembla.com/MyPackages/
+        
+        Documentations
+        --------------
+        `XMLBuilder` is simple library build on top of `ElementTree.TreeBuilder` to
+        simplify xml files creation as much as possible. Althow it can produce
+        structured result with identated child tags. `XMLBuilder` use python `with`
+        statement to define xml tag levels and `&lt;&lt;` operator for simple cases -
+        text and tag without childs.
+        
+        First we need to create xmlbuilder
+        
+        from xmlbuilder import XMLBuilder
+        # params - encoding = 'utf8',
+        # builder = None, - ElementTree.TreeBuilder
+        # tab_level = None, - current tab l;evel - for formatted output only
+        # format = False, - create formatted output
+        # tab_step = " " * 4 - indentation step
+        xml = XMLBuilder()
+        
+        
+        Use `with` statement to make document structure
+        #create and open tag 'root_tag' with text 'text' and attributes
+        with xml.root_tag(text,attr1=val1,attr2=val2):
+        #create and open tag 'sub_tag'
+        with xml.sub_tag(text,attr3=val3):
+        #create tag which are not valid python identificator
+        with xml('one-more-sub-tag',attr7=val37):
+        xml &lt;&lt; "Some textual data"
+        #here tag 'one-more-sub-tag' are closed
+        #Tags without children can be created using `&lt;&lt;` operator
+        for val in range(15):
+        xml &lt;&lt; ('message',"python rocks!"[:i])
+        #create 15 child tag like &lt;message&gt; python r&lt;/message&gt;
+        #all tags closed
+        node = ~x # get etree.ElementTree object
+        xml_data = str(x)
+        unicode_xml_data = unicode(x)
+        
+Keywords: xml
+Platform: UNKNOWN
diff --git a/xmlbuilder-0.9/README.txt b/xmlbuilder-0.9/README.txt
new file mode 100644
index 00000000..7a7131fc
--- /dev/null
+++ b/xmlbuilder-0.9/README.txt
@@ -0,0 +1 @@
+Pythonic way to build xml files
\ No newline at end of file
diff --git a/xmlbuilder-0.9/setup.cfg b/xmlbuilder-0.9/setup.cfg
new file mode 100644
index 00000000..b14b0bc3
--- /dev/null
+++ b/xmlbuilder-0.9/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/xmlbuilder-0.9/setup.py b/xmlbuilder-0.9/setup.py
new file mode 100644
index 00000000..931cb315
--- /dev/null
+++ b/xmlbuilder-0.9/setup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#-------------------------------------------------------------------------------
+import os
+import sys
+import glob
+import os.path
+from setuptools import setup
+#from distutils.core import setup
+#-------------------------------------------------------------------------------
+if 'upload' in sys.argv:
+    # for .pypirc file
+    try:
+        os.environ['HOME']
+    except KeyError:
+        os.environ['HOME'] = '..\\'
+#-------------------------------------------------------------------------------
+fpath = lambda x : os.path.join(*x.split('/'))
+#-------------------------------------------------------------------------------
+PYPI_URL = 'http://pypi.python.org/pypi/xmlbuilder'
+ld = open(fpath('xmlbuilder/docs/long_descr.rst')).read()
+ld = ld.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
+setup(
+    name = "xmlbuilder",
+    fullname = "xmlbuilder",
+    version = "0.9",
+    packages = ["xmlbuilder"],
+    package_dir = {'xmlbuilder':'xmlbuilder'},
+    author = "koder",
+    author_email = "koder_dot_mail@gmail_dot_com",
+    maintainer = 'koder',
+    maintainer_email = "koder_dot_mail@gmail_dot_com",
+    description = "Pythonic way to create xml files",
+    license = "MIT",
+    keywords = "xml",
+    test_suite = "xml_buider.tests",
+    url = PYPI_URL,
+    download_url = PYPI_URL,
+    long_description = ld,
+    #include_package_data = True,
+    #package_data = {'xmlbuilder':["docs/*.rst"]},
+    #data_files = [('', ['xmlbuilder/docs/long_descr.rst'])]
+)
+#-------------------------------------------------------------------------------
diff --git a/xmlbuilder-0.9/xmlbuilder.egg-info/PKG-INFO b/xmlbuilder-0.9/xmlbuilder.egg-info/PKG-INFO
new file mode 100644
index 00000000..d931c15a
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder.egg-info/PKG-INFO
@@ -0,0 +1,80 @@
+Metadata-Version: 1.0
+Name: xmlbuilder
+Version: 0.9
+Summary: Pythonic way to create xml files
+Home-page: http://pypi.python.org/pypi/xmlbuilder
+Author: koder
+Author-email: koder_dot_mail@gmail_dot_com
+License: MIT
+Download-URL: http://pypi.python.org/pypi/xmlbuilder
+Description: Example of usage:
+        -----------------
+        
+        
+        from __future__ import with_statement
+        from xmlbuilder import XMLBuilder
+        x = XMLBuilder(format=True)
+        with x.root(a = 1):
+        with x.data:
+        [x &lt;&lt; ('node',{'val':i}) for i in range(10)]
+        
+        print str(x)
+        
+        will print
+        
+        &lt;root a="1"&gt;
+        &lt;data&gt;
+        &lt;node val="0" /&gt;
+        &lt;node val="1" /&gt;
+        &lt;node val="2" /&gt;
+        &lt;node val="3" /&gt;
+        &lt;node val="4" /&gt;
+        &lt;node val="5" /&gt;
+        &lt;node val="6" /&gt;
+        &lt;node val="7" /&gt;
+        &lt;node val="8" /&gt;
+        &lt;node val="9" /&gt;
+        &lt;/data&gt;
+        &lt;/root&gt;
+        
+        Mercurial repo:http://hg.assembla.com/MyPackages/
+        
+        Documentations
+        --------------
+        `XMLBuilder` is simple library build on top of `ElementTree.TreeBuilder` to
+        simplify xml files creation as much as possible. Althow it can produce
+        structured result with identated child tags. `XMLBuilder` use python `with`
+        statement to define xml tag levels and `&lt;&lt;` operator for simple cases -
+        text and tag without childs.
+        
+        First we need to create xmlbuilder
+        
+        from xmlbuilder import XMLBuilder
+        # params - encoding = 'utf8',
+        # builder = None, - ElementTree.TreeBuilder
+        # tab_level = None, - current tab l;evel - for formatted output only
+        # format = False, - create formatted output
+        # tab_step = " " * 4 - indentation step
+        xml = XMLBuilder()
+        
+        
+        Use `with` statement to make document structure
+        #create and open tag 'root_tag' with text 'text' and attributes
+        with xml.root_tag(text,attr1=val1,attr2=val2):
+        #create and open tag 'sub_tag'
+        with xml.sub_tag(text,attr3=val3):
+        #create tag which are not valid python identificator
+        with xml('one-more-sub-tag',attr7=val37):
+        xml &lt;&lt; "Some textual data"
+        #here tag 'one-more-sub-tag' are closed
+        #Tags without children can be created using `&lt;&lt;` operator
+        for val in range(15):
+        xml &lt;&lt; ('message',"python rocks!"[:i])
+        #create 15 child tag like &lt;message&gt; python r&lt;/message&gt;
+        #all tags closed
+        node = ~x # get etree.ElementTree object
+        xml_data = str(x)
+        unicode_xml_data = unicode(x)
+        
+Keywords: xml
+Platform: UNKNOWN
diff --git a/xmlbuilder-0.9/xmlbuilder.egg-info/SOURCES.txt b/xmlbuilder-0.9/xmlbuilder.egg-info/SOURCES.txt
new file mode 100644
index 00000000..57272a81
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder.egg-info/SOURCES.txt
@@ -0,0 +1,11 @@
+LICENSE
+MANIFEST.in
+README.txt
+setup.py
+xmlbuilder/__init__.py
+xmlbuilder.egg-info/PKG-INFO
+xmlbuilder.egg-info/SOURCES.txt
+xmlbuilder.egg-info/dependency_links.txt
+xmlbuilder.egg-info/top_level.txt
+xmlbuilder/docs/long_descr.rst
+xmlbuilder/tests/__init__.py
\ No newline at end of file
diff --git a/xmlbuilder-0.9/xmlbuilder.egg-info/dependency_links.txt b/xmlbuilder-0.9/xmlbuilder.egg-info/dependency_links.txt
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/xmlbuilder-0.9/xmlbuilder.egg-info/top_level.txt b/xmlbuilder-0.9/xmlbuilder.egg-info/top_level.txt
new file mode 100644
index 00000000..9f1d4861
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder.egg-info/top_level.txt
@@ -0,0 +1 @@
+xmlbuilder
diff --git a/xmlbuilder-0.9/xmlbuilder/__init__.py b/xmlbuilder-0.9/xmlbuilder/__init__.py
new file mode 100644
index 00000000..1be17b0f
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder/__init__.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+#-------------------------------------------------------------------------------
+from __future__ import with_statement
+#-------------------------------------------------------------------------------
+from xml.etree.ElementTree import TreeBuilder,tostring
+#-------------------------------------------------------------------------------
+__all__ = ["XMLBuilder"]
+__doc__ = """
+XMLBuilder is simple library build on top of ElementTree.TreeBuilder to
+simplify xml files creation as much as possible. Althow it can produce
+structured result with identated child tags. `XMLBuilder` use python `with`
+statement to define xml tag levels and `<<` operator for simple cases -
+text and tag without childs.
+
+from __future__ import with_statement
+from xmlbuilder import XMLBuilder
+x = XMLBuilder(format=True)
+with x.root(a = 1):
+    with x.data:
+        [x << ('node',{'val':i}) for i in range(10)]
+
+etree_node = ~x
+print str(x)
+"""
+#-------------------------------------------------------------------------------
+class _XMLNode(object):
+    """Class for internal usage"""
+    def __init__(self,parent,name,builder):
+        self.builder = builder
+        self.name = name
+        self.text = []
+        self.attrs = {}
+        self.entered = False
+        self.parent = parent
+    def __call__(self,*dt,**mp):
+        text = "".join(dt)
+        if self.entered:
+            self.builder.data(text)
+        else:
+            self.text.append(text)
+        if self.entered:
+            raise ValueError("Can't add attributes to already opened element")
+        smp = dict((k,str(v)) for k,v in mp.items())
+        self.attrs.update(smp)
+        return self
+    def __enter__(self):
+        self.parent += 1
+        self.builder.start(self.name,self.attrs)
+        self.builder.data("".join(self.text))
+        self.entered = True
+        return self
+    def __exit__(self,x,y,z):
+        self.parent -= 1
+        self.builder.end(self.name)
+        return False
+#-------------------------------------------------------------------------------
+class XMLBuilder(object):
+    """XmlBuilder(encoding = 'utf-8', # result xml file encoding
+            builder = None, #etree.TreeBuilder or compatible class
+            tab_level = None, #current tabulation level - string
+            format = False,   # make formatted output
+            tab_step = " " * 4) # tabulation step
+    use str(builder) or unicode(builder) to get xml text or
+    ~builder to obtaine etree.ElementTree
+    """
+    def __init__(self,encoding = 'utf-8',
+                      builder = None,
+                      tab_level = None,
+                      format = False,
+                      tab_step = " " * 4):
+        self.__builder = builder or TreeBuilder()
+        self.__encoding = encoding 
+        if format :
+            if tab_level is None:
+                tab_level = ""
+        if tab_level is not None:
+            if not format:
+                raise ValueError("format is False, but tab_level not None")
+        self.__tab_level = tab_level # current format level
+        self.__tab_step = tab_step   # format step
+        self.__has_sub_tag = False   # True, if current tag had childrens
+        self.__node = None
+    # called from _XMLNode when tag opened
+    def __iadd__(self,val):
+        self.__has_sub_tag = False
+        if self.__tab_level is not None:
+            self.__builder.data("\n" + self.__tab_level)
+            self.__tab_level += self.__tab_step
+        return self
+    # called from XMLNode when tag closed
+    def __isub__(self,val):
+        if self.__tab_level is not None:
+            self.__tab_level = self.__tab_level[:-len(self.__tab_step)]
+            if self.__has_sub_tag:
+                self.__builder.data("\n" + self.__tab_level)
+        self.__has_sub_tag = True
+        return self
+    def __getattr__(self,name):
+        return _XMLNode(self,name,self.__builder)
+    def __call__(self,name,*dt,**mp):
+        x = _XMLNode(self,name,self.__builder)
+        x(*dt,**mp)
+        return x
+    #create new tag or add text
+    #possible shift values
+    #string - text
+    #tuple(string1,string2,dict) - new tag with name string1,attrs = dict,and text string2
+    #dict and string2 are optional
+    def __lshift__(self,val):
+        if isinstance(val,basestring):
+            self.__builder.data(val)
+        else:
+            self.__has_sub_tag = True
+            assert hasattr(val,'__len__'),\
+                'Shifted value should be tuple or list like object not %r' % val
+            assert hasattr(val,'__getitem__'),\
+                'Shifted value should be tuple or list like object not %r' % val
+            name = val[0]
+            if len(val) == 3:
+                text = val[1]
+                attrs = val[2]
+            elif len(val) == 1:
+                text = ""
+                attrs = {}
+            elif len(val) == 2:
+                if isinstance(val[1],basestring):
+                    text = val[1]
+                    attrs = {}
+                else:
+                    text = ""
+                    attrs = val[1]
+            if self.__tab_level is not None:
+                self.__builder.data("\n" + self.__tab_level)
+            self.__builder.start(name,
+                                 dict((k,str(v)) for k,v in attrs.items()))
+            if text:
+                self.__builder.data(text)
+            self.__builder.end(name)
+        return self # to allow xml << some1 << some2 << some3
+    #close builder
+    def __invert__(self):
+        if self.__node is not None:
+            return self.__node
+        self.__node = self.__builder.close()
+        return self.__node
+    def __str__(self):
+        """return generated xml"""
+        return tostring(~self,self.__encoding)
+    def __unicode__(self):
+        """return generated xml"""
+        res = tostring(~self,self.__encoding)
+        return res.decode(self.__encoding)
+#-------------------------------------------------------------------------------
diff --git a/xmlbuilder-0.9/xmlbuilder/docs/long_descr.rst b/xmlbuilder-0.9/xmlbuilder/docs/long_descr.rst
new file mode 100644
index 00000000..4e82bc85
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder/docs/long_descr.rst
@@ -0,0 +1,68 @@
+Example of usage:
+-----------------
+
+
+from __future__ import with_statement
+from xmlbuilder import XMLBuilder
+x = XMLBuilder(format=True)
+with x.root(a = 1):
+    with x.data:
+        [x << ('node',{'val':i}) for i in range(10)]
+
+print str(x)
+
+will print
+
+<root a="1">
+    <data>
+        <node val="0" />
+        <node val="1" />
+        <node val="2" />
+        <node val="3" />
+        <node val="4" />
+        <node val="5" />
+        <node val="6" />
+        <node val="7" />
+        <node val="8" />
+        <node val="9" />
+    </data>
+</root>
+
+Mercurial repo:http://hg.assembla.com/MyPackages/
+
+Documentations
+--------------
+`XMLBuilder` is simple library build on top of `ElementTree.TreeBuilder` to
+simplify xml files creation as much as possible. Althow it can produce
+structured result with identated child tags. `XMLBuilder` use python `with`
+statement to define xml tag levels and `<<` operator for simple cases -
+text and tag without childs.
+
+First we need to create xmlbuilder
+
+    from xmlbuilder import XMLBuilder
+    # params - encoding = 'utf8',
+    # builder = None, - ElementTree.TreeBuilder 
+    # tab_level = None, - current tab l;evel - for formatted output only
+    # format = False, - create formatted output
+    # tab_step = " " * 4 - indentation step
+    xml = XMLBuilder()
+
+
+Use `with` statement to make document structure
+    #create and open tag 'root_tag' with text 'text' and attributes
+    with xml.root_tag(text,attr1=val1,attr2=val2):
+        #create and open tag 'sub_tag'
+        with xml.sub_tag(text,attr3=val3):
+            #create tag which are not valid python identificator
+            with xml('one-more-sub-tag',attr7=val37):
+                xml << "Some textual data"
+            #here tag 'one-more-sub-tag' are closed
+			#Tags without children can be created using `<<` operator
+            for val in range(15):
+                xml << ('message',"python rocks!"[:i])
+            #create 15 child tag like <message> python r</message>
+    #all tags closed
+    node = ~x # get etree.ElementTree object
+    xml_data = str(x)
+    unicode_xml_data = unicode(x)
diff --git a/xmlbuilder-0.9/xmlbuilder/tests/__init__.py b/xmlbuilder-0.9/xmlbuilder/tests/__init__.py
new file mode 100644
index 00000000..67eaa67f
--- /dev/null
+++ b/xmlbuilder-0.9/xmlbuilder/tests/__init__.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+from __future__ import with_statement
+#-------------------------------------------------------------------------------
+import unittest
+from xml.etree.ElementTree import fromstring
+#-------------------------------------------------------------------------------
+from xmlbuilder import XMLBuilder
+#-------------------------------------------------------------------------------
+def xmlStructureEqual(xml1,xml2):
+    tree1 = fromstring(xml1)
+    tree2 = fromstring(xml2)
+    return _xmlStructureEqual(tree1,tree2)
+#-------------------------------------------------------------------------------
+def _xmlStructureEqual(tree1,tree2):
+    if tree1.tag != tree2.tag:
+        return False
+    attr1 = list(tree1.attrib.items())
+    attr1.sort()
+    attr2 = list(tree2.attrib.items())
+    attr2.sort()
+    if attr1 != attr2:
+        return False
+    return tree1.getchildren() == tree2.getchildren()
+#-------------------------------------------------------------------------------
+result1 = \
+"""
+<root>
+    <array />
+    <array len="10">
+        <el val="0" />
+        <el val="1">xyz</el>
+        <el val="2">abc</el>
+        <el val="3" />
+        <el val="4" />
+        <el val="5" />
+        <sup-el val="23">test  </sup-el>
+    </array>
+</root>
+""".strip()
+#-------------------------------------------------------------------------------
+class TestXMLBuilder(unittest.TestCase):
+    def testShift(self):
+        xml = (XMLBuilder() << ('root',))
+        self.assertEqual(str(xml),"<root />")
+        
+        xml = XMLBuilder()
+        xml << ('root',"some text")
+        self.assertEqual(str(xml),"<root>some text</root>")
+        
+        xml = XMLBuilder()
+        xml << ('root',{'x':1,'y':'2'})
+        self.assert_(xmlStructureEqual(str(xml),"<root x='1' y='2'>some text</root>"))
+        
+        xml = XMLBuilder()
+        xml << ('root',{'x':1,'y':'2'})
+        self.assert_(xmlStructureEqual(str(xml),"<root x='1' y='2'></root>"))
+
+        xml = XMLBuilder()
+        xml << ('root',{'x':1,'y':'2'})
+        self.assert_(not xmlStructureEqual(str(xml),"<root x='2' y='2'></root>"))
+
+        
+        xml = XMLBuilder()
+        xml << ('root',"gonduras.ua",{'x':1,'y':'2'})
+        self.assert_(xmlStructureEqual(str(xml),"<root x='1' y='2'>gonduras.ua</root>"))
+        
+        xml = XMLBuilder()
+        xml << ('root',"gonduras.ua",{'x':1,'y':'2'})
+        self.assert_(xmlStructureEqual(str(xml),"<root x='1' y='2'>gonduras.com</root>"))
+    #---------------------------------------------------------------------------
+    def testWith(self):
+        xml = XMLBuilder()
+        with xml.root(lenght = 12):
+            pass
+        self.assertEqual(str(xml),'<root lenght="12" />')
+        
+        xml = XMLBuilder()
+        with xml.root():
+            xml << "text1" << "text2" << ('some_node',)
+        self.assertEqual(str(xml),"<root>text1text2<some_node /></root>")
+    #---------------------------------------------------------------------------
+    def testFormat(self):
+        x = XMLBuilder('utf-8',format = True)
+        with x.root():
+            x << ('array',)
+            with x.array(len = 10):
+                with x.el(val = 0):
+                    pass
+                with x.el('xyz',val = 1):
+                    pass
+                x << ("el","abc",{'val':2}) << ('el',dict(val=3))
+                x << ('el',dict(val=4)) << ('el',dict(val='5'))
+                with x('sup-el',val = 23):
+                    x << "test  "
+        self.assertEqual(str(x),result1)
+#-------------------------------------------------------------------------------
+if __name__ == '__main__':
+    unittest.main()
+#-------------------------------------------------------------------------------
-- 
2.47.0