From 0cc7b9add6b022da7381d0ad2de9348c2cb54c09 Mon Sep 17 00:00:00 2001
From: Dave Kuhlman <dkuhlman@davekuhlman.org>
Date: Thu, 14 Dec 2017 14:48:14 -0800
Subject: [PATCH] Fix to export of child that is abstract

---
 README.rst                               |  11 +++++
 generateDS.html                          |   6 +--
 generateDS.py                            |  38 +++++++++-----
 generateDS.txt                           |   2 +-
 generateds_gui_notes.html                |   6 +--
 generateds_gui_notes.txt                 |   2 +-
 gui/generateds_gui.py                    |   2 +-
 librarytemplate_howto.html               |   6 +--
 librarytemplate_howto.txt                |   2 +-
 process_includes.py                      |   2 +-
 setup.py                                 |   2 +-
 tests/OnePer/oneperType00_2One.py        |  28 ++++++++---
 tests/OnePer/oneperType01_2One.py        |  28 ++++++++---
 tests/OnePer/oneperType02_2One.py        |  28 ++++++++---
 tests/OnePer/oneperType03_2One.py        |  28 ++++++++---
 tests/abstract_type1_sup.py              |  30 +++++++++---
 tests/annotations1_sup.py                |  28 ++++++++---
 tests/anonymous_type1_sup.py             |  28 ++++++++---
 tests/anysimpletype1_sup.py              |  28 ++++++++---
 tests/anywildcard1_sup.py                |  28 ++++++++---
 tests/attr_groups1_sup.py                |  28 ++++++++---
 tests/catalogtest1_sup.py                |  28 ++++++++---
 tests/cdata1_sup.py                      |  28 ++++++++---
 tests/cleanupname1_sup.py                |  28 ++++++++---
 tests/copy_all                           |   4 +-
 tests/defaults_cases1_sup.py             |  28 ++++++++---
 tests/defaults_coverage1_sup.py          |  28 ++++++++---
 tests/disable_xml_super1_sup.py          |  12 +++++
 tests/extensions1_sup.py                 |  28 ++++++++---
 tests/ipo1_sup.py                        |  28 ++++++++---
 tests/ipo2_sup.py                        |  28 ++++++++---
 tests/mapcleanname1_sup.py               |  28 ++++++++---
 tests/mixedcontent1_sup.py               |  28 ++++++++---
 tests/mixedcontent2_sup.py               |  28 ++++++++---
 tests/nested_def1_sup.py                 |  28 ++++++++---
 tests/out1_sup.py                        |  28 ++++++++---
 tests/people_procincl1_sup.py            |  30 +++++++++---
 tests/prefix_classname1_sup.py           |  28 ++++++++---
 tests/recursive_simpletype1_sup.py       |  28 ++++++++---
 tests/reference_simpletype1_sup.py       |  28 ++++++++---
 tests/rem_dup_elems1_sup.py              |  28 ++++++++---
 tests/simplecontent_restriction1_sup.py  |  28 ++++++++---
 tests/simpletype_memberspecs1_sup.py     |  28 ++++++++---
 tests/simpletypes_other1_sup.py          |  28 ++++++++---
 tests/to_etree1_sup.py                   |  28 ++++++++---
 tests/validate_simpletypes1_sup.py       |  28 ++++++++---
 tests/validate_simpletypes1_warnings.txt |  60 +++++++++++------------
 tests/validate_simpletypes2_sup.py       |  28 ++++++++---
 tutorial/generateds_tutorial.html        |   6 +--
 tutorial/generateds_tutorial.txt         |   2 +-
 tutorial/generateds_tutorial.zip         | Bin 48769 -> 48768 bytes
 51 files changed, 816 insertions(+), 303 deletions(-)

diff --git a/README.rst b/README.rst
index f00ca0e..c900431 100644
--- a/README.rst
+++ b/README.rst
@@ -141,6 +141,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 Change history
 --------------
 
+Version 2.29.4 (12/14/2017)
+
+- Fix for exporting the child of an element, when that child is
+  declared as an instance of an abstract type (abstract="true" in
+  the schema).  When exporting, the type of the child needs to be
+  determine at runtime through polymorphism.  This fix prevents the
+  containing (parent) object from passing the name of the abstract
+  class to the instance of the concrete class when calling its
+  export function.  Thanks to Rob Calvert for reporting this and for
+  his analysis that helped me understand the problem.
+
 Version 2.29.3 (12/11/2017)
 
 - Resolved an issue with a type casting problem that occurs when a
diff --git a/generateDS.html b/generateDS.html
index 1dcd6c3..0184ba9 100644
--- a/generateDS.html
+++ b/generateDS.html
@@ -220,7 +220,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.3</td>
+<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.4</td>
 </tr>
 </tbody>
 </table>
@@ -229,7 +229,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">date:</th><td class="field-body">December 11, 2017</td>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">December 14, 2017</td>
 </tr>
 </tbody>
 </table>
@@ -3380,7 +3380,7 @@ following among others:</p>
 <div class="footer">
 <hr class="footer" />
 <a class="reference external" href="generateDS.txt">View document source</a>.
-Generated on: 2017-12-11 21:49 UTC.
+Generated on: 2017-12-14 22:46 UTC.
 Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
 
 </div>
diff --git a/generateDS.py b/generateDS.py
index 146f8a0..bfe6ae7 100755
--- a/generateDS.py
+++ b/generateDS.py
@@ -229,7 +229,7 @@ logging.disable(logging.INFO)
 # Do not modify the following VERSION comments.
 # Used by updateversion.py.
 ##VERSION##
-VERSION = '2.29.3'
+VERSION = '2.29.4'
 ##VERSION##
 
 BaseStrTypes = six.string_types
@@ -2751,14 +2751,14 @@ def generateExportChildren(wrt, element, hasChildren, namespace):
                     wrt("%sfor %s_ in self.%s:\n" % (
                         fill, name, name,))
                     wrt("%s    %s_.export(outfile, level, namespace_, "
-                        "name_='%s', pretty_print=pretty_print)\n" % (
-                            fill, name, unmappedName, ))
+                        "pretty_print=pretty_print)\n" % (
+                            fill, name, ))
                 elif abstract_child:
                     wrt("%sif self.%s is not None:\n" % (fill, name, ))
                     wrt("%s    self.%s.export(outfile, level, "
-                        "namespace_, name_='%s', "
+                        "namespace_, "
                         "pretty_print=pretty_print)\n" % (
-                            fill, name, unmappedName, ))
+                            fill, name, ))
                 elif child.getMaxOccurs() > 1:
                     generateExportFn_2(
                         wrt, child, unmappedName, namespace, '    ')
@@ -5051,6 +5051,18 @@ else:
 #xmldisable#    doc = etree_.parse(infile, parser=parser, **kwargs)
 #xmldisable#    return doc
 
+#xmldisable#def parsexmlstring_(instring, parser=None, **kwargs):
+#xmldisable#    if parser is None:
+#xmldisable#        # Use the lxml ElementTree compatible parser so that, e.g.,
+#xmldisable#        #   we ignore comments.
+#xmldisable#        try:
+#xmldisable#            parser = etree_.ETCompatXMLParser()
+#xmldisable#        except AttributeError:
+#xmldisable#            # fallback to xml.etree
+#xmldisable#            parser = etree_.XMLParser()
+#xmldisable#    element = etree_.fromstring(instring, parser=parser, **kwargs)
+#xmldisable#    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -5879,12 +5891,15 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
-%(preserve_cdata_tags)s    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
+%(preserve_cdata_tags)s    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = '%(rootElement)s'
@@ -5892,7 +5907,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 #silence#    if not silence:
 #silence#        sys.stdout.write('<?xml version="1.0" ?>\\n')
 #silence#        rootObj.export(
diff --git a/generateDS.txt b/generateDS.txt
index 9d1961b..608888f 100644
--- a/generateDS.txt
+++ b/generateDS.txt
@@ -12,7 +12,7 @@ generateDS -- Generate Data Structures from XML Schema
 
 .. version
 
-:revision: 2.29.3
+:revision: 2.29.4
 
 .. version
 
diff --git a/generateds_gui_notes.html b/generateds_gui_notes.html
index 0c269e4..576b482 100644
--- a/generateds_gui_notes.html
+++ b/generateds_gui_notes.html
@@ -220,7 +220,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.3</td>
+<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.4</td>
 </tr>
 </tbody>
 </table>
@@ -229,7 +229,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">date:</th><td class="field-body">December 11, 2017</td>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">December 14, 2017</td>
 </tr>
 </tbody>
 </table>
@@ -401,7 +401,7 @@ $ mv generateds_gui.mo locale/ru/LC_MESSAGES/
 <div class="footer">
 <hr class="footer" />
 <a class="reference external" href="generateds_gui_notes.txt">View document source</a>.
-Generated on: 2017-12-11 21:49 UTC.
+Generated on: 2017-12-14 22:46 UTC.
 Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
 
 </div>
diff --git a/generateds_gui_notes.txt b/generateds_gui_notes.txt
index a45bea9..59a6d69 100644
--- a/generateds_gui_notes.txt
+++ b/generateds_gui_notes.txt
@@ -12,7 +12,7 @@ GenerateDS GUI Notes
 
 .. version
 
-:revision: 2.29.3
+:revision: 2.29.4
 
 .. version
 
diff --git a/gui/generateds_gui.py b/gui/generateds_gui.py
index acf8ee3..4e13f90 100644
--- a/gui/generateds_gui.py
+++ b/gui/generateds_gui.py
@@ -41,7 +41,7 @@ from libgenerateDS.gui import generateds_gui_session
 # Do not modify the following VERSION comments.
 # Used by updateversion.py.
 ##VERSION##
-VERSION = '2.29.3'
+VERSION = '2.29.4'
 ##VERSION##
 
 
diff --git a/librarytemplate_howto.html b/librarytemplate_howto.html
index 5897afc..0dff031 100644
--- a/librarytemplate_howto.html
+++ b/librarytemplate_howto.html
@@ -217,7 +217,7 @@ dkuhlman (at) davekuhlman (dot) org
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.3</td>
+<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.4</td>
 </tr>
 </tbody>
 </table>
@@ -226,7 +226,7 @@ dkuhlman (at) davekuhlman (dot) org
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">date:</th><td class="field-body">December 11, 2017</td>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">December 14, 2017</td>
 </tr>
 </tbody>
 </table>
@@ -380,7 +380,7 @@ this command for your needs.  For example, you may need to use
 <div class="footer">
 <hr class="footer" />
 <a class="reference external" href="librarytemplate_howto.txt">View document source</a>.
-Generated on: 2017-12-11 21:49 UTC.
+Generated on: 2017-12-14 22:46 UTC.
 Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
 
 </div>
diff --git a/librarytemplate_howto.txt b/librarytemplate_howto.txt
index 0515349..702e571 100644
--- a/librarytemplate_howto.txt
+++ b/librarytemplate_howto.txt
@@ -8,7 +8,7 @@ How to package a generateDS.py generated library
 
 .. version
 
-:revision: 2.29.3
+:revision: 2.29.4
 
 .. version
 
diff --git a/process_includes.py b/process_includes.py
index 8568033..723639a 100644
--- a/process_includes.py
+++ b/process_includes.py
@@ -40,7 +40,7 @@ except ImportError:
 # Do not modify the following VERSION comments.
 # Used by updateversion.py.
 ##VERSION##
-VERSION = '2.29.3'
+VERSION = '2.29.4'
 ##VERSION##
 
 CatalogDict = {}
diff --git a/setup.py b/setup.py
index da910b9..93eeec3 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ setup(name="generateDS",
 # Do not modify the following VERSION comments.
 # Used by updateversion.py.
 ##VERSION##
-    version="2.29.3",
+    version="2.29.4",
 ##VERSION##
     author="Dave Kuhlman",
     author_email="dkuhlman@davekuhlman.org",
diff --git a/tests/OnePer/oneperType00_2One.py b/tests/OnePer/oneperType00_2One.py
index 2bd2128..1ffccc3 100644
--- a/tests/OnePer/oneperType00_2One.py
+++ b/tests/OnePer/oneperType00_2One.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -869,13 +881,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'oneperType00_1'
@@ -883,7 +898,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/OnePer/oneperType01_2One.py b/tests/OnePer/oneperType01_2One.py
index f22521e..f5a4267 100644
--- a/tests/OnePer/oneperType01_2One.py
+++ b/tests/OnePer/oneperType01_2One.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -946,13 +958,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'oneperType01_1'
@@ -960,7 +975,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/OnePer/oneperType02_2One.py b/tests/OnePer/oneperType02_2One.py
index b7f4e24..6c37be0 100644
--- a/tests/OnePer/oneperType02_2One.py
+++ b/tests/OnePer/oneperType02_2One.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -946,13 +958,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'oneperType02_1'
@@ -960,7 +975,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/OnePer/oneperType03_2One.py b/tests/OnePer/oneperType03_2One.py
index a240d22..5b825be 100644
--- a/tests/OnePer/oneperType03_2One.py
+++ b/tests/OnePer/oneperType03_2One.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -946,13 +958,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'oneperType03_1'
@@ -960,7 +975,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/abstract_type1_sup.py b/tests/abstract_type1_sup.py
index af44aab..8f617de 100644
--- a/tests/abstract_type1_sup.py
+++ b/tests/abstract_type1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -774,7 +786,7 @@ class carrierType(GeneratedsSuper):
         else:
             eol_ = ''
         for fleet_ in self.fleet:
-            fleet_.export(outfile, level, namespace_, name_='fleet', pretty_print=pretty_print)
+            fleet_.export(outfile, level, namespace_, pretty_print=pretty_print)
     def build(self, node):
         already_processed = set()
         self.buildAttributes(node, node.attrib, already_processed)
@@ -1081,13 +1093,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'carrierType'
@@ -1095,7 +1110,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/annotations1_sup.py b/tests/annotations1_sup.py
index b95527a..306fae8 100644
--- a/tests/annotations1_sup.py
+++ b/tests/annotations1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1073,13 +1085,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'document1Type'
@@ -1087,7 +1102,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/anonymous_type1_sup.py b/tests/anonymous_type1_sup.py
index 200d52e..80237f1 100644
--- a/tests/anonymous_type1_sup.py
+++ b/tests/anonymous_type1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1140,13 +1152,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'FooList'
@@ -1154,7 +1169,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/anysimpletype1_sup.py b/tests/anysimpletype1_sup.py
index 126c501..e3f355a 100644
--- a/tests/anysimpletype1_sup.py
+++ b/tests/anysimpletype1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -970,13 +982,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'test1element'
@@ -984,7 +999,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/anywildcard1_sup.py b/tests/anywildcard1_sup.py
index 95dfc40..22dac15 100644
--- a/tests/anywildcard1_sup.py
+++ b/tests/anywildcard1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1322,13 +1334,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'PlantType_single'
@@ -1336,7 +1351,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/attr_groups1_sup.py b/tests/attr_groups1_sup.py
index 676f8c6..4c34fd7 100644
--- a/tests/attr_groups1_sup.py
+++ b/tests/attr_groups1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -968,13 +980,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'GetUserReq'
@@ -982,7 +997,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/catalogtest1_sup.py b/tests/catalogtest1_sup.py
index f0c9f3d..65a9e4d 100644
--- a/tests/catalogtest1_sup.py
+++ b/tests/catalogtest1_sup.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -779,13 +791,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = ''
@@ -793,7 +808,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/cdata1_sup.py b/tests/cdata1_sup.py
index ce39ea6..147ebb0 100644
--- a/tests/cdata1_sup.py
+++ b/tests/cdata1_sup.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -940,13 +952,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'cdataListType'
@@ -954,7 +969,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/cleanupname1_sup.py b/tests/cleanupname1_sup.py
index 3d6c984..0c1f1fc 100644
--- a/tests/cleanupname1_sup.py
+++ b/tests/cleanupname1_sup.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1290,13 +1302,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'dataKind'
@@ -1304,7 +1319,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/copy_all b/tests/copy_all
index 0ba8184..87ffae9 100755
--- a/tests/copy_all
+++ b/tests/copy_all
@@ -59,7 +59,7 @@ cp rem_dup_elems2_sup.py rem_dup_elems1_sup.py
 cp rem_dup_elems2_sub.py rem_dup_elems1_sub.py
 cp disable_xml_super2_sup.py disable_xml_super1_sup.py
 cp disable_xml_super2_sub.py disable_xml_super1_sub.py
-cp defaults_cases_export2_sub.py defaults_cases_export1_sub.py
-cp defaults_cases_export2_sup.py defaults_cases_export1_sup.py
+#cp defaults_cases_export2_sub.py defaults_cases_export1_sub.py
+#cp defaults_cases_export2_sup.py defaults_cases_export1_sup.py
 cp mixedcontent2_sub.py mixedcontent1_sub.py
 cp mixedcontent2_sup.py mixedcontent1_sup.py
diff --git a/tests/defaults_cases1_sup.py b/tests/defaults_cases1_sup.py
index 024b77a..e576fe2 100644
--- a/tests/defaults_cases1_sup.py
+++ b/tests/defaults_cases1_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1175,13 +1187,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'DefaultTypes'
@@ -1189,7 +1204,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/defaults_coverage1_sup.py b/tests/defaults_coverage1_sup.py
index 08d5248..0979dee 100644
--- a/tests/defaults_coverage1_sup.py
+++ b/tests/defaults_coverage1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1538,13 +1550,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'DefaultTypes'
@@ -1552,7 +1567,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/disable_xml_super1_sup.py b/tests/disable_xml_super1_sup.py
index 78be9fb..fd6688a 100644
--- a/tests/disable_xml_super1_sup.py
+++ b/tests/disable_xml_super1_sup.py
@@ -58,6 +58,18 @@ else:
 ##     doc = etree_.parse(infile, parser=parser, **kwargs)
 ##     return doc
 
+## def parsexmlstring_(instring, parser=None, **kwargs):
+##     if parser is None:
+##         # Use the lxml ElementTree compatible parser so that, e.g.,
+##         #   we ignore comments.
+##         try:
+##             parser = etree_.ETCompatXMLParser()
+##         except AttributeError:
+##             # fallback to xml.etree
+##             parser = etree_.XMLParser()
+##     element = etree_.fromstring(instring, parser=parser, **kwargs)
+##     return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
diff --git a/tests/extensions1_sup.py b/tests/extensions1_sup.py
index efd5c31..e24e044 100644
--- a/tests/extensions1_sup.py
+++ b/tests/extensions1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1859,13 +1871,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'containerType'
@@ -1873,7 +1888,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/ipo1_sup.py b/tests/ipo1_sup.py
index c1c929a..5299bd7 100644
--- a/tests/ipo1_sup.py
+++ b/tests/ipo1_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1526,13 +1538,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'PurchaseOrderType'
@@ -1540,7 +1555,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/ipo2_sup.py b/tests/ipo2_sup.py
index c1c929a..5299bd7 100644
--- a/tests/ipo2_sup.py
+++ b/tests/ipo2_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1526,13 +1538,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'PurchaseOrderType'
@@ -1540,7 +1555,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/mapcleanname1_sup.py b/tests/mapcleanname1_sup.py
index 711180e..b3fe993 100644
--- a/tests/mapcleanname1_sup.py
+++ b/tests/mapcleanname1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1637,13 +1649,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'complex_type01'
@@ -1651,7 +1666,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/mixedcontent1_sup.py b/tests/mixedcontent1_sup.py
index 59f9c98..475c35d 100644
--- a/tests/mixedcontent1_sup.py
+++ b/tests/mixedcontent1_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1319,13 +1331,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'rootType'
@@ -1333,7 +1348,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/mixedcontent2_sup.py b/tests/mixedcontent2_sup.py
index 59f9c98..475c35d 100644
--- a/tests/mixedcontent2_sup.py
+++ b/tests/mixedcontent2_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1319,13 +1331,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'rootType'
@@ -1333,7 +1348,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/nested_def1_sup.py b/tests/nested_def1_sup.py
index 1627dc4..eea9981 100644
--- a/tests/nested_def1_sup.py
+++ b/tests/nested_def1_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1189,13 +1201,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'containerType'
@@ -1203,7 +1218,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/out1_sup.py b/tests/out1_sup.py
index b2588ef..898638c 100644
--- a/tests/out1_sup.py
+++ b/tests/out1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -3579,13 +3591,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'people'
@@ -3593,7 +3608,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/people_procincl1_sup.py b/tests/people_procincl1_sup.py
index 2fa8158..8758543 100644
--- a/tests/people_procincl1_sup.py
+++ b/tests/people_procincl1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1544,7 +1556,7 @@ class agent(GeneratedsSuper):
         if self.info is not None:
             self.info.export(outfile, level, namespace_, name_='info', pretty_print=pretty_print)
         for vehicle_ in self.vehicle:
-            vehicle_.export(outfile, level, namespace_, name_='vehicle', pretty_print=pretty_print)
+            vehicle_.export(outfile, level, namespace_, pretty_print=pretty_print)
     def build(self, node):
         already_processed = set()
         self.buildAttributes(node, node.attrib, already_processed)
@@ -2978,13 +2990,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'people'
@@ -2992,7 +3007,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/prefix_classname1_sup.py b/tests/prefix_classname1_sup.py
index b28364c..04ac7be 100644
--- a/tests/prefix_classname1_sup.py
+++ b/tests/prefix_classname1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -2584,13 +2596,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'people'
@@ -2598,7 +2613,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/recursive_simpletype1_sup.py b/tests/recursive_simpletype1_sup.py
index 05c0429..bb467f4 100644
--- a/tests/recursive_simpletype1_sup.py
+++ b/tests/recursive_simpletype1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -884,13 +896,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'PersonType'
@@ -898,7 +913,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/reference_simpletype1_sup.py b/tests/reference_simpletype1_sup.py
index 3f3f430..e9fb502 100644
--- a/tests/reference_simpletype1_sup.py
+++ b/tests/reference_simpletype1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -902,13 +914,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'xs_integer'
@@ -916,7 +931,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/rem_dup_elems1_sup.py b/tests/rem_dup_elems1_sup.py
index beef45b..c4ed8e3 100644
--- a/tests/rem_dup_elems1_sup.py
+++ b/tests/rem_dup_elems1_sup.py
@@ -56,6 +56,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -958,13 +970,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'authorsType'
@@ -972,7 +987,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/simplecontent_restriction1_sup.py b/tests/simplecontent_restriction1_sup.py
index d5f8785..c1ba056 100644
--- a/tests/simplecontent_restriction1_sup.py
+++ b/tests/simplecontent_restriction1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1077,13 +1089,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'IdentifierType'
@@ -1091,7 +1106,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/simpletype_memberspecs1_sup.py b/tests/simpletype_memberspecs1_sup.py
index cd93882..4428797 100644
--- a/tests/simpletype_memberspecs1_sup.py
+++ b/tests/simpletype_memberspecs1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -934,13 +946,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'SpecialDate'
@@ -948,7 +963,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/simpletypes_other1_sup.py b/tests/simpletypes_other1_sup.py
index b295309..32ef27f 100644
--- a/tests/simpletypes_other1_sup.py
+++ b/tests/simpletypes_other1_sup.py
@@ -55,6 +55,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1279,13 +1291,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'simpleTypeTestsType'
@@ -1293,7 +1308,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/to_etree1_sup.py b/tests/to_etree1_sup.py
index 911ec6d..f2b634d 100644
--- a/tests/to_etree1_sup.py
+++ b/tests/to_etree1_sup.py
@@ -57,6 +57,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -3028,13 +3040,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'peopleType'
@@ -3042,7 +3057,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
 ##     if not silence:
 ##         sys.stdout.write('<?xml version="1.0" ?>\n')
 ##         rootObj.export(
diff --git a/tests/validate_simpletypes1_sup.py b/tests/validate_simpletypes1_sup.py
index 938d76e..dd6a1fa 100644
--- a/tests/validate_simpletypes1_sup.py
+++ b/tests/validate_simpletypes1_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1724,13 +1736,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'containerType'
@@ -1738,7 +1753,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tests/validate_simpletypes1_warnings.txt b/tests/validate_simpletypes1_warnings.txt
index 0309c68..2b72df6 100644
--- a/tests/validate_simpletypes1_warnings.txt
+++ b/tests/validate_simpletypes1_warnings.txt
@@ -1,60 +1,60 @@
-tests/validate_simpletypes2_sup.py:1047: UserWarning: Value "2" does not match xsd minExclusive restriction on integer_range_1_st
+tests/validate_simpletypes2_sup.py:1059: UserWarning: Value "2" does not match xsd minExclusive restriction on integer_range_1_st
   warnings_.warn('Value "%(value)s" does not match xsd minExclusive restriction on integer_range_1_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1057: UserWarning: Value "mmaaa1234mnopzzz" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
+tests/validate_simpletypes2_sup.py:1069: UserWarning: Value "mmaaa1234mnopzzz" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
   warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_pattern_st_patterns_, ))
-tests/validate_simpletypes2_sup.py:1070: UserWarning: Value "floatxx" does not match xsd enumeration restriction on token_enum_st
+tests/validate_simpletypes2_sup.py:1082: UserWarning: Value "floatxx" does not match xsd enumeration restriction on token_enum_st
   warnings_.warn('Value "%(value)s" does not match xsd enumeration restriction on token_enum_st' % {"value" : value.encode("utf-8")} )
-tests/validate_simpletypes2_sup.py:1077: UserWarning: Value "22" does not match xsd maxInclusive restriction on integer_range_incl_st
+tests/validate_simpletypes2_sup.py:1089: UserWarning: Value "22" does not match xsd maxInclusive restriction on integer_range_incl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxInclusive restriction on integer_range_incl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1082: UserWarning: Value "-40" does not match xsd minExclusive restriction on integer_range_excl_st
+tests/validate_simpletypes2_sup.py:1094: UserWarning: Value "-40" does not match xsd minExclusive restriction on integer_range_excl_st
   warnings_.warn('Value "%(value)s" does not match xsd minExclusive restriction on integer_range_excl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1091: UserWarning: Value "mno pqr" does not match xsd minLength restriction on min_max_length_st
+tests/validate_simpletypes2_sup.py:1103: UserWarning: Value "mno pqr" does not match xsd minLength restriction on min_max_length_st
   warnings_.warn('Value "%(value)s" does not match xsd minLength restriction on min_max_length_st' % {"value" : value.encode("utf-8")} )
-tests/validate_simpletypes2_sup.py:1096: UserWarning: Value "012345" does not match xsd length restriction on length_st
+tests/validate_simpletypes2_sup.py:1108: UserWarning: Value "012345" does not match xsd length restriction on length_st
   warnings_.warn('Value "%(value)s" does not match xsd length restriction on length_st' % {"value" : value.encode("utf-8")} )
-tests/validate_simpletypes2_sup.py:1173: UserWarning: Value "0.2" does not match xsd minInclusive restriction on anonymous_float_valueType
+tests/validate_simpletypes2_sup.py:1185: UserWarning: Value "0.2" does not match xsd minInclusive restriction on anonymous_float_valueType
   warnings_.warn('Value "%(value)s" does not match xsd minInclusive restriction on anonymous_float_valueType' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1167: UserWarning: Value "efgh" does not match xsd pattern restrictions: [['^abcd$|^ef\\|gh$']]
+tests/validate_simpletypes2_sup.py:1179: UserWarning: Value "efgh" does not match xsd pattern restrictions: [['^abcd$|^ef\\|gh$']]
   warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_vbar_pattern_st_patterns_, ))
-tests/validate_simpletypes2_sup.py:1049: UserWarning: Value "9" does not match xsd maxExclusive restriction on integer_range_1_st
+tests/validate_simpletypes2_sup.py:1061: UserWarning: Value "9" does not match xsd maxExclusive restriction on integer_range_1_st
   warnings_.warn('Value "%(value)s" does not match xsd maxExclusive restriction on integer_range_1_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1057: UserWarning: Value "aaa1234mnopzzzbcd" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
+tests/validate_simpletypes2_sup.py:1069: UserWarning: Value "aaa1234mnopzzzbcd" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
   warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_pattern_st_patterns_, ))
-tests/validate_simpletypes2_sup.py:1075: UserWarning: Value "-50" does not match xsd minInclusive restriction on integer_range_incl_st
+tests/validate_simpletypes2_sup.py:1087: UserWarning: Value "-50" does not match xsd minInclusive restriction on integer_range_incl_st
   warnings_.warn('Value "%(value)s" does not match xsd minInclusive restriction on integer_range_incl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1089: UserWarning: Value "asdf asdf asdf asdf asdf asdf" does not match xsd maxLength restriction on min_max_length_st
+tests/validate_simpletypes2_sup.py:1101: UserWarning: Value "asdf asdf asdf asdf asdf asdf" does not match xsd maxLength restriction on min_max_length_st
   warnings_.warn('Value "%(value)s" does not match xsd maxLength restriction on min_max_length_st' % {"value" : value.encode("utf-8")} )
-tests/validate_simpletypes2_sup.py:1096: UserWarning: Value "01234567890" does not match xsd length restriction on length_st
+tests/validate_simpletypes2_sup.py:1108: UserWarning: Value "01234567890" does not match xsd length restriction on length_st
   warnings_.warn('Value "%(value)s" does not match xsd length restriction on length_st' % {"value" : value.encode("utf-8")} )
-tests/validate_simpletypes2_sup.py:1106: UserWarning: Value "2015-05-01" does not match xsd minInclusive restriction on date_minincl_st
+tests/validate_simpletypes2_sup.py:1118: UserWarning: Value "2015-05-01" does not match xsd minInclusive restriction on date_minincl_st
   warnings_.warn('Value "%(value)s" does not match xsd minInclusive restriction on date_minincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1111: UserWarning: Value "2015-11-01" does not match xsd maxInclusive restriction on date_maxincl_st
+tests/validate_simpletypes2_sup.py:1123: UserWarning: Value "2015-11-01" does not match xsd maxInclusive restriction on date_maxincl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxInclusive restriction on date_maxincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1116: UserWarning: Value "2015-05-01" does not match xsd minExclusive restriction on date_minexcl_st
+tests/validate_simpletypes2_sup.py:1128: UserWarning: Value "2015-05-01" does not match xsd minExclusive restriction on date_minexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd minExclusive restriction on date_minexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1121: UserWarning: Value "2015-11-01" does not match xsd maxExclusive restriction on date_maxexcl_st
+tests/validate_simpletypes2_sup.py:1133: UserWarning: Value "2015-11-01" does not match xsd maxExclusive restriction on date_maxexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxExclusive restriction on date_maxexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1126: UserWarning: Value "13:30:00" does not match xsd minInclusive restriction on time_minincl_st
+tests/validate_simpletypes2_sup.py:1138: UserWarning: Value "13:30:00" does not match xsd minInclusive restriction on time_minincl_st
   warnings_.warn('Value "%(value)s" does not match xsd minInclusive restriction on time_minincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1131: UserWarning: Value "17:00:00" does not match xsd maxInclusive restriction on time_maxincl_st
+tests/validate_simpletypes2_sup.py:1143: UserWarning: Value "17:00:00" does not match xsd maxInclusive restriction on time_maxincl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxInclusive restriction on time_maxincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1136: UserWarning: Value "13:30:00" does not match xsd minExclusive restriction on time_minexcl_st
+tests/validate_simpletypes2_sup.py:1148: UserWarning: Value "13:30:00" does not match xsd minExclusive restriction on time_minexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd minExclusive restriction on time_minexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1141: UserWarning: Value "17:00:00" does not match xsd maxExclusive restriction on time_maxexcl_st
+tests/validate_simpletypes2_sup.py:1153: UserWarning: Value "17:00:00" does not match xsd maxExclusive restriction on time_maxexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxExclusive restriction on time_maxexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1146: UserWarning: Value "2015-06-01 13:20:10" does not match xsd minInclusive restriction on datetime_minincl_st
+tests/validate_simpletypes2_sup.py:1158: UserWarning: Value "2015-06-01 13:20:10" does not match xsd minInclusive restriction on datetime_minincl_st
   warnings_.warn('Value "%(value)s" does not match xsd minInclusive restriction on datetime_minincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1151: UserWarning: Value "2015-11-01 14:20:10" does not match xsd maxInclusive restriction on datetime_maxincl_st
+tests/validate_simpletypes2_sup.py:1163: UserWarning: Value "2015-11-01 14:20:10" does not match xsd maxInclusive restriction on datetime_maxincl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxInclusive restriction on datetime_maxincl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1156: UserWarning: Value "2015-06-01 13:20:10" does not match xsd minExclusive restriction on datetime_minexcl_st
+tests/validate_simpletypes2_sup.py:1168: UserWarning: Value "2015-06-01 13:20:10" does not match xsd minExclusive restriction on datetime_minexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd minExclusive restriction on datetime_minexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1161: UserWarning: Value "2015-11-01 14:20:10" does not match xsd maxExclusive restriction on datetime_maxexcl_st
+tests/validate_simpletypes2_sup.py:1173: UserWarning: Value "2015-11-01 14:20:10" does not match xsd maxExclusive restriction on datetime_maxexcl_st
   warnings_.warn('Value "%(value)s" does not match xsd maxExclusive restriction on datetime_maxexcl_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1175: UserWarning: Value "6.6" does not match xsd maxInclusive restriction on anonymous_float_valueType
+tests/validate_simpletypes2_sup.py:1187: UserWarning: Value "6.6" does not match xsd maxInclusive restriction on anonymous_float_valueType
   warnings_.warn('Value "%(value)s" does not match xsd maxInclusive restriction on anonymous_float_valueType' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1054: UserWarning: Value "aaa12zzz" does not match xsd minLength restriction on pattern_st
+tests/validate_simpletypes2_sup.py:1066: UserWarning: Value "aaa12zzz" does not match xsd minLength restriction on pattern_st
   warnings_.warn('Value "%(value)s" does not match xsd minLength restriction on pattern_st' % {"value" : value} )
-tests/validate_simpletypes2_sup.py:1057: UserWarning: Value "aaa12zzz" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
+tests/validate_simpletypes2_sup.py:1069: UserWarning: Value "aaa12zzz" does not match xsd pattern restrictions: [['^aaa.*zzz$', '^bbb.*xxx$'], ['^.*123.*$', '^.*456.*$']]
   warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_pattern_st_patterns_, ))
-tests/validate_simpletypes2_sup.py:1599: UserWarning: Value "pqrst" does not match xsd minLength restriction on simpleTwoElementTwoType
+tests/validate_simpletypes2_sup.py:1611: UserWarning: Value "pqrst" does not match xsd minLength restriction on simpleTwoElementTwoType
   warnings_.warn('Value "%(value)s" does not match xsd minLength restriction on simpleTwoElementTwoType' % {"value" : value.encode("utf-8")} )
diff --git a/tests/validate_simpletypes2_sup.py b/tests/validate_simpletypes2_sup.py
index 938d76e..dd6a1fa 100644
--- a/tests/validate_simpletypes2_sup.py
+++ b/tests/validate_simpletypes2_sup.py
@@ -54,6 +54,18 @@ def parsexml_(infile, parser=None, **kwargs):
     doc = etree_.parse(infile, parser=parser, **kwargs)
     return doc
 
+def parsexmlstring_(instring, parser=None, **kwargs):
+    if parser is None:
+        # Use the lxml ElementTree compatible parser so that, e.g.,
+        #   we ignore comments.
+        try:
+            parser = etree_.ETCompatXMLParser()
+        except AttributeError:
+            # fallback to xml.etree
+            parser = etree_.XMLParser()
+    element = etree_.fromstring(instring, parser=parser, **kwargs)
+    return element
+
 #
 # Namespace prefix definition table (and other attributes, too)
 #
@@ -1724,13 +1736,16 @@ def parseEtree(inFileName, silence=False):
 
 
 def parseString(inString, silence=False):
-    if sys.version_info.major == 2:
-        from StringIO import StringIO as IOBuffer
-    else:
-        from io import BytesIO as IOBuffer
+    '''Parse a string, create the object tree, and export it.
+
+    Arguments:
+    - inString -- A string.  This XML fragment should not start
+      with an XML declaration containing an encoding.
+    - silence -- A boolean.  If False, export the object.
+    Returns -- The root object in the tree.
+    '''
     parser = None
-    doc = parsexml_(IOBuffer(inString), parser)
-    rootNode = doc.getroot()
+    rootNode= parsexmlstring_(inString, parser)
     rootTag, rootClass = get_root_tag(rootNode)
     if rootClass is None:
         rootTag = 'containerType'
@@ -1738,7 +1753,6 @@ def parseString(inString, silence=False):
     rootObj = rootClass.factory()
     rootObj.build(rootNode)
     # Enable Python to collect the space used by the DOM.
-    doc = None
     if not silence:
         sys.stdout.write('<?xml version="1.0" ?>\n')
         rootObj.export(
diff --git a/tutorial/generateds_tutorial.html b/tutorial/generateds_tutorial.html
index 6bcda28..40a49cd 100644
--- a/tutorial/generateds_tutorial.html
+++ b/tutorial/generateds_tutorial.html
@@ -219,7 +219,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.3</td>
+<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.4</td>
 </tr>
 </tbody>
 </table>
@@ -228,7 +228,7 @@ They are used by updateversion.py. -->
 <col class="field-name" />
 <col class="field-body" />
 <tbody valign="top">
-<tr class="field"><th class="field-name">date:</th><td class="field-body">December 11, 2017</td>
+<tr class="field"><th class="field-name">date:</th><td class="field-body">December 14, 2017</td>
 </tr>
 </tbody>
 </table>
@@ -1210,7 +1210,7 @@ named <tt class="docutils literal">garden_api.py</tt>, you can create an instanc
 <div class="footer">
 <hr class="footer" />
 <a class="reference external" href="generateds_tutorial.txt">View document source</a>.
-Generated on: 2017-12-11 21:49 UTC.
+Generated on: 2017-12-14 22:46 UTC.
 Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source.
 
 </div>
diff --git a/tutorial/generateds_tutorial.txt b/tutorial/generateds_tutorial.txt
index 3124998..ec4b328 100644
--- a/tutorial/generateds_tutorial.txt
+++ b/tutorial/generateds_tutorial.txt
@@ -11,7 +11,7 @@ generateDS -- Introduction and Tutorial
 
 .. version
 
-:revision: 2.29.3
+:revision: 2.29.4
 
 .. version
 
diff --git a/tutorial/generateds_tutorial.zip b/tutorial/generateds_tutorial.zip
index 4c6f535896bcc912fc6e2b4a715ba0ec1c59a05b..2df8e3b9633f8cec64f6150fb17446a73cb67f39 100644
GIT binary patch
delta 6920
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
OlTN-Z24lGZ0001ovp(Da

delta 6927
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
V0#lWfkGU0-O};G#WVrwU004JiNhtsT

-- 
GitLab