1 # regression test for SAX 2.0 -*- coding: utf-8 -*- 2 # $Id$ 3 4 from xml.sax import make_parser, ContentHandler, \ 5 SAXException, SAXReaderNotAvailable, SAXParseException 6 try: 7 make_parser() 8 except SAXReaderNotAvailable: 9 # don't try to test this module if we cannot create a parser 10 raise ImportError("no XML parsers available") 11 from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \ 12 XMLFilterBase 13 from xml.sax.expatreader import create_parser 14 from xml.sax.handler import feature_namespaces 15 from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl 16 from cStringIO import StringIO 17 from test.test_support import findfile, run_unittest 18 import unittest 19 20 TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") 21 TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") 22 23 ns_uri = "http://www.python.org/xml-ns/saxtest/" 24 25 class XmlTestBase(unittest.TestCase): 26 def verify_empty_attrs(self, attrs): 27 self.assertRaises(KeyError, attrs.getValue, "attr") 28 self.assertRaises(KeyError, attrs.getValueByQName, "attr") 29 self.assertRaises(KeyError, attrs.getNameByQName, "attr") 30 self.assertRaises(KeyError, attrs.getQNameByName, "attr") 31 self.assertRaises(KeyError, attrs.__getitem__, "attr") 32 self.assertEqual(attrs.getLength(), 0) 33 self.assertEqual(attrs.getNames(), []) 34 self.assertEqual(attrs.getQNames(), []) 35 self.assertEqual(len(attrs), 0) 36 self.assertFalse(attrs.has_key("attr")) 37 self.assertEqual(attrs.keys(), []) 38 self.assertEqual(attrs.get("attrs"), None) 39 self.assertEqual(attrs.get("attrs", 25), 25) 40 self.assertEqual(attrs.items(), []) 41 self.assertEqual(attrs.values(), []) 42 43 def verify_empty_nsattrs(self, attrs): 44 self.assertRaises(KeyError, attrs.getValue, (ns_uri, "attr")) 45 self.assertRaises(KeyError, attrs.getValueByQName, "ns:attr") 46 self.assertRaises(KeyError, attrs.getNameByQName, "ns:attr") 47 self.assertRaises(KeyError, attrs.getQNameByName, (ns_uri, "attr")) 48 self.assertRaises(KeyError, attrs.__getitem__, (ns_uri, "attr")) 49 self.assertEqual(attrs.getLength(), 0) 50 self.assertEqual(attrs.getNames(), []) 51 self.assertEqual(attrs.getQNames(), []) 52 self.assertEqual(len(attrs), 0) 53 self.assertFalse(attrs.has_key((ns_uri, "attr"))) 54 self.assertEqual(attrs.keys(), []) 55 self.assertEqual(attrs.get((ns_uri, "attr")), None) 56 self.assertEqual(attrs.get((ns_uri, "attr"), 25), 25) 57 self.assertEqual(attrs.items(), []) 58 self.assertEqual(attrs.values(), []) 59 60 def verify_attrs_wattr(self, attrs): 61 self.assertEqual(attrs.getLength(), 1) 62 self.assertEqual(attrs.getNames(), ["attr"]) 63 self.assertEqual(attrs.getQNames(), ["attr"]) 64 self.assertEqual(len(attrs), 1) 65 self.assertTrue(attrs.has_key("attr")) 66 self.assertEqual(attrs.keys(), ["attr"]) 67 self.assertEqual(attrs.get("attr"), "val") 68 self.assertEqual(attrs.get("attr", 25), "val") 69 self.assertEqual(attrs.items(), [("attr", "val")]) 70 self.assertEqual(attrs.values(), ["val"]) 71 self.assertEqual(attrs.getValue("attr"), "val") 72 self.assertEqual(attrs.getValueByQName("attr"), "val") 73 self.assertEqual(attrs.getNameByQName("attr"), "attr") 74 self.assertEqual(attrs["attr"], "val") 75 self.assertEqual(attrs.getQNameByName("attr"), "attr") 76 77 class MakeParserTest(unittest.TestCase): 78 def test_make_parser2(self): 79 # Creating parsers several times in a row should succeed. 80 # Testing this because there have been failures of this kind 81 # before. 82 from xml.sax import make_parser 83 p = make_parser() 84 from xml.sax import make_parser 85 p = make_parser() 86 from xml.sax import make_parser 87 p = make_parser() 88 from xml.sax import make_parser 89 p = make_parser() 90 from xml.sax import make_parser 91 p = make_parser() 92 from xml.sax import make_parser 93 p = make_parser() 94 95 96 # =========================================================================== 97 # 98 # saxutils tests 99 # 100 # =========================================================================== 101 102 class SaxutilsTest(unittest.TestCase): 103 # ===== escape 104 def test_escape_basic(self): 105 self.assertEqual(escape("Donald Duck & Co"), "Donald Duck & Co") 106 107 def test_escape_all(self): 108 self.assertEqual(escape("<Donald Duck & Co>"), 109 "<Donald Duck & Co>") 110 111 def test_escape_extra(self): 112 self.assertEqual(escape("Hei p deg", {"" : "å"}), 113 "Hei på deg") 114 115 # ===== unescape 116 def test_unescape_basic(self): 117 self.assertEqual(unescape("Donald Duck & Co"), "Donald Duck & Co") 118 119 def test_unescape_all(self): 120 self.assertEqual(unescape("<Donald Duck & Co>"), 121 "<Donald Duck & Co>") 122 123 def test_unescape_extra(self): 124 self.assertEqual(unescape("Hei p deg", {"" : "å"}), 125 "Hei på deg") 126 127 def test_unescape_amp_extra(self): 128 self.assertEqual(unescape("&foo;", {"&foo;": "splat"}), "&foo;") 129 130 # ===== quoteattr 131 def test_quoteattr_basic(self): 132 self.assertEqual(quoteattr("Donald Duck & Co"), 133 '"Donald Duck & Co"') 134 135 def test_single_quoteattr(self): 136 self.assertEqual(quoteattr('Includes "double" quotes'), 137 '\'Includes "double" quotes\'') 138 139 def test_double_quoteattr(self): 140 self.assertEqual(quoteattr("Includes 'single' quotes"), 141 "\"Includes 'single' quotes\"") 142 143 def test_single_double_quoteattr(self): 144 self.assertEqual(quoteattr("Includes 'single' and \"double\" quotes"), 145 "\"Includes 'single' and "double" quotes\"") 146 147 # ===== make_parser 148 def test_make_parser(self): 149 # Creating a parser should succeed - it should fall back 150 # to the expatreader 151 p = make_parser(['xml.parsers.no_such_parser']) 152 153 154 # ===== XMLGenerator 155 156 start = '<?xml version="1.0" encoding="iso-8859-1"?>\n' 157 158 class XmlgenTest(unittest.TestCase): 159 def test_xmlgen_basic(self): 160 result = StringIO() 161 gen = XMLGenerator(result) 162 gen.startDocument() 163 gen.startElement("doc", {}) 164 gen.endElement("doc") 165 gen.endDocument() 166 167 self.assertEqual(result.getvalue(), start + "<doc></doc>") 168 169 def test_xmlgen_content(self): 170 result = StringIO() 171 gen = XMLGenerator(result) 172 173 gen.startDocument() 174 gen.startElement("doc", {}) 175 gen.characters("huhei") 176 gen.endElement("doc") 177 gen.endDocument() 178 179 self.assertEqual(result.getvalue(), start + "<doc>huhei</doc>") 180 181 def test_xmlgen_pi(self): 182 result = StringIO() 183 gen = XMLGenerator(result) 184 185 gen.startDocument() 186 gen.processingInstruction("test", "data") 187 gen.startElement("doc", {}) 188 gen.endElement("doc") 189 gen.endDocument() 190 191 self.assertEqual(result.getvalue(), start + "<?test data?><doc></doc>") 192 193 def test_xmlgen_content_escape(self): 194 result = StringIO() 195 gen = XMLGenerator(result) 196 197 gen.startDocument() 198 gen.startElement("doc", {}) 199 gen.characters("<huhei&") 200 gen.endElement("doc") 201 gen.endDocument() 202 203 self.assertEqual(result.getvalue(), 204 start + "<doc><huhei&</doc>") 205 206 def test_xmlgen_attr_escape(self): 207 result = StringIO() 208 gen = XMLGenerator(result) 209 210 gen.startDocument() 211 gen.startElement("doc", {"a": '"'}) 212 gen.startElement("e", {"a": "'"}) 213 gen.endElement("e") 214 gen.startElement("e", {"a": "'\""}) 215 gen.endElement("e") 216 gen.startElement("e", {"a": "\n\r\t"}) 217 gen.endElement("e") 218 gen.endElement("doc") 219 gen.endDocument() 220 221 self.assertEqual(result.getvalue(), start + 222 ("<doc a='\"'><e a=\"'\"></e>" 223 "<e a=\"'"\"></e>" 224 "<e a=\" 	\"></e></doc>")) 225 226 def test_xmlgen_ignorable(self): 227 result = StringIO() 228 gen = XMLGenerator(result) 229 230 gen.startDocument() 231 gen.startElement("doc", {}) 232 gen.ignorableWhitespace(" ") 233 gen.endElement("doc") 234 gen.endDocument() 235 236 self.assertEqual(result.getvalue(), start + "<doc> </doc>") 237 238 def test_xmlgen_ns(self): 239 result = StringIO() 240 gen = XMLGenerator(result) 241 242 gen.startDocument() 243 gen.startPrefixMapping("ns1", ns_uri) 244 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) 245 # add an unqualified name 246 gen.startElementNS((None, "udoc"), None, {}) 247 gen.endElementNS((None, "udoc"), None) 248 gen.endElementNS((ns_uri, "doc"), "ns1:doc") 249 gen.endPrefixMapping("ns1") 250 gen.endDocument() 251 252 self.assertEqual(result.getvalue(), start + \ 253 ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % 254 ns_uri)) 255 256 def test_1463026_1(self): 257 result = StringIO() 258 gen = XMLGenerator(result) 259 260 gen.startDocument() 261 gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) 262 gen.endElementNS((None, 'a'), 'a') 263 gen.endDocument() 264 265 self.assertEqual(result.getvalue(), start+'<a b="c"></a>') 266 267 def test_1463026_2(self): 268 result = StringIO() 269 gen = XMLGenerator(result) 270 271 gen.startDocument() 272 gen.startPrefixMapping(None, 'qux') 273 gen.startElementNS(('qux', 'a'), 'a', {}) 274 gen.endElementNS(('qux', 'a'), 'a') 275 gen.endPrefixMapping(None) 276 gen.endDocument() 277 278 self.assertEqual(result.getvalue(), start+'<a xmlns="qux"></a>') 279 280 def test_1463026_3(self): 281 result = StringIO() 282 gen = XMLGenerator(result) 283 284 gen.startDocument() 285 gen.startPrefixMapping('my', 'qux') 286 gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) 287 gen.endElementNS(('qux', 'a'), 'a') 288 gen.endPrefixMapping('my') 289 gen.endDocument() 290 291 self.assertEqual(result.getvalue(), 292 start+'<my:a xmlns:my="qux" b="c"></my:a>') 293 294 def test_5027_1(self): 295 # The xml prefix (as in xml:lang below) is reserved and bound by 296 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had 297 # a bug whereby a KeyError is thrown because this namespace is missing 298 # from a dictionary. 299 # 300 # This test demonstrates the bug by parsing a document. 301 test_xml = StringIO( 302 '<?xml version="1.0"?>' 303 '<a:g1 xmlns:a="http://example.com/ns">' 304 '<a:g2 xml:lang="en">Hello</a:g2>' 305 '</a:g1>') 306 307 parser = make_parser() 308 parser.setFeature(feature_namespaces, True) 309 result = StringIO() 310 gen = XMLGenerator(result) 311 parser.setContentHandler(gen) 312 parser.parse(test_xml) 313 314 self.assertEqual(result.getvalue(), 315 start + ( 316 '<a:g1 xmlns:a="http://example.com/ns">' 317 '<a:g2 xml:lang="en">Hello</a:g2>' 318 '</a:g1>')) 319 320 def test_5027_2(self): 321 # The xml prefix (as in xml:lang below) is reserved and bound by 322 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had 323 # a bug whereby a KeyError is thrown because this namespace is missing 324 # from a dictionary. 325 # 326 # This test demonstrates the bug by direct manipulation of the 327 # XMLGenerator. 328 result = StringIO() 329 gen = XMLGenerator(result) 330 331 gen.startDocument() 332 gen.startPrefixMapping('a', 'http://example.com/ns') 333 gen.startElementNS(('http://example.com/ns', 'g1'), 'g1', {}) 334 lang_attr = {('http://www.w3.org/XML/1998/namespace', 'lang'): 'en'} 335 gen.startElementNS(('http://example.com/ns', 'g2'), 'g2', lang_attr) 336 gen.characters('Hello') 337 gen.endElementNS(('http://example.com/ns', 'g2'), 'g2') 338 gen.endElementNS(('http://example.com/ns', 'g1'), 'g1') 339 gen.endPrefixMapping('a') 340 gen.endDocument() 341 342 self.assertEqual(result.getvalue(), 343 start + ( 344 '<a:g1 xmlns:a="http://example.com/ns">' 345 '<a:g2 xml:lang="en">Hello</a:g2>' 346 '</a:g1>')) 347 348 349 class XMLFilterBaseTest(unittest.TestCase): 350 def test_filter_basic(self): 351 result = StringIO() 352 gen = XMLGenerator(result) 353 filter = XMLFilterBase() 354 filter.setContentHandler(gen) 355 356 filter.startDocument() 357 filter.startElement("doc", {}) 358 filter.characters("content") 359 filter.ignorableWhitespace(" ") 360 filter.endElement("doc") 361 filter.endDocument() 362 363 self.assertEqual(result.getvalue(), start + "<doc>content </doc>") 364 365 # =========================================================================== 366 # 367 # expatreader tests 368 # 369 # =========================================================================== 370 371 xml_test_out = open(TEST_XMLFILE_OUT).read() 372 373 class ExpatReaderTest(XmlTestBase): 374 375 # ===== XMLReader support 376 377 def test_expat_file(self): 378 parser = create_parser() 379 result = StringIO() 380 xmlgen = XMLGenerator(result) 381 382 parser.setContentHandler(xmlgen) 383 parser.parse(open(TEST_XMLFILE)) 384 385 self.assertEqual(result.getvalue(), xml_test_out) 386 387 # ===== DTDHandler support 388 389 class TestDTDHandler: 390 391 def __init__(self): 392 self._notations = [] 393 self._entities = [] 394 395 def notationDecl(self, name, publicId, systemId): 396 self._notations.append((name, publicId, systemId)) 397 398 def unparsedEntityDecl(self, name, publicId, systemId, ndata): 399 self._entities.append((name, publicId, systemId, ndata)) 400 401 def test_expat_dtdhandler(self): 402 parser = create_parser() 403 handler = self.TestDTDHandler() 404 parser.setDTDHandler(handler) 405 406 parser.feed('<!DOCTYPE doc [\n') 407 parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n') 408 parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n') 409 parser.feed(']>\n') 410 parser.feed('<doc></doc>') 411 parser.close() 412 413 self.assertEqual(handler._notations, 414 [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)]) 415 self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")]) 416 417 # ===== EntityResolver support 418 419 class TestEntityResolver: 420 421 def resolveEntity(self, publicId, systemId): 422 inpsrc = InputSource() 423 inpsrc.setByteStream(StringIO("<entity/>")) 424 return inpsrc 425 426 def test_expat_entityresolver(self): 427 parser = create_parser() 428 parser.setEntityResolver(self.TestEntityResolver()) 429 result = StringIO() 430 parser.setContentHandler(XMLGenerator(result)) 431 432 parser.feed('<!DOCTYPE doc [\n') 433 parser.feed(' <!ENTITY test SYSTEM "whatever">\n') 434 parser.feed(']>\n') 435 parser.feed('<doc>&test;</doc>') 436 parser.close() 437 438 self.assertEqual(result.getvalue(), start + 439 "<doc><entity></entity></doc>") 440 441 # ===== Attributes support 442 443 class AttrGatherer(ContentHandler): 444 445 def startElement(self, name, attrs): 446 self._attrs = attrs 447 448 def startElementNS(self, name, qname, attrs): 449 self._attrs = attrs 450 451 def test_expat_attrs_empty(self): 452 parser = create_parser() 453 gather = self.AttrGatherer() 454 parser.setContentHandler(gather) 455 456 parser.feed("<doc/>") 457 parser.close() 458 459 self.verify_empty_attrs(gather._attrs) 460 461 def test_expat_attrs_wattr(self): 462 parser = create_parser() 463 gather = self.AttrGatherer() 464 parser.setContentHandler(gather) 465 466 parser.feed("<doc attr='val'/>") 467 parser.close() 468 469 self.verify_attrs_wattr(gather._attrs) 470 471 def test_expat_nsattrs_empty(self): 472 parser = create_parser(1) 473 gather = self.AttrGatherer() 474 parser.setContentHandler(gather) 475 476 parser.feed("<doc/>") 477 parser.close() 478 479 self.verify_empty_nsattrs(gather._attrs) 480 481 def test_expat_nsattrs_wattr(self): 482 parser = create_parser(1) 483 gather = self.AttrGatherer() 484 parser.setContentHandler(gather) 485 486 parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri) 487 parser.close() 488 489 attrs = gather._attrs 490 491 self.assertEqual(attrs.getLength(), 1) 492 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) 493 self.assertTrue((attrs.getQNames() == [] or 494 attrs.getQNames() == ["ns:attr"])) 495 self.assertEqual(len(attrs), 1) 496 self.assertTrue(attrs.has_key((ns_uri, "attr"))) 497 self.assertEqual(attrs.get((ns_uri, "attr")), "val") 498 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") 499 self.assertEqual(attrs.items(), [((ns_uri, "attr"), "val")]) 500 self.assertEqual(attrs.values(), ["val"]) 501 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") 502 self.assertEqual(attrs[(ns_uri, "attr")], "val") 503 504 # ===== InputSource support 505 506 def test_expat_inpsource_filename(self): 507 parser = create_parser() 508 result = StringIO() 509 xmlgen = XMLGenerator(result) 510 511 parser.setContentHandler(xmlgen) 512 parser.parse(TEST_XMLFILE) 513 514 self.assertEqual(result.getvalue(), xml_test_out) 515 516 def test_expat_inpsource_sysid(self): 517 parser = create_parser() 518 result = StringIO() 519 xmlgen = XMLGenerator(result) 520 521 parser.setContentHandler(xmlgen) 522 parser.parse(InputSource(TEST_XMLFILE)) 523 524 self.assertEqual(result.getvalue(), xml_test_out) 525 526 def test_expat_inpsource_stream(self): 527 parser = create_parser() 528 result = StringIO() 529 xmlgen = XMLGenerator(result) 530 531 parser.setContentHandler(xmlgen) 532 inpsrc = InputSource() 533 inpsrc.setByteStream(open(TEST_XMLFILE)) 534 parser.parse(inpsrc) 535 536 self.assertEqual(result.getvalue(), xml_test_out) 537 538 # ===== IncrementalParser support 539 540 def test_expat_incremental(self): 541 result = StringIO() 542 xmlgen = XMLGenerator(result) 543 parser = create_parser() 544 parser.setContentHandler(xmlgen) 545 546 parser.feed("<doc>") 547 parser.feed("</doc>") 548 parser.close() 549 550 self.assertEqual(result.getvalue(), start + "<doc></doc>") 551 552 def test_expat_incremental_reset(self): 553 result = StringIO() 554 xmlgen = XMLGenerator(result) 555 parser = create_parser() 556 parser.setContentHandler(xmlgen) 557 558 parser.feed("<doc>") 559 parser.feed("text") 560 561 result = StringIO() 562 xmlgen = XMLGenerator(result) 563 parser.setContentHandler(xmlgen) 564 parser.reset() 565 566 parser.feed("<doc>") 567 parser.feed("text") 568 parser.feed("</doc>") 569 parser.close() 570 571 self.assertEqual(result.getvalue(), start + "<doc>text</doc>") 572 573 # ===== Locator support 574 575 def test_expat_locator_noinfo(self): 576 result = StringIO() 577 xmlgen = XMLGenerator(result) 578 parser = create_parser() 579 parser.setContentHandler(xmlgen) 580 581 parser.feed("<doc>") 582 parser.feed("</doc>") 583 parser.close() 584 585 self.assertEqual(parser.getSystemId(), None) 586 self.assertEqual(parser.getPublicId(), None) 587 self.assertEqual(parser.getLineNumber(), 1) 588 589 def test_expat_locator_withinfo(self): 590 result = StringIO() 591 xmlgen = XMLGenerator(result) 592 parser = create_parser() 593 parser.setContentHandler(xmlgen) 594 parser.parse(TEST_XMLFILE) 595 596 self.assertEqual(parser.getSystemId(), TEST_XMLFILE) 597 self.assertEqual(parser.getPublicId(), None) 598 599 600 # =========================================================================== 601 # 602 # error reporting 603 # 604 # =========================================================================== 605 606 class ErrorReportingTest(unittest.TestCase): 607 def test_expat_inpsource_location(self): 608 parser = create_parser() 609 parser.setContentHandler(ContentHandler()) # do nothing 610 source = InputSource() 611 source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed 612 name = "a file name" 613 source.setSystemId(name) 614 try: 615 parser.parse(source) 616 self.fail() 617 except SAXException, e: 618 self.assertEqual(e.getSystemId(), name) 619 620 def test_expat_incomplete(self): 621 parser = create_parser() 622 parser.setContentHandler(ContentHandler()) # do nothing 623 self.assertRaises(SAXParseException, parser.parse, StringIO("<foo>")) 624 625 def test_sax_parse_exception_str(self): 626 # pass various values from a locator to the SAXParseException to 627 # make sure that the __str__() doesn't fall apart when None is 628 # passed instead of an integer line and column number 629 # 630 # use "normal" values for the locator: 631 str(SAXParseException("message", None, 632 self.DummyLocator(1, 1))) 633 # use None for the line number: 634 str(SAXParseException("message", None, 635 self.DummyLocator(None, 1))) 636 # use None for the column number: 637 str(SAXParseException("message", None, 638 self.DummyLocator(1, None))) 639 # use None for both: 640 str(SAXParseException("message", None, 641 self.DummyLocator(None, None))) 642 643 class DummyLocator: 644 def __init__(self, lineno, colno): 645 self._lineno = lineno 646 self._colno = colno 647 648 def getPublicId(self): 649 return "pubid" 650 651 def getSystemId(self): 652 return "sysid" 653 654 def getLineNumber(self): 655 return self._lineno 656 657 def getColumnNumber(self): 658 return self._colno 659 660 # =========================================================================== 661 # 662 # xmlreader tests 663 # 664 # =========================================================================== 665 666 class XmlReaderTest(XmlTestBase): 667 668 # ===== AttributesImpl 669 def test_attrs_empty(self): 670 self.verify_empty_attrs(AttributesImpl({})) 671 672 def test_attrs_wattr(self): 673 self.verify_attrs_wattr(AttributesImpl({"attr" : "val"})) 674 675 def test_nsattrs_empty(self): 676 self.verify_empty_nsattrs(AttributesNSImpl({}, {})) 677 678 def test_nsattrs_wattr(self): 679 attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, 680 {(ns_uri, "attr") : "ns:attr"}) 681 682 self.assertEqual(attrs.getLength(), 1) 683 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) 684 self.assertEqual(attrs.getQNames(), ["ns:attr"]) 685 self.assertEqual(len(attrs), 1) 686 self.assertTrue(attrs.has_key((ns_uri, "attr"))) 687 self.assertEqual(attrs.keys(), [(ns_uri, "attr")]) 688 self.assertEqual(attrs.get((ns_uri, "attr")), "val") 689 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") 690 self.assertEqual(attrs.items(), [((ns_uri, "attr"), "val")]) 691 self.assertEqual(attrs.values(), ["val"]) 692 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") 693 self.assertEqual(attrs.getValueByQName("ns:attr"), "val") 694 self.assertEqual(attrs.getNameByQName("ns:attr"), (ns_uri, "attr")) 695 self.assertEqual(attrs[(ns_uri, "attr")], "val") 696 self.assertEqual(attrs.getQNameByName((ns_uri, "attr")), "ns:attr") 697 698 699 # During the development of Python 2.5, an attempt to move the "xml" 700 # package implementation to a new package ("xmlcore") proved painful. 701 # The goal of this change was to allow applications to be able to 702 # obtain and rely on behavior in the standard library implementation 703 # of the XML support without needing to be concerned about the 704 # availability of the PyXML implementation. 705 # 706 # While the existing import hackery in Lib/xml/__init__.py can cause 707 # PyXML's _xmlpus package to supplant the "xml" package, that only 708 # works because either implementation uses the "xml" package name for 709 # imports. 710 # 711 # The move resulted in a number of problems related to the fact that 712 # the import machinery's "package context" is based on the name that's 713 # being imported rather than the __name__ of the actual package 714 # containment; it wasn't possible for the "xml" package to be replaced 715 # by a simple module that indirected imports to the "xmlcore" package. 716 # 717 # The following two tests exercised bugs that were introduced in that 718 # attempt. Keeping these tests around will help detect problems with 719 # other attempts to provide reliable access to the standard library's 720 # implementation of the XML support. 721 722 def test_sf_1511497(self): 723 # Bug report: http://www.python.org/sf/1511497 724 import sys 725 old_modules = sys.modules.copy() 726 for modname in sys.modules.keys(): 727 if modname.startswith("xml."): 728 del sys.modules[modname] 729 try: 730 import xml.sax.expatreader 731 module = xml.sax.expatreader 732 self.assertEqual(module.__name__, "xml.sax.expatreader") 733 finally: 734 sys.modules.update(old_modules) 735 736 def test_sf_1513611(self): 737 # Bug report: http://www.python.org/sf/1513611 738 sio = StringIO("invalid") 739 parser = make_parser() 740 from xml.sax import SAXParseException 741 self.assertRaises(SAXParseException, parser.parse, sio) 742 743 744 def test_main(): 745 run_unittest(MakeParserTest, 746 SaxutilsTest, 747 XmlgenTest, 748 ExpatReaderTest, 749 ErrorReportingTest, 750 XmlReaderTest) 751 752 if __name__ == "__main__": 753 test_main() 754