1# regression test for SAX 2.0 2# $Id$ 3 4from xml.sax import make_parser, ContentHandler, \ 5 SAXException, SAXReaderNotAvailable, SAXParseException 6import unittest 7from unittest import mock 8try: 9 make_parser() 10except SAXReaderNotAvailable: 11 # don't try to test this module if we cannot create a parser 12 raise unittest.SkipTest("no XML parsers available") 13from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \ 14 XMLFilterBase, prepare_input_source 15from xml.sax.expatreader import create_parser 16from xml.sax.handler import feature_namespaces, feature_external_ges 17from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl 18from io import BytesIO, StringIO 19import codecs 20import os.path 21import shutil 22from urllib.error import URLError 23import urllib.request 24from test import support 25from test.support import findfile, run_unittest, FakePath, TESTFN 26 27TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") 28TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") 29try: 30 TEST_XMLFILE.encode("utf-8") 31 TEST_XMLFILE_OUT.encode("utf-8") 32except UnicodeEncodeError: 33 raise unittest.SkipTest("filename is not encodable to utf8") 34 35supports_nonascii_filenames = True 36if not os.path.supports_unicode_filenames: 37 try: 38 support.TESTFN_UNICODE.encode(support.TESTFN_ENCODING) 39 except (UnicodeError, TypeError): 40 # Either the file system encoding is None, or the file name 41 # cannot be encoded in the file system encoding. 42 supports_nonascii_filenames = False 43requires_nonascii_filenames = unittest.skipUnless( 44 supports_nonascii_filenames, 45 'Requires non-ascii filenames support') 46 47ns_uri = "http://www.python.org/xml-ns/saxtest/" 48 49class XmlTestBase(unittest.TestCase): 50 def verify_empty_attrs(self, attrs): 51 self.assertRaises(KeyError, attrs.getValue, "attr") 52 self.assertRaises(KeyError, attrs.getValueByQName, "attr") 53 self.assertRaises(KeyError, attrs.getNameByQName, "attr") 54 self.assertRaises(KeyError, attrs.getQNameByName, "attr") 55 self.assertRaises(KeyError, attrs.__getitem__, "attr") 56 self.assertEqual(attrs.getLength(), 0) 57 self.assertEqual(attrs.getNames(), []) 58 self.assertEqual(attrs.getQNames(), []) 59 self.assertEqual(len(attrs), 0) 60 self.assertNotIn("attr", attrs) 61 self.assertEqual(list(attrs.keys()), []) 62 self.assertEqual(attrs.get("attrs"), None) 63 self.assertEqual(attrs.get("attrs", 25), 25) 64 self.assertEqual(list(attrs.items()), []) 65 self.assertEqual(list(attrs.values()), []) 66 67 def verify_empty_nsattrs(self, attrs): 68 self.assertRaises(KeyError, attrs.getValue, (ns_uri, "attr")) 69 self.assertRaises(KeyError, attrs.getValueByQName, "ns:attr") 70 self.assertRaises(KeyError, attrs.getNameByQName, "ns:attr") 71 self.assertRaises(KeyError, attrs.getQNameByName, (ns_uri, "attr")) 72 self.assertRaises(KeyError, attrs.__getitem__, (ns_uri, "attr")) 73 self.assertEqual(attrs.getLength(), 0) 74 self.assertEqual(attrs.getNames(), []) 75 self.assertEqual(attrs.getQNames(), []) 76 self.assertEqual(len(attrs), 0) 77 self.assertNotIn((ns_uri, "attr"), attrs) 78 self.assertEqual(list(attrs.keys()), []) 79 self.assertEqual(attrs.get((ns_uri, "attr")), None) 80 self.assertEqual(attrs.get((ns_uri, "attr"), 25), 25) 81 self.assertEqual(list(attrs.items()), []) 82 self.assertEqual(list(attrs.values()), []) 83 84 def verify_attrs_wattr(self, attrs): 85 self.assertEqual(attrs.getLength(), 1) 86 self.assertEqual(attrs.getNames(), ["attr"]) 87 self.assertEqual(attrs.getQNames(), ["attr"]) 88 self.assertEqual(len(attrs), 1) 89 self.assertIn("attr", attrs) 90 self.assertEqual(list(attrs.keys()), ["attr"]) 91 self.assertEqual(attrs.get("attr"), "val") 92 self.assertEqual(attrs.get("attr", 25), "val") 93 self.assertEqual(list(attrs.items()), [("attr", "val")]) 94 self.assertEqual(list(attrs.values()), ["val"]) 95 self.assertEqual(attrs.getValue("attr"), "val") 96 self.assertEqual(attrs.getValueByQName("attr"), "val") 97 self.assertEqual(attrs.getNameByQName("attr"), "attr") 98 self.assertEqual(attrs["attr"], "val") 99 self.assertEqual(attrs.getQNameByName("attr"), "attr") 100 101 102def xml_str(doc, encoding=None): 103 if encoding is None: 104 return doc 105 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, doc) 106 107def xml_bytes(doc, encoding, decl_encoding=...): 108 if decl_encoding is ...: 109 decl_encoding = encoding 110 return xml_str(doc, decl_encoding).encode(encoding, 'xmlcharrefreplace') 111 112def make_xml_file(doc, encoding, decl_encoding=...): 113 if decl_encoding is ...: 114 decl_encoding = encoding 115 with open(TESTFN, 'w', encoding=encoding, errors='xmlcharrefreplace') as f: 116 f.write(xml_str(doc, decl_encoding)) 117 118 119class ParseTest(unittest.TestCase): 120 data = '<money value="$\xa3\u20ac\U0001017b">$\xa3\u20ac\U0001017b</money>' 121 122 def tearDown(self): 123 support.unlink(TESTFN) 124 125 def check_parse(self, f): 126 from xml.sax import parse 127 result = StringIO() 128 parse(f, XMLGenerator(result, 'utf-8')) 129 self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) 130 131 def test_parse_text(self): 132 encodings = ('us-ascii', 'iso-8859-1', 'utf-8', 133 'utf-16', 'utf-16le', 'utf-16be') 134 for encoding in encodings: 135 self.check_parse(StringIO(xml_str(self.data, encoding))) 136 make_xml_file(self.data, encoding) 137 with open(TESTFN, 'r', encoding=encoding) as f: 138 self.check_parse(f) 139 self.check_parse(StringIO(self.data)) 140 make_xml_file(self.data, encoding, None) 141 with open(TESTFN, 'r', encoding=encoding) as f: 142 self.check_parse(f) 143 144 def test_parse_bytes(self): 145 # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, 146 # UTF-16 is autodetected 147 encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') 148 for encoding in encodings: 149 self.check_parse(BytesIO(xml_bytes(self.data, encoding))) 150 make_xml_file(self.data, encoding) 151 self.check_parse(TESTFN) 152 with open(TESTFN, 'rb') as f: 153 self.check_parse(f) 154 self.check_parse(BytesIO(xml_bytes(self.data, encoding, None))) 155 make_xml_file(self.data, encoding, None) 156 self.check_parse(TESTFN) 157 with open(TESTFN, 'rb') as f: 158 self.check_parse(f) 159 # accept UTF-8 with BOM 160 self.check_parse(BytesIO(xml_bytes(self.data, 'utf-8-sig', 'utf-8'))) 161 make_xml_file(self.data, 'utf-8-sig', 'utf-8') 162 self.check_parse(TESTFN) 163 with open(TESTFN, 'rb') as f: 164 self.check_parse(f) 165 self.check_parse(BytesIO(xml_bytes(self.data, 'utf-8-sig', None))) 166 make_xml_file(self.data, 'utf-8-sig', None) 167 self.check_parse(TESTFN) 168 with open(TESTFN, 'rb') as f: 169 self.check_parse(f) 170 # accept data with declared encoding 171 self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1'))) 172 make_xml_file(self.data, 'iso-8859-1') 173 self.check_parse(TESTFN) 174 with open(TESTFN, 'rb') as f: 175 self.check_parse(f) 176 # fail on non-UTF-8 incompatible data without declared encoding 177 with self.assertRaises(SAXException): 178 self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1', None))) 179 make_xml_file(self.data, 'iso-8859-1', None) 180 with self.assertRaises(SAXException): 181 self.check_parse(TESTFN) 182 with open(TESTFN, 'rb') as f: 183 with self.assertRaises(SAXException): 184 self.check_parse(f) 185 186 def test_parse_path_object(self): 187 make_xml_file(self.data, 'utf-8', None) 188 self.check_parse(FakePath(TESTFN)) 189 190 def test_parse_InputSource(self): 191 # accept data without declared but with explicitly specified encoding 192 make_xml_file(self.data, 'iso-8859-1', None) 193 with open(TESTFN, 'rb') as f: 194 input = InputSource() 195 input.setByteStream(f) 196 input.setEncoding('iso-8859-1') 197 self.check_parse(input) 198 199 def test_parse_close_source(self): 200 builtin_open = open 201 fileobj = None 202 203 def mock_open(*args): 204 nonlocal fileobj 205 fileobj = builtin_open(*args) 206 return fileobj 207 208 with mock.patch('xml.sax.saxutils.open', side_effect=mock_open): 209 make_xml_file(self.data, 'iso-8859-1', None) 210 with self.assertRaises(SAXException): 211 self.check_parse(TESTFN) 212 self.assertTrue(fileobj.closed) 213 214 def check_parseString(self, s): 215 from xml.sax import parseString 216 result = StringIO() 217 parseString(s, XMLGenerator(result, 'utf-8')) 218 self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) 219 220 def test_parseString_text(self): 221 encodings = ('us-ascii', 'iso-8859-1', 'utf-8', 222 'utf-16', 'utf-16le', 'utf-16be') 223 for encoding in encodings: 224 self.check_parseString(xml_str(self.data, encoding)) 225 self.check_parseString(self.data) 226 227 def test_parseString_bytes(self): 228 # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, 229 # UTF-16 is autodetected 230 encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') 231 for encoding in encodings: 232 self.check_parseString(xml_bytes(self.data, encoding)) 233 self.check_parseString(xml_bytes(self.data, encoding, None)) 234 # accept UTF-8 with BOM 235 self.check_parseString(xml_bytes(self.data, 'utf-8-sig', 'utf-8')) 236 self.check_parseString(xml_bytes(self.data, 'utf-8-sig', None)) 237 # accept data with declared encoding 238 self.check_parseString(xml_bytes(self.data, 'iso-8859-1')) 239 # fail on non-UTF-8 incompatible data without declared encoding 240 with self.assertRaises(SAXException): 241 self.check_parseString(xml_bytes(self.data, 'iso-8859-1', None)) 242 243class MakeParserTest(unittest.TestCase): 244 def test_make_parser2(self): 245 # Creating parsers several times in a row should succeed. 246 # Testing this because there have been failures of this kind 247 # before. 248 from xml.sax import make_parser 249 p = make_parser() 250 from xml.sax import make_parser 251 p = make_parser() 252 from xml.sax import make_parser 253 p = make_parser() 254 from xml.sax import make_parser 255 p = make_parser() 256 from xml.sax import make_parser 257 p = make_parser() 258 from xml.sax import make_parser 259 p = make_parser() 260 261 def test_make_parser3(self): 262 # Testing that make_parser can handle different types of 263 # iterables. 264 make_parser(['module']) 265 make_parser(('module', )) 266 make_parser({'module'}) 267 make_parser(frozenset({'module'})) 268 make_parser({'module': None}) 269 make_parser(iter(['module'])) 270 271 def test_make_parser4(self): 272 # Testing that make_parser can handle empty iterables. 273 make_parser([]) 274 make_parser(tuple()) 275 make_parser(set()) 276 make_parser(frozenset()) 277 make_parser({}) 278 make_parser(iter([])) 279 280 def test_make_parser5(self): 281 # Testing that make_parser can handle iterables with more than 282 # one item. 283 make_parser(['module1', 'module2']) 284 make_parser(('module1', 'module2')) 285 make_parser({'module1', 'module2'}) 286 make_parser(frozenset({'module1', 'module2'})) 287 make_parser({'module1': None, 'module2': None}) 288 make_parser(iter(['module1', 'module2'])) 289 290# =========================================================================== 291# 292# saxutils tests 293# 294# =========================================================================== 295 296class SaxutilsTest(unittest.TestCase): 297 # ===== escape 298 def test_escape_basic(self): 299 self.assertEqual(escape("Donald Duck & Co"), "Donald Duck & Co") 300 301 def test_escape_all(self): 302 self.assertEqual(escape("<Donald Duck & Co>"), 303 "<Donald Duck & Co>") 304 305 def test_escape_extra(self): 306 self.assertEqual(escape("Hei på deg", {"å" : "å"}), 307 "Hei på deg") 308 309 # ===== unescape 310 def test_unescape_basic(self): 311 self.assertEqual(unescape("Donald Duck & Co"), "Donald Duck & Co") 312 313 def test_unescape_all(self): 314 self.assertEqual(unescape("<Donald Duck & Co>"), 315 "<Donald Duck & Co>") 316 317 def test_unescape_extra(self): 318 self.assertEqual(unescape("Hei på deg", {"å" : "å"}), 319 "Hei på deg") 320 321 def test_unescape_amp_extra(self): 322 self.assertEqual(unescape("&foo;", {"&foo;": "splat"}), "&foo;") 323 324 # ===== quoteattr 325 def test_quoteattr_basic(self): 326 self.assertEqual(quoteattr("Donald Duck & Co"), 327 '"Donald Duck & Co"') 328 329 def test_single_quoteattr(self): 330 self.assertEqual(quoteattr('Includes "double" quotes'), 331 '\'Includes "double" quotes\'') 332 333 def test_double_quoteattr(self): 334 self.assertEqual(quoteattr("Includes 'single' quotes"), 335 "\"Includes 'single' quotes\"") 336 337 def test_single_double_quoteattr(self): 338 self.assertEqual(quoteattr("Includes 'single' and \"double\" quotes"), 339 "\"Includes 'single' and "double" quotes\"") 340 341 # ===== make_parser 342 def test_make_parser(self): 343 # Creating a parser should succeed - it should fall back 344 # to the expatreader 345 p = make_parser(['xml.parsers.no_such_parser']) 346 347 348class PrepareInputSourceTest(unittest.TestCase): 349 350 def setUp(self): 351 self.file = support.TESTFN 352 with open(self.file, "w") as tmp: 353 tmp.write("This was read from a file.") 354 355 def tearDown(self): 356 support.unlink(self.file) 357 358 def make_byte_stream(self): 359 return BytesIO(b"This is a byte stream.") 360 361 def make_character_stream(self): 362 return StringIO("This is a character stream.") 363 364 def checkContent(self, stream, content): 365 self.assertIsNotNone(stream) 366 self.assertEqual(stream.read(), content) 367 stream.close() 368 369 370 def test_character_stream(self): 371 # If the source is an InputSource with a character stream, use it. 372 src = InputSource(self.file) 373 src.setCharacterStream(self.make_character_stream()) 374 prep = prepare_input_source(src) 375 self.assertIsNone(prep.getByteStream()) 376 self.checkContent(prep.getCharacterStream(), 377 "This is a character stream.") 378 379 def test_byte_stream(self): 380 # If the source is an InputSource that does not have a character 381 # stream but does have a byte stream, use the byte stream. 382 src = InputSource(self.file) 383 src.setByteStream(self.make_byte_stream()) 384 prep = prepare_input_source(src) 385 self.assertIsNone(prep.getCharacterStream()) 386 self.checkContent(prep.getByteStream(), 387 b"This is a byte stream.") 388 389 def test_system_id(self): 390 # If the source is an InputSource that has neither a character 391 # stream nor a byte stream, open the system ID. 392 src = InputSource(self.file) 393 prep = prepare_input_source(src) 394 self.assertIsNone(prep.getCharacterStream()) 395 self.checkContent(prep.getByteStream(), 396 b"This was read from a file.") 397 398 def test_string(self): 399 # If the source is a string, use it as a system ID and open it. 400 prep = prepare_input_source(self.file) 401 self.assertIsNone(prep.getCharacterStream()) 402 self.checkContent(prep.getByteStream(), 403 b"This was read from a file.") 404 405 def test_path_objects(self): 406 # If the source is a Path object, use it as a system ID and open it. 407 prep = prepare_input_source(FakePath(self.file)) 408 self.assertIsNone(prep.getCharacterStream()) 409 self.checkContent(prep.getByteStream(), 410 b"This was read from a file.") 411 412 def test_binary_file(self): 413 # If the source is a binary file-like object, use it as a byte 414 # stream. 415 prep = prepare_input_source(self.make_byte_stream()) 416 self.assertIsNone(prep.getCharacterStream()) 417 self.checkContent(prep.getByteStream(), 418 b"This is a byte stream.") 419 420 def test_text_file(self): 421 # If the source is a text file-like object, use it as a character 422 # stream. 423 prep = prepare_input_source(self.make_character_stream()) 424 self.assertIsNone(prep.getByteStream()) 425 self.checkContent(prep.getCharacterStream(), 426 "This is a character stream.") 427 428 429# ===== XMLGenerator 430 431class XmlgenTest: 432 def test_xmlgen_basic(self): 433 result = self.ioclass() 434 gen = XMLGenerator(result) 435 gen.startDocument() 436 gen.startElement("doc", {}) 437 gen.endElement("doc") 438 gen.endDocument() 439 440 self.assertEqual(result.getvalue(), self.xml("<doc></doc>")) 441 442 def test_xmlgen_basic_empty(self): 443 result = self.ioclass() 444 gen = XMLGenerator(result, short_empty_elements=True) 445 gen.startDocument() 446 gen.startElement("doc", {}) 447 gen.endElement("doc") 448 gen.endDocument() 449 450 self.assertEqual(result.getvalue(), self.xml("<doc/>")) 451 452 def test_xmlgen_content(self): 453 result = self.ioclass() 454 gen = XMLGenerator(result) 455 456 gen.startDocument() 457 gen.startElement("doc", {}) 458 gen.characters("huhei") 459 gen.endElement("doc") 460 gen.endDocument() 461 462 self.assertEqual(result.getvalue(), self.xml("<doc>huhei</doc>")) 463 464 def test_xmlgen_content_empty(self): 465 result = self.ioclass() 466 gen = XMLGenerator(result, short_empty_elements=True) 467 468 gen.startDocument() 469 gen.startElement("doc", {}) 470 gen.characters("huhei") 471 gen.endElement("doc") 472 gen.endDocument() 473 474 self.assertEqual(result.getvalue(), self.xml("<doc>huhei</doc>")) 475 476 def test_xmlgen_pi(self): 477 result = self.ioclass() 478 gen = XMLGenerator(result) 479 480 gen.startDocument() 481 gen.processingInstruction("test", "data") 482 gen.startElement("doc", {}) 483 gen.endElement("doc") 484 gen.endDocument() 485 486 self.assertEqual(result.getvalue(), 487 self.xml("<?test data?><doc></doc>")) 488 489 def test_xmlgen_content_escape(self): 490 result = self.ioclass() 491 gen = XMLGenerator(result) 492 493 gen.startDocument() 494 gen.startElement("doc", {}) 495 gen.characters("<huhei&") 496 gen.endElement("doc") 497 gen.endDocument() 498 499 self.assertEqual(result.getvalue(), 500 self.xml("<doc><huhei&</doc>")) 501 502 def test_xmlgen_attr_escape(self): 503 result = self.ioclass() 504 gen = XMLGenerator(result) 505 506 gen.startDocument() 507 gen.startElement("doc", {"a": '"'}) 508 gen.startElement("e", {"a": "'"}) 509 gen.endElement("e") 510 gen.startElement("e", {"a": "'\""}) 511 gen.endElement("e") 512 gen.startElement("e", {"a": "\n\r\t"}) 513 gen.endElement("e") 514 gen.endElement("doc") 515 gen.endDocument() 516 517 self.assertEqual(result.getvalue(), self.xml( 518 "<doc a='\"'><e a=\"'\"></e>" 519 "<e a=\"'"\"></e>" 520 "<e a=\" 	\"></e></doc>")) 521 522 def test_xmlgen_encoding(self): 523 encodings = ('iso-8859-15', 'utf-8', 'utf-8-sig', 524 'utf-16', 'utf-16be', 'utf-16le', 525 'utf-32', 'utf-32be', 'utf-32le') 526 for encoding in encodings: 527 result = self.ioclass() 528 gen = XMLGenerator(result, encoding=encoding) 529 530 gen.startDocument() 531 gen.startElement("doc", {"a": '\u20ac'}) 532 gen.characters("\u20ac") 533 gen.endElement("doc") 534 gen.endDocument() 535 536 self.assertEqual(result.getvalue(), 537 self.xml('<doc a="\u20ac">\u20ac</doc>', encoding=encoding)) 538 539 def test_xmlgen_unencodable(self): 540 result = self.ioclass() 541 gen = XMLGenerator(result, encoding='ascii') 542 543 gen.startDocument() 544 gen.startElement("doc", {"a": '\u20ac'}) 545 gen.characters("\u20ac") 546 gen.endElement("doc") 547 gen.endDocument() 548 549 self.assertEqual(result.getvalue(), 550 self.xml('<doc a="€">€</doc>', encoding='ascii')) 551 552 def test_xmlgen_ignorable(self): 553 result = self.ioclass() 554 gen = XMLGenerator(result) 555 556 gen.startDocument() 557 gen.startElement("doc", {}) 558 gen.ignorableWhitespace(" ") 559 gen.endElement("doc") 560 gen.endDocument() 561 562 self.assertEqual(result.getvalue(), self.xml("<doc> </doc>")) 563 564 def test_xmlgen_ignorable_empty(self): 565 result = self.ioclass() 566 gen = XMLGenerator(result, short_empty_elements=True) 567 568 gen.startDocument() 569 gen.startElement("doc", {}) 570 gen.ignorableWhitespace(" ") 571 gen.endElement("doc") 572 gen.endDocument() 573 574 self.assertEqual(result.getvalue(), self.xml("<doc> </doc>")) 575 576 def test_xmlgen_encoding_bytes(self): 577 encodings = ('iso-8859-15', 'utf-8', 'utf-8-sig', 578 'utf-16', 'utf-16be', 'utf-16le', 579 'utf-32', 'utf-32be', 'utf-32le') 580 for encoding in encodings: 581 result = self.ioclass() 582 gen = XMLGenerator(result, encoding=encoding) 583 584 gen.startDocument() 585 gen.startElement("doc", {"a": '\u20ac'}) 586 gen.characters("\u20ac".encode(encoding)) 587 gen.ignorableWhitespace(" ".encode(encoding)) 588 gen.endElement("doc") 589 gen.endDocument() 590 591 self.assertEqual(result.getvalue(), 592 self.xml('<doc a="\u20ac">\u20ac </doc>', encoding=encoding)) 593 594 def test_xmlgen_ns(self): 595 result = self.ioclass() 596 gen = XMLGenerator(result) 597 598 gen.startDocument() 599 gen.startPrefixMapping("ns1", ns_uri) 600 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) 601 # add an unqualified name 602 gen.startElementNS((None, "udoc"), None, {}) 603 gen.endElementNS((None, "udoc"), None) 604 gen.endElementNS((ns_uri, "doc"), "ns1:doc") 605 gen.endPrefixMapping("ns1") 606 gen.endDocument() 607 608 self.assertEqual(result.getvalue(), self.xml( 609 '<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % 610 ns_uri)) 611 612 def test_xmlgen_ns_empty(self): 613 result = self.ioclass() 614 gen = XMLGenerator(result, short_empty_elements=True) 615 616 gen.startDocument() 617 gen.startPrefixMapping("ns1", ns_uri) 618 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) 619 # add an unqualified name 620 gen.startElementNS((None, "udoc"), None, {}) 621 gen.endElementNS((None, "udoc"), None) 622 gen.endElementNS((ns_uri, "doc"), "ns1:doc") 623 gen.endPrefixMapping("ns1") 624 gen.endDocument() 625 626 self.assertEqual(result.getvalue(), self.xml( 627 '<ns1:doc xmlns:ns1="%s"><udoc/></ns1:doc>' % 628 ns_uri)) 629 630 def test_1463026_1(self): 631 result = self.ioclass() 632 gen = XMLGenerator(result) 633 634 gen.startDocument() 635 gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) 636 gen.endElementNS((None, 'a'), 'a') 637 gen.endDocument() 638 639 self.assertEqual(result.getvalue(), self.xml('<a b="c"></a>')) 640 641 def test_1463026_1_empty(self): 642 result = self.ioclass() 643 gen = XMLGenerator(result, short_empty_elements=True) 644 645 gen.startDocument() 646 gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) 647 gen.endElementNS((None, 'a'), 'a') 648 gen.endDocument() 649 650 self.assertEqual(result.getvalue(), self.xml('<a b="c"/>')) 651 652 def test_1463026_2(self): 653 result = self.ioclass() 654 gen = XMLGenerator(result) 655 656 gen.startDocument() 657 gen.startPrefixMapping(None, 'qux') 658 gen.startElementNS(('qux', 'a'), 'a', {}) 659 gen.endElementNS(('qux', 'a'), 'a') 660 gen.endPrefixMapping(None) 661 gen.endDocument() 662 663 self.assertEqual(result.getvalue(), self.xml('<a xmlns="qux"></a>')) 664 665 def test_1463026_2_empty(self): 666 result = self.ioclass() 667 gen = XMLGenerator(result, short_empty_elements=True) 668 669 gen.startDocument() 670 gen.startPrefixMapping(None, 'qux') 671 gen.startElementNS(('qux', 'a'), 'a', {}) 672 gen.endElementNS(('qux', 'a'), 'a') 673 gen.endPrefixMapping(None) 674 gen.endDocument() 675 676 self.assertEqual(result.getvalue(), self.xml('<a xmlns="qux"/>')) 677 678 def test_1463026_3(self): 679 result = self.ioclass() 680 gen = XMLGenerator(result) 681 682 gen.startDocument() 683 gen.startPrefixMapping('my', 'qux') 684 gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) 685 gen.endElementNS(('qux', 'a'), 'a') 686 gen.endPrefixMapping('my') 687 gen.endDocument() 688 689 self.assertEqual(result.getvalue(), 690 self.xml('<my:a xmlns:my="qux" b="c"></my:a>')) 691 692 def test_1463026_3_empty(self): 693 result = self.ioclass() 694 gen = XMLGenerator(result, short_empty_elements=True) 695 696 gen.startDocument() 697 gen.startPrefixMapping('my', 'qux') 698 gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) 699 gen.endElementNS(('qux', 'a'), 'a') 700 gen.endPrefixMapping('my') 701 gen.endDocument() 702 703 self.assertEqual(result.getvalue(), 704 self.xml('<my:a xmlns:my="qux" b="c"/>')) 705 706 def test_5027_1(self): 707 # The xml prefix (as in xml:lang below) is reserved and bound by 708 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had 709 # a bug whereby a KeyError is raised because this namespace is missing 710 # from a dictionary. 711 # 712 # This test demonstrates the bug by parsing a document. 713 test_xml = StringIO( 714 '<?xml version="1.0"?>' 715 '<a:g1 xmlns:a="http://example.com/ns">' 716 '<a:g2 xml:lang="en">Hello</a:g2>' 717 '</a:g1>') 718 719 parser = make_parser() 720 parser.setFeature(feature_namespaces, True) 721 result = self.ioclass() 722 gen = XMLGenerator(result) 723 parser.setContentHandler(gen) 724 parser.parse(test_xml) 725 726 self.assertEqual(result.getvalue(), 727 self.xml( 728 '<a:g1 xmlns:a="http://example.com/ns">' 729 '<a:g2 xml:lang="en">Hello</a:g2>' 730 '</a:g1>')) 731 732 def test_5027_2(self): 733 # The xml prefix (as in xml:lang below) is reserved and bound by 734 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had 735 # a bug whereby a KeyError is raised because this namespace is missing 736 # from a dictionary. 737 # 738 # This test demonstrates the bug by direct manipulation of the 739 # XMLGenerator. 740 result = self.ioclass() 741 gen = XMLGenerator(result) 742 743 gen.startDocument() 744 gen.startPrefixMapping('a', 'http://example.com/ns') 745 gen.startElementNS(('http://example.com/ns', 'g1'), 'g1', {}) 746 lang_attr = {('http://www.w3.org/XML/1998/namespace', 'lang'): 'en'} 747 gen.startElementNS(('http://example.com/ns', 'g2'), 'g2', lang_attr) 748 gen.characters('Hello') 749 gen.endElementNS(('http://example.com/ns', 'g2'), 'g2') 750 gen.endElementNS(('http://example.com/ns', 'g1'), 'g1') 751 gen.endPrefixMapping('a') 752 gen.endDocument() 753 754 self.assertEqual(result.getvalue(), 755 self.xml( 756 '<a:g1 xmlns:a="http://example.com/ns">' 757 '<a:g2 xml:lang="en">Hello</a:g2>' 758 '</a:g1>')) 759 760 def test_no_close_file(self): 761 result = self.ioclass() 762 def func(out): 763 gen = XMLGenerator(out) 764 gen.startDocument() 765 gen.startElement("doc", {}) 766 func(result) 767 self.assertFalse(result.closed) 768 769 def test_xmlgen_fragment(self): 770 result = self.ioclass() 771 gen = XMLGenerator(result) 772 773 # Don't call gen.startDocument() 774 gen.startElement("foo", {"a": "1.0"}) 775 gen.characters("Hello") 776 gen.endElement("foo") 777 gen.startElement("bar", {"b": "2.0"}) 778 gen.endElement("bar") 779 # Don't call gen.endDocument() 780 781 self.assertEqual(result.getvalue(), 782 self.xml('<foo a="1.0">Hello</foo><bar b="2.0"></bar>')[len(self.xml('')):]) 783 784class StringXmlgenTest(XmlgenTest, unittest.TestCase): 785 ioclass = StringIO 786 787 def xml(self, doc, encoding='iso-8859-1'): 788 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, doc) 789 790 test_xmlgen_unencodable = None 791 792class BytesXmlgenTest(XmlgenTest, unittest.TestCase): 793 ioclass = BytesIO 794 795 def xml(self, doc, encoding='iso-8859-1'): 796 return ('<?xml version="1.0" encoding="%s"?>\n%s' % 797 (encoding, doc)).encode(encoding, 'xmlcharrefreplace') 798 799class WriterXmlgenTest(BytesXmlgenTest): 800 class ioclass(list): 801 write = list.append 802 closed = False 803 804 def seekable(self): 805 return True 806 807 def tell(self): 808 # return 0 at start and not 0 after start 809 return len(self) 810 811 def getvalue(self): 812 return b''.join(self) 813 814class StreamWriterXmlgenTest(XmlgenTest, unittest.TestCase): 815 def ioclass(self): 816 raw = BytesIO() 817 writer = codecs.getwriter('ascii')(raw, 'xmlcharrefreplace') 818 writer.getvalue = raw.getvalue 819 return writer 820 821 def xml(self, doc, encoding='iso-8859-1'): 822 return ('<?xml version="1.0" encoding="%s"?>\n%s' % 823 (encoding, doc)).encode('ascii', 'xmlcharrefreplace') 824 825class StreamReaderWriterXmlgenTest(XmlgenTest, unittest.TestCase): 826 fname = support.TESTFN + '-codecs' 827 828 def ioclass(self): 829 writer = codecs.open(self.fname, 'w', encoding='ascii', 830 errors='xmlcharrefreplace', buffering=0) 831 def cleanup(): 832 writer.close() 833 support.unlink(self.fname) 834 self.addCleanup(cleanup) 835 def getvalue(): 836 # Windows will not let use reopen without first closing 837 writer.close() 838 with open(writer.name, 'rb') as f: 839 return f.read() 840 writer.getvalue = getvalue 841 return writer 842 843 def xml(self, doc, encoding='iso-8859-1'): 844 return ('<?xml version="1.0" encoding="%s"?>\n%s' % 845 (encoding, doc)).encode('ascii', 'xmlcharrefreplace') 846 847start = b'<?xml version="1.0" encoding="iso-8859-1"?>\n' 848 849 850class XMLFilterBaseTest(unittest.TestCase): 851 def test_filter_basic(self): 852 result = BytesIO() 853 gen = XMLGenerator(result) 854 filter = XMLFilterBase() 855 filter.setContentHandler(gen) 856 857 filter.startDocument() 858 filter.startElement("doc", {}) 859 filter.characters("content") 860 filter.ignorableWhitespace(" ") 861 filter.endElement("doc") 862 filter.endDocument() 863 864 self.assertEqual(result.getvalue(), start + b"<doc>content </doc>") 865 866# =========================================================================== 867# 868# expatreader tests 869# 870# =========================================================================== 871 872with open(TEST_XMLFILE_OUT, 'rb') as f: 873 xml_test_out = f.read() 874 875class ExpatReaderTest(XmlTestBase): 876 877 # ===== XMLReader support 878 879 def test_expat_binary_file(self): 880 parser = create_parser() 881 result = BytesIO() 882 xmlgen = XMLGenerator(result) 883 884 parser.setContentHandler(xmlgen) 885 with open(TEST_XMLFILE, 'rb') as f: 886 parser.parse(f) 887 888 self.assertEqual(result.getvalue(), xml_test_out) 889 890 def test_expat_text_file(self): 891 parser = create_parser() 892 result = BytesIO() 893 xmlgen = XMLGenerator(result) 894 895 parser.setContentHandler(xmlgen) 896 with open(TEST_XMLFILE, 'rt', encoding='iso-8859-1') as f: 897 parser.parse(f) 898 899 self.assertEqual(result.getvalue(), xml_test_out) 900 901 @requires_nonascii_filenames 902 def test_expat_binary_file_nonascii(self): 903 fname = support.TESTFN_UNICODE 904 shutil.copyfile(TEST_XMLFILE, fname) 905 self.addCleanup(support.unlink, fname) 906 907 parser = create_parser() 908 result = BytesIO() 909 xmlgen = XMLGenerator(result) 910 911 parser.setContentHandler(xmlgen) 912 parser.parse(open(fname, 'rb')) 913 914 self.assertEqual(result.getvalue(), xml_test_out) 915 916 def test_expat_binary_file_bytes_name(self): 917 fname = os.fsencode(TEST_XMLFILE) 918 parser = create_parser() 919 result = BytesIO() 920 xmlgen = XMLGenerator(result) 921 922 parser.setContentHandler(xmlgen) 923 with open(fname, 'rb') as f: 924 parser.parse(f) 925 926 self.assertEqual(result.getvalue(), xml_test_out) 927 928 def test_expat_binary_file_int_name(self): 929 parser = create_parser() 930 result = BytesIO() 931 xmlgen = XMLGenerator(result) 932 933 parser.setContentHandler(xmlgen) 934 with open(TEST_XMLFILE, 'rb') as f: 935 with open(f.fileno(), 'rb', closefd=False) as f2: 936 parser.parse(f2) 937 938 self.assertEqual(result.getvalue(), xml_test_out) 939 940 # ===== DTDHandler support 941 942 class TestDTDHandler: 943 944 def __init__(self): 945 self._notations = [] 946 self._entities = [] 947 948 def notationDecl(self, name, publicId, systemId): 949 self._notations.append((name, publicId, systemId)) 950 951 def unparsedEntityDecl(self, name, publicId, systemId, ndata): 952 self._entities.append((name, publicId, systemId, ndata)) 953 954 955 class TestEntityRecorder: 956 def __init__(self): 957 self.entities = [] 958 959 def resolveEntity(self, publicId, systemId): 960 self.entities.append((publicId, systemId)) 961 source = InputSource() 962 source.setPublicId(publicId) 963 source.setSystemId(systemId) 964 return source 965 966 def test_expat_dtdhandler(self): 967 parser = create_parser() 968 handler = self.TestDTDHandler() 969 parser.setDTDHandler(handler) 970 971 parser.feed('<!DOCTYPE doc [\n') 972 parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n') 973 parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n') 974 parser.feed(']>\n') 975 parser.feed('<doc></doc>') 976 parser.close() 977 978 self.assertEqual(handler._notations, 979 [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)]) 980 self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")]) 981 982 def test_expat_external_dtd_enabled(self): 983 # clear _opener global variable 984 self.addCleanup(urllib.request.urlcleanup) 985 986 parser = create_parser() 987 parser.setFeature(feature_external_ges, True) 988 resolver = self.TestEntityRecorder() 989 parser.setEntityResolver(resolver) 990 991 with self.assertRaises(URLError): 992 parser.feed( 993 '<!DOCTYPE external SYSTEM "unsupported://non-existing">\n' 994 ) 995 self.assertEqual( 996 resolver.entities, [(None, 'unsupported://non-existing')] 997 ) 998 999 def test_expat_external_dtd_default(self): 1000 parser = create_parser() 1001 resolver = self.TestEntityRecorder() 1002 parser.setEntityResolver(resolver) 1003 1004 parser.feed( 1005 '<!DOCTYPE external SYSTEM "unsupported://non-existing">\n' 1006 ) 1007 parser.feed('<doc />') 1008 parser.close() 1009 self.assertEqual(resolver.entities, []) 1010 1011 # ===== EntityResolver support 1012 1013 class TestEntityResolver: 1014 1015 def resolveEntity(self, publicId, systemId): 1016 inpsrc = InputSource() 1017 inpsrc.setByteStream(BytesIO(b"<entity/>")) 1018 return inpsrc 1019 1020 def test_expat_entityresolver_enabled(self): 1021 parser = create_parser() 1022 parser.setFeature(feature_external_ges, True) 1023 parser.setEntityResolver(self.TestEntityResolver()) 1024 result = BytesIO() 1025 parser.setContentHandler(XMLGenerator(result)) 1026 1027 parser.feed('<!DOCTYPE doc [\n') 1028 parser.feed(' <!ENTITY test SYSTEM "whatever">\n') 1029 parser.feed(']>\n') 1030 parser.feed('<doc>&test;</doc>') 1031 parser.close() 1032 1033 self.assertEqual(result.getvalue(), start + 1034 b"<doc><entity></entity></doc>") 1035 1036 def test_expat_entityresolver_default(self): 1037 parser = create_parser() 1038 self.assertEqual(parser.getFeature(feature_external_ges), False) 1039 parser.setEntityResolver(self.TestEntityResolver()) 1040 result = BytesIO() 1041 parser.setContentHandler(XMLGenerator(result)) 1042 1043 parser.feed('<!DOCTYPE doc [\n') 1044 parser.feed(' <!ENTITY test SYSTEM "whatever">\n') 1045 parser.feed(']>\n') 1046 parser.feed('<doc>&test;</doc>') 1047 parser.close() 1048 1049 self.assertEqual(result.getvalue(), start + 1050 b"<doc></doc>") 1051 1052 # ===== Attributes support 1053 1054 class AttrGatherer(ContentHandler): 1055 1056 def startElement(self, name, attrs): 1057 self._attrs = attrs 1058 1059 def startElementNS(self, name, qname, attrs): 1060 self._attrs = attrs 1061 1062 def test_expat_attrs_empty(self): 1063 parser = create_parser() 1064 gather = self.AttrGatherer() 1065 parser.setContentHandler(gather) 1066 1067 parser.feed("<doc/>") 1068 parser.close() 1069 1070 self.verify_empty_attrs(gather._attrs) 1071 1072 def test_expat_attrs_wattr(self): 1073 parser = create_parser() 1074 gather = self.AttrGatherer() 1075 parser.setContentHandler(gather) 1076 1077 parser.feed("<doc attr='val'/>") 1078 parser.close() 1079 1080 self.verify_attrs_wattr(gather._attrs) 1081 1082 def test_expat_nsattrs_empty(self): 1083 parser = create_parser(1) 1084 gather = self.AttrGatherer() 1085 parser.setContentHandler(gather) 1086 1087 parser.feed("<doc/>") 1088 parser.close() 1089 1090 self.verify_empty_nsattrs(gather._attrs) 1091 1092 def test_expat_nsattrs_wattr(self): 1093 parser = create_parser(1) 1094 gather = self.AttrGatherer() 1095 parser.setContentHandler(gather) 1096 1097 parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri) 1098 parser.close() 1099 1100 attrs = gather._attrs 1101 1102 self.assertEqual(attrs.getLength(), 1) 1103 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) 1104 self.assertTrue((attrs.getQNames() == [] or 1105 attrs.getQNames() == ["ns:attr"])) 1106 self.assertEqual(len(attrs), 1) 1107 self.assertIn((ns_uri, "attr"), attrs) 1108 self.assertEqual(attrs.get((ns_uri, "attr")), "val") 1109 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") 1110 self.assertEqual(list(attrs.items()), [((ns_uri, "attr"), "val")]) 1111 self.assertEqual(list(attrs.values()), ["val"]) 1112 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") 1113 self.assertEqual(attrs[(ns_uri, "attr")], "val") 1114 1115 # ===== InputSource support 1116 1117 def test_expat_inpsource_filename(self): 1118 parser = create_parser() 1119 result = BytesIO() 1120 xmlgen = XMLGenerator(result) 1121 1122 parser.setContentHandler(xmlgen) 1123 parser.parse(TEST_XMLFILE) 1124 1125 self.assertEqual(result.getvalue(), xml_test_out) 1126 1127 def test_expat_inpsource_sysid(self): 1128 parser = create_parser() 1129 result = BytesIO() 1130 xmlgen = XMLGenerator(result) 1131 1132 parser.setContentHandler(xmlgen) 1133 parser.parse(InputSource(TEST_XMLFILE)) 1134 1135 self.assertEqual(result.getvalue(), xml_test_out) 1136 1137 @requires_nonascii_filenames 1138 def test_expat_inpsource_sysid_nonascii(self): 1139 fname = support.TESTFN_UNICODE 1140 shutil.copyfile(TEST_XMLFILE, fname) 1141 self.addCleanup(support.unlink, fname) 1142 1143 parser = create_parser() 1144 result = BytesIO() 1145 xmlgen = XMLGenerator(result) 1146 1147 parser.setContentHandler(xmlgen) 1148 parser.parse(InputSource(fname)) 1149 1150 self.assertEqual(result.getvalue(), xml_test_out) 1151 1152 def test_expat_inpsource_byte_stream(self): 1153 parser = create_parser() 1154 result = BytesIO() 1155 xmlgen = XMLGenerator(result) 1156 1157 parser.setContentHandler(xmlgen) 1158 inpsrc = InputSource() 1159 with open(TEST_XMLFILE, 'rb') as f: 1160 inpsrc.setByteStream(f) 1161 parser.parse(inpsrc) 1162 1163 self.assertEqual(result.getvalue(), xml_test_out) 1164 1165 def test_expat_inpsource_character_stream(self): 1166 parser = create_parser() 1167 result = BytesIO() 1168 xmlgen = XMLGenerator(result) 1169 1170 parser.setContentHandler(xmlgen) 1171 inpsrc = InputSource() 1172 with open(TEST_XMLFILE, 'rt', encoding='iso-8859-1') as f: 1173 inpsrc.setCharacterStream(f) 1174 parser.parse(inpsrc) 1175 1176 self.assertEqual(result.getvalue(), xml_test_out) 1177 1178 # ===== IncrementalParser support 1179 1180 def test_expat_incremental(self): 1181 result = BytesIO() 1182 xmlgen = XMLGenerator(result) 1183 parser = create_parser() 1184 parser.setContentHandler(xmlgen) 1185 1186 parser.feed("<doc>") 1187 parser.feed("</doc>") 1188 parser.close() 1189 1190 self.assertEqual(result.getvalue(), start + b"<doc></doc>") 1191 1192 def test_expat_incremental_reset(self): 1193 result = BytesIO() 1194 xmlgen = XMLGenerator(result) 1195 parser = create_parser() 1196 parser.setContentHandler(xmlgen) 1197 1198 parser.feed("<doc>") 1199 parser.feed("text") 1200 1201 result = BytesIO() 1202 xmlgen = XMLGenerator(result) 1203 parser.setContentHandler(xmlgen) 1204 parser.reset() 1205 1206 parser.feed("<doc>") 1207 parser.feed("text") 1208 parser.feed("</doc>") 1209 parser.close() 1210 1211 self.assertEqual(result.getvalue(), start + b"<doc>text</doc>") 1212 1213 # ===== Locator support 1214 1215 def test_expat_locator_noinfo(self): 1216 result = BytesIO() 1217 xmlgen = XMLGenerator(result) 1218 parser = create_parser() 1219 parser.setContentHandler(xmlgen) 1220 1221 parser.feed("<doc>") 1222 parser.feed("</doc>") 1223 parser.close() 1224 1225 self.assertEqual(parser.getSystemId(), None) 1226 self.assertEqual(parser.getPublicId(), None) 1227 self.assertEqual(parser.getLineNumber(), 1) 1228 1229 def test_expat_locator_withinfo(self): 1230 result = BytesIO() 1231 xmlgen = XMLGenerator(result) 1232 parser = create_parser() 1233 parser.setContentHandler(xmlgen) 1234 parser.parse(TEST_XMLFILE) 1235 1236 self.assertEqual(parser.getSystemId(), TEST_XMLFILE) 1237 self.assertEqual(parser.getPublicId(), None) 1238 1239 @requires_nonascii_filenames 1240 def test_expat_locator_withinfo_nonascii(self): 1241 fname = support.TESTFN_UNICODE 1242 shutil.copyfile(TEST_XMLFILE, fname) 1243 self.addCleanup(support.unlink, fname) 1244 1245 result = BytesIO() 1246 xmlgen = XMLGenerator(result) 1247 parser = create_parser() 1248 parser.setContentHandler(xmlgen) 1249 parser.parse(fname) 1250 1251 self.assertEqual(parser.getSystemId(), fname) 1252 self.assertEqual(parser.getPublicId(), None) 1253 1254 1255# =========================================================================== 1256# 1257# error reporting 1258# 1259# =========================================================================== 1260 1261class ErrorReportingTest(unittest.TestCase): 1262 def test_expat_inpsource_location(self): 1263 parser = create_parser() 1264 parser.setContentHandler(ContentHandler()) # do nothing 1265 source = InputSource() 1266 source.setByteStream(BytesIO(b"<foo bar foobar>")) #ill-formed 1267 name = "a file name" 1268 source.setSystemId(name) 1269 try: 1270 parser.parse(source) 1271 self.fail() 1272 except SAXException as e: 1273 self.assertEqual(e.getSystemId(), name) 1274 1275 def test_expat_incomplete(self): 1276 parser = create_parser() 1277 parser.setContentHandler(ContentHandler()) # do nothing 1278 self.assertRaises(SAXParseException, parser.parse, StringIO("<foo>")) 1279 self.assertEqual(parser.getColumnNumber(), 5) 1280 self.assertEqual(parser.getLineNumber(), 1) 1281 1282 def test_sax_parse_exception_str(self): 1283 # pass various values from a locator to the SAXParseException to 1284 # make sure that the __str__() doesn't fall apart when None is 1285 # passed instead of an integer line and column number 1286 # 1287 # use "normal" values for the locator: 1288 str(SAXParseException("message", None, 1289 self.DummyLocator(1, 1))) 1290 # use None for the line number: 1291 str(SAXParseException("message", None, 1292 self.DummyLocator(None, 1))) 1293 # use None for the column number: 1294 str(SAXParseException("message", None, 1295 self.DummyLocator(1, None))) 1296 # use None for both: 1297 str(SAXParseException("message", None, 1298 self.DummyLocator(None, None))) 1299 1300 class DummyLocator: 1301 def __init__(self, lineno, colno): 1302 self._lineno = lineno 1303 self._colno = colno 1304 1305 def getPublicId(self): 1306 return "pubid" 1307 1308 def getSystemId(self): 1309 return "sysid" 1310 1311 def getLineNumber(self): 1312 return self._lineno 1313 1314 def getColumnNumber(self): 1315 return self._colno 1316 1317# =========================================================================== 1318# 1319# xmlreader tests 1320# 1321# =========================================================================== 1322 1323class XmlReaderTest(XmlTestBase): 1324 1325 # ===== AttributesImpl 1326 def test_attrs_empty(self): 1327 self.verify_empty_attrs(AttributesImpl({})) 1328 1329 def test_attrs_wattr(self): 1330 self.verify_attrs_wattr(AttributesImpl({"attr" : "val"})) 1331 1332 def test_nsattrs_empty(self): 1333 self.verify_empty_nsattrs(AttributesNSImpl({}, {})) 1334 1335 def test_nsattrs_wattr(self): 1336 attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, 1337 {(ns_uri, "attr") : "ns:attr"}) 1338 1339 self.assertEqual(attrs.getLength(), 1) 1340 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) 1341 self.assertEqual(attrs.getQNames(), ["ns:attr"]) 1342 self.assertEqual(len(attrs), 1) 1343 self.assertIn((ns_uri, "attr"), attrs) 1344 self.assertEqual(list(attrs.keys()), [(ns_uri, "attr")]) 1345 self.assertEqual(attrs.get((ns_uri, "attr")), "val") 1346 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") 1347 self.assertEqual(list(attrs.items()), [((ns_uri, "attr"), "val")]) 1348 self.assertEqual(list(attrs.values()), ["val"]) 1349 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") 1350 self.assertEqual(attrs.getValueByQName("ns:attr"), "val") 1351 self.assertEqual(attrs.getNameByQName("ns:attr"), (ns_uri, "attr")) 1352 self.assertEqual(attrs[(ns_uri, "attr")], "val") 1353 self.assertEqual(attrs.getQNameByName((ns_uri, "attr")), "ns:attr") 1354 1355 1356def test_main(): 1357 run_unittest(MakeParserTest, 1358 ParseTest, 1359 SaxutilsTest, 1360 PrepareInputSourceTest, 1361 StringXmlgenTest, 1362 BytesXmlgenTest, 1363 WriterXmlgenTest, 1364 StreamWriterXmlgenTest, 1365 StreamReaderWriterXmlgenTest, 1366 ExpatReaderTest, 1367 ErrorReportingTest, 1368 XmlReaderTest) 1369 1370if __name__ == "__main__": 1371 test_main() 1372