Home | History | Annotate | Download | only in test
      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                          "&lt;Donald Duck &amp; Co&gt;")
    110 
    111     def test_escape_extra(self):
    112         self.assertEqual(escape("Hei p deg", {"" : "&aring;"}),
    113                          "Hei p&aring; deg")
    114 
    115     # ===== unescape

    116     def test_unescape_basic(self):
    117         self.assertEqual(unescape("Donald Duck &amp; Co"), "Donald Duck & Co")
    118 
    119     def test_unescape_all(self):
    120         self.assertEqual(unescape("&lt;Donald Duck &amp; Co&gt;"),
    121                          "<Donald Duck & Co>")
    122 
    123     def test_unescape_extra(self):
    124         self.assertEqual(unescape("Hei p deg", {"" : "&aring;"}),
    125                          "Hei p&aring; deg")
    126 
    127     def test_unescape_amp_extra(self):
    128         self.assertEqual(unescape("&amp;foo;", {"&foo;": "splat"}), "&foo;")
    129 
    130     # ===== quoteattr

    131     def test_quoteattr_basic(self):
    132         self.assertEqual(quoteattr("Donald Duck & Co"),
    133                          '"Donald Duck &amp; 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 &quot;double&quot; 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>&lt;huhei&amp;</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=\"'&quot;\"></e>"
    224              "<e a=\"&#10;&#13;&#9;\"></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