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