diff -r fd9f7bdd7472 Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py Fri Jun 20 01:38:37 2014 -0700 +++ b/Lib/test/test_xml_etree.py Sun Jun 22 14:02:28 2014 -0400 @@ -1185,6 +1185,30 @@ """.format(html.escape(SIMPLE_XMLFILE, True)) +XINCLUDE["Recursive1.xml"] = """\ + + +

The following is the source code of Recursive2.xml:

+ +
+""" + +XINCLUDE["Recursive2.xml"] = """\ + + +

The following is the source code of Recursive2.xml:

+ +
+""" + +XINCLUDE["Recursive3.xml"] = """\ + + +

The following is the source code of Recursive2.xml:

+ +
+""" + # # badly formatted xi:include tags @@ -1306,6 +1330,12 @@ ' \n' '') # C5 + document = self.xinclude_loader("Recursive1.xml") + with self.assertRaises(SyntaxError) as cm: + ElementInclude.include(document,self.xinclude_loader) + self.assertEqual(str(cm.exception),"recursive include detected: loaded Recursive2.xml more than once") + + def test_xinclude_failures(self): from xml.etree import ElementInclude diff -r fd9f7bdd7472 Lib/xml/etree/ElementInclude.py --- a/Lib/xml/etree/ElementInclude.py Fri Jun 20 01:38:37 2014 -0700 +++ b/Lib/xml/etree/ElementInclude.py Sun Jun 22 14:02:28 2014 -0400 @@ -114,6 +114,7 @@ "cannot load %r as %r" % (href, parse) ) node = copy.copy(node) + _include(node,loader,[href]) if e.tail: node.tail = (node.tail or "") + e.tail elem[i] = node @@ -141,3 +142,73 @@ else: include(e, loader) i = i + 1 + +## +# Expand XInclude directives. Helper function identical to include, +# Only difference is that the already_included array detects +# recursive includes. +# +# @param elem Root element. +# @param loader Optional resource loader. If omitted, it defaults +# to {@link default_loader}. If given, it should be a callable +# that implements the same interface as default_loader. +# @param already_included includes a list of already included files +# in order (prevention of infinite include condition) +# @throws FatalIncludeError If the function fails to include a given +# resource, or if the tree contains malformed XInclude elements. +# @throws OSError If the function fails to load a given resource. + +def _include(elem, loader=None, already_included=None): + if loader is None: + loader = default_loader + if already_included == None: + already_included = [] + # look for xinclude elements + i = 0 + while i < len(elem): + e = elem[i] + if e.tag == XINCLUDE_INCLUDE: + # process xinclude directive + href = e.get("href") + parse = e.get("parse", "xml") + if parse == "xml": + node = loader(href, parse) + if node is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + node = copy.copy(node) + if not href in already_included: + already_included.append(href) + _include(node,loader,already_included) ### newly added code here + else: + raise FatalIncludeError( + "recursive include detected: loaded %s more than once" % (href) + ) + if e.tail: + node.tail = (node.tail or "") + e.tail + elem[i] = node + elif parse == "text": + text = loader(href, parse, e.get("encoding")) + if text is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + if i: + node = elem[i-1] + node.tail = (node.tail or "") + text + (e.tail or "") + else: + elem.text = (elem.text or "") + text + (e.tail or "") + del elem[i] + continue + else: + raise FatalIncludeError( + "unknown parse type in xi:include tag (%r)" % parse + ) + elif e.tag == XINCLUDE_FALLBACK: + raise FatalIncludeError( + "xi:fallback tag must be child of xi:include (%r)" % e.tag + ) + else: + include(e, loader) + i = i + 1