Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 1 | # xml.etree test. This file contains enough tests to make sure that |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 2 | # all included components work as they should. For a more extensive |
| 3 | # test suite, see the selftest script in the ElementTree distribution. |
| 4 | |
Christian Heimes | 05e8be1 | 2008-02-23 18:30:17 +0000 | [diff] [blame] | 5 | import doctest |
| 6 | import sys |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 7 | |
Benjamin Peterson | ee8712c | 2008-05-20 21:35:26 +0000 | [diff] [blame] | 8 | from test import support |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 9 | |
| 10 | SAMPLE_XML = """ |
| 11 | <body> |
| 12 | <tag>text</tag> |
| 13 | <tag /> |
| 14 | <section> |
| 15 | <tag>subtext</tag> |
| 16 | </section> |
| 17 | </body> |
| 18 | """ |
| 19 | |
| 20 | SAMPLE_XML_NS = """ |
| 21 | <body xmlns="http://effbot.org/ns"> |
| 22 | <tag>text</tag> |
| 23 | <tag /> |
| 24 | <section> |
| 25 | <tag>subtext</tag> |
| 26 | </section> |
| 27 | </body> |
| 28 | """ |
| 29 | |
| 30 | def sanity(): |
| 31 | """ |
| 32 | Import sanity. |
| 33 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 34 | >>> from xml.etree import ElementTree |
| 35 | >>> from xml.etree import ElementInclude |
| 36 | >>> from xml.etree import ElementPath |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 37 | """ |
| 38 | |
| 39 | def check_method(method): |
Guido van Rossum | d59da4b | 2007-05-22 18:11:13 +0000 | [diff] [blame] | 40 | if not hasattr(method, '__call__'): |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 41 | print(method, "not callable") |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 42 | |
Guido van Rossum | 34d1928 | 2007-08-09 01:03:29 +0000 | [diff] [blame] | 43 | def serialize(ET, elem): |
| 44 | import io |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 45 | tree = ET.ElementTree(elem) |
Guido van Rossum | 34d1928 | 2007-08-09 01:03:29 +0000 | [diff] [blame] | 46 | file = io.StringIO() |
| 47 | tree.write(file) |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 48 | return file.getvalue() |
| 49 | |
| 50 | def summarize(elem): |
| 51 | return elem.tag |
| 52 | |
| 53 | def summarize_list(seq): |
Guido van Rossum | c1f779c | 2007-07-03 08:25:58 +0000 | [diff] [blame] | 54 | return list(map(summarize, seq)) |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 55 | |
| 56 | def interface(): |
| 57 | """ |
| 58 | Test element tree interface. |
| 59 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 60 | >>> from xml.etree import ElementTree as ET |
| 61 | |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 62 | >>> element = ET.Element("tag", key="value") |
| 63 | >>> tree = ET.ElementTree(element) |
| 64 | |
| 65 | Make sure all standard element methods exist. |
| 66 | |
| 67 | >>> check_method(element.append) |
| 68 | >>> check_method(element.insert) |
| 69 | >>> check_method(element.remove) |
| 70 | >>> check_method(element.getchildren) |
| 71 | >>> check_method(element.find) |
| 72 | >>> check_method(element.findall) |
| 73 | >>> check_method(element.findtext) |
| 74 | >>> check_method(element.clear) |
| 75 | >>> check_method(element.get) |
| 76 | >>> check_method(element.set) |
| 77 | >>> check_method(element.keys) |
| 78 | >>> check_method(element.items) |
| 79 | >>> check_method(element.getiterator) |
| 80 | |
| 81 | Basic method sanity checks. |
| 82 | |
| 83 | >>> serialize(ET, element) # 1 |
| 84 | '<tag key="value" />' |
| 85 | >>> subelement = ET.Element("subtag") |
| 86 | >>> element.append(subelement) |
| 87 | >>> serialize(ET, element) # 2 |
| 88 | '<tag key="value"><subtag /></tag>' |
| 89 | >>> element.insert(0, subelement) |
| 90 | >>> serialize(ET, element) # 3 |
| 91 | '<tag key="value"><subtag /><subtag /></tag>' |
| 92 | >>> element.remove(subelement) |
| 93 | >>> serialize(ET, element) # 4 |
| 94 | '<tag key="value"><subtag /></tag>' |
| 95 | >>> element.remove(subelement) |
| 96 | >>> serialize(ET, element) # 5 |
| 97 | '<tag key="value" />' |
| 98 | >>> element.remove(subelement) |
| 99 | Traceback (most recent call last): |
| 100 | ValueError: list.remove(x): x not in list |
| 101 | >>> serialize(ET, element) # 6 |
| 102 | '<tag key="value" />' |
| 103 | """ |
| 104 | |
| 105 | def find(): |
| 106 | """ |
| 107 | Test find methods (including xpath syntax). |
| 108 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 109 | >>> from xml.etree import ElementTree as ET |
| 110 | |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 111 | >>> elem = ET.XML(SAMPLE_XML) |
| 112 | >>> elem.find("tag").tag |
| 113 | 'tag' |
| 114 | >>> ET.ElementTree(elem).find("tag").tag |
| 115 | 'tag' |
| 116 | >>> elem.find("section/tag").tag |
| 117 | 'tag' |
| 118 | >>> ET.ElementTree(elem).find("section/tag").tag |
| 119 | 'tag' |
| 120 | >>> elem.findtext("tag") |
| 121 | 'text' |
| 122 | >>> elem.findtext("tog") |
| 123 | >>> elem.findtext("tog", "default") |
| 124 | 'default' |
| 125 | >>> ET.ElementTree(elem).findtext("tag") |
| 126 | 'text' |
| 127 | >>> elem.findtext("section/tag") |
| 128 | 'subtext' |
| 129 | >>> ET.ElementTree(elem).findtext("section/tag") |
| 130 | 'subtext' |
| 131 | >>> summarize_list(elem.findall("tag")) |
| 132 | ['tag', 'tag'] |
| 133 | >>> summarize_list(elem.findall("*")) |
| 134 | ['tag', 'tag', 'section'] |
| 135 | >>> summarize_list(elem.findall(".//tag")) |
| 136 | ['tag', 'tag', 'tag'] |
| 137 | >>> summarize_list(elem.findall("section/tag")) |
| 138 | ['tag'] |
| 139 | >>> summarize_list(elem.findall("section//tag")) |
| 140 | ['tag'] |
| 141 | >>> summarize_list(elem.findall("section/*")) |
| 142 | ['tag'] |
| 143 | >>> summarize_list(elem.findall("section//*")) |
| 144 | ['tag'] |
| 145 | >>> summarize_list(elem.findall("section/.//*")) |
| 146 | ['tag'] |
| 147 | >>> summarize_list(elem.findall("*/*")) |
| 148 | ['tag'] |
| 149 | >>> summarize_list(elem.findall("*//*")) |
| 150 | ['tag'] |
| 151 | >>> summarize_list(elem.findall("*/tag")) |
| 152 | ['tag'] |
| 153 | >>> summarize_list(elem.findall("*/./tag")) |
| 154 | ['tag'] |
| 155 | >>> summarize_list(elem.findall("./tag")) |
| 156 | ['tag', 'tag'] |
| 157 | >>> summarize_list(elem.findall(".//tag")) |
| 158 | ['tag', 'tag', 'tag'] |
| 159 | >>> summarize_list(elem.findall("././tag")) |
| 160 | ['tag', 'tag'] |
| 161 | >>> summarize_list(ET.ElementTree(elem).findall("/tag")) |
| 162 | ['tag', 'tag'] |
| 163 | >>> summarize_list(ET.ElementTree(elem).findall("./tag")) |
| 164 | ['tag', 'tag'] |
| 165 | >>> elem = ET.XML(SAMPLE_XML_NS) |
| 166 | >>> summarize_list(elem.findall("tag")) |
| 167 | [] |
| 168 | >>> summarize_list(elem.findall("{http://effbot.org/ns}tag")) |
| 169 | ['{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag'] |
| 170 | >>> summarize_list(elem.findall(".//{http://effbot.org/ns}tag")) |
| 171 | ['{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag', '{http://effbot.org/ns}tag'] |
| 172 | """ |
| 173 | |
| 174 | def parseliteral(): |
| 175 | r""" |
| 176 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 177 | >>> from xml.etree import ElementTree as ET |
| 178 | |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 179 | >>> element = ET.XML("<html><body>text</body></html>") |
| 180 | >>> ET.ElementTree(element).write(sys.stdout) |
| 181 | <html><body>text</body></html> |
| 182 | >>> element = ET.fromstring("<html><body>text</body></html>") |
| 183 | >>> ET.ElementTree(element).write(sys.stdout) |
| 184 | <html><body>text</body></html> |
Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 185 | >>> print(ET.tostring(element)) |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 186 | <html><body>text</body></html> |
Guido van Rossum | b62e8a8 | 2007-10-10 22:48:24 +0000 | [diff] [blame] | 187 | >>> print(repr(ET.tostring(element, "ascii"))) |
Guido van Rossum | 98297ee | 2007-11-06 21:34:58 +0000 | [diff] [blame] | 188 | b"<?xml version='1.0' encoding='ascii'?>\n<html><body>text</body></html>" |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 189 | >>> _, ids = ET.XMLID("<html><body>text</body></html>") |
| 190 | >>> len(ids) |
| 191 | 0 |
| 192 | >>> _, ids = ET.XMLID("<html><body id='body'>text</body></html>") |
| 193 | >>> len(ids) |
| 194 | 1 |
| 195 | >>> ids["body"].tag |
| 196 | 'body' |
| 197 | """ |
| 198 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 199 | |
| 200 | def check_encoding(ET, encoding): |
Fredrik Lundh | 8911ca3d | 2005-12-16 22:07:17 +0000 | [diff] [blame] | 201 | """ |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 202 | >>> from xml.etree import ElementTree as ET |
| 203 | |
| 204 | >>> check_encoding(ET, "ascii") |
| 205 | >>> check_encoding(ET, "us-ascii") |
| 206 | >>> check_encoding(ET, "iso-8859-1") |
| 207 | >>> check_encoding(ET, "iso-8859-15") |
| 208 | >>> check_encoding(ET, "cp437") |
| 209 | >>> check_encoding(ET, "mac-roman") |
Fredrik Lundh | 8911ca3d | 2005-12-16 22:07:17 +0000 | [diff] [blame] | 210 | """ |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 211 | ET.XML("<?xml version='1.0' encoding='%s'?><xml />" % encoding) |
| 212 | |
Fredrik Lundh | 8911ca3d | 2005-12-16 22:07:17 +0000 | [diff] [blame] | 213 | |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 214 | # |
| 215 | # xinclude tests (samples from appendix C of the xinclude specification) |
| 216 | |
| 217 | XINCLUDE = {} |
| 218 | |
| 219 | XINCLUDE["C1.xml"] = """\ |
| 220 | <?xml version='1.0'?> |
| 221 | <document xmlns:xi="http://www.w3.org/2001/XInclude"> |
| 222 | <p>120 Mz is adequate for an average home user.</p> |
| 223 | <xi:include href="disclaimer.xml"/> |
| 224 | </document> |
| 225 | """ |
| 226 | |
| 227 | XINCLUDE["disclaimer.xml"] = """\ |
| 228 | <?xml version='1.0'?> |
| 229 | <disclaimer> |
| 230 | <p>The opinions represented herein represent those of the individual |
| 231 | and should not be interpreted as official policy endorsed by this |
| 232 | organization.</p> |
| 233 | </disclaimer> |
| 234 | """ |
| 235 | |
| 236 | XINCLUDE["C2.xml"] = """\ |
| 237 | <?xml version='1.0'?> |
| 238 | <document xmlns:xi="http://www.w3.org/2001/XInclude"> |
| 239 | <p>This document has been accessed |
| 240 | <xi:include href="count.txt" parse="text"/> times.</p> |
| 241 | </document> |
| 242 | """ |
| 243 | |
| 244 | XINCLUDE["count.txt"] = "324387" |
| 245 | |
| 246 | XINCLUDE["C3.xml"] = """\ |
| 247 | <?xml version='1.0'?> |
| 248 | <document xmlns:xi="http://www.w3.org/2001/XInclude"> |
| 249 | <p>The following is the source of the "data.xml" resource:</p> |
| 250 | <example><xi:include href="data.xml" parse="text"/></example> |
| 251 | </document> |
| 252 | """ |
| 253 | |
| 254 | XINCLUDE["data.xml"] = """\ |
| 255 | <?xml version='1.0'?> |
| 256 | <data> |
| 257 | <item><![CDATA[Brooks & Shields]]></item> |
| 258 | </data> |
| 259 | """ |
| 260 | |
| 261 | XINCLUDE["C5.xml"] = """\ |
| 262 | <?xml version='1.0'?> |
| 263 | <div xmlns:xi="http://www.w3.org/2001/XInclude"> |
| 264 | <xi:include href="example.txt" parse="text"> |
| 265 | <xi:fallback> |
| 266 | <xi:include href="fallback-example.txt" parse="text"> |
| 267 | <xi:fallback><a href="mailto:bob@example.org">Report error</a></xi:fallback> |
| 268 | </xi:include> |
| 269 | </xi:fallback> |
| 270 | </xi:include> |
| 271 | </div> |
| 272 | """ |
| 273 | |
| 274 | XINCLUDE["default.xml"] = """\ |
| 275 | <?xml version='1.0'?> |
| 276 | <document xmlns:xi="http://www.w3.org/2001/XInclude"> |
| 277 | <p>Example.</p> |
| 278 | <xi:include href="samples/simple.xml"/> |
| 279 | </document> |
| 280 | """ |
| 281 | |
| 282 | def xinclude_loader(href, parse="xml", encoding=None): |
| 283 | try: |
| 284 | data = XINCLUDE[href] |
| 285 | except KeyError: |
| 286 | raise IOError("resource not found") |
| 287 | if parse == "xml": |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 288 | from xml.etree.ElementTree import XML |
| 289 | return XML(data) |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 290 | return data |
| 291 | |
| 292 | def xinclude(): |
| 293 | r""" |
| 294 | Basic inclusion example (XInclude C.1) |
| 295 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 296 | >>> from xml.etree import ElementTree as ET |
| 297 | >>> from xml.etree import ElementInclude |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 298 | |
| 299 | >>> document = xinclude_loader("C1.xml") |
| 300 | >>> ElementInclude.include(document, xinclude_loader) |
Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 301 | >>> print(serialize(ET, document)) # C1 |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 302 | <document> |
| 303 | <p>120 Mz is adequate for an average home user.</p> |
| 304 | <disclaimer> |
| 305 | <p>The opinions represented herein represent those of the individual |
| 306 | and should not be interpreted as official policy endorsed by this |
| 307 | organization.</p> |
| 308 | </disclaimer> |
| 309 | </document> |
| 310 | |
| 311 | Textual inclusion example (XInclude C.2) |
| 312 | |
| 313 | >>> document = xinclude_loader("C2.xml") |
| 314 | >>> ElementInclude.include(document, xinclude_loader) |
Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 315 | >>> print(serialize(ET, document)) # C2 |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 316 | <document> |
| 317 | <p>This document has been accessed |
| 318 | 324387 times.</p> |
| 319 | </document> |
| 320 | |
| 321 | Textual inclusion of XML example (XInclude C.3) |
| 322 | |
| 323 | >>> document = xinclude_loader("C3.xml") |
| 324 | >>> ElementInclude.include(document, xinclude_loader) |
Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 325 | >>> print(serialize(ET, document)) # C3 |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 326 | <document> |
| 327 | <p>The following is the source of the "data.xml" resource:</p> |
| 328 | <example><?xml version='1.0'?> |
| 329 | <data> |
| 330 | <item><![CDATA[Brooks & Shields]]></item> |
| 331 | </data> |
| 332 | </example> |
| 333 | </document> |
| 334 | |
| 335 | Fallback example (XInclude C.5) |
| 336 | Note! Fallback support is not yet implemented |
| 337 | |
| 338 | >>> document = xinclude_loader("C5.xml") |
| 339 | >>> ElementInclude.include(document, xinclude_loader) |
| 340 | Traceback (most recent call last): |
| 341 | IOError: resource not found |
| 342 | >>> # print serialize(ET, document) # C5 |
| 343 | |
| 344 | """ |
| 345 | |
| 346 | def test_main(): |
| 347 | from test import test_xml_etree |
Benjamin Peterson | ee8712c | 2008-05-20 21:35:26 +0000 | [diff] [blame] | 348 | support.run_doctest(test_xml_etree, verbosity=True) |
Armin Rigo | 9ed7306 | 2005-12-14 18:10:45 +0000 | [diff] [blame] | 349 | |
| 350 | if __name__ == '__main__': |
| 351 | test_main() |