1 # regression test for SAX 2.0 -*- coding: iso-8859-1 -*-
4 from xml
.sax
import make_parser
, ContentHandler
, \
5 SAXException
, SAXReaderNotAvailable
, SAXParseException
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
, \
13 from xml
.sax
.expatreader
import create_parser
14 from xml
.sax
.xmlreader
import InputSource
, AttributesImpl
, AttributesNSImpl
15 from cStringIO
import StringIO
16 from test
.test_support
import findfile
, run_unittest
19 TEST_XMLFILE
= findfile("test.xml", subdir
="xmltestdata")
20 TEST_XMLFILE_OUT
= findfile("test.xml.out", subdir
="xmltestdata")
22 ns_uri
= "http://www.python.org/xml-ns/saxtest/"
24 class XmlTestBase(unittest
.TestCase
):
25 def verify_empty_attrs(self
, attrs
):
26 self
.assertRaises(KeyError, attrs
.getValue
, "attr")
27 self
.assertRaises(KeyError, attrs
.getValueByQName
, "attr")
28 self
.assertRaises(KeyError, attrs
.getNameByQName
, "attr")
29 self
.assertRaises(KeyError, attrs
.getQNameByName
, "attr")
30 self
.assertRaises(KeyError, attrs
.__getitem
__, "attr")
31 self
.assertEquals(attrs
.getLength(), 0)
32 self
.assertEquals(attrs
.getNames(), [])
33 self
.assertEquals(attrs
.getQNames(), [])
34 self
.assertEquals(len(attrs
), 0)
35 self
.assertFalse(attrs
.has_key("attr"))
36 self
.assertEquals(attrs
.keys(), [])
37 self
.assertEquals(attrs
.get("attrs"), None)
38 self
.assertEquals(attrs
.get("attrs", 25), 25)
39 self
.assertEquals(attrs
.items(), [])
40 self
.assertEquals(attrs
.values(), [])
42 def verify_empty_nsattrs(self
, attrs
):
43 self
.assertRaises(KeyError, attrs
.getValue
, (ns_uri
, "attr"))
44 self
.assertRaises(KeyError, attrs
.getValueByQName
, "ns:attr")
45 self
.assertRaises(KeyError, attrs
.getNameByQName
, "ns:attr")
46 self
.assertRaises(KeyError, attrs
.getQNameByName
, (ns_uri
, "attr"))
47 self
.assertRaises(KeyError, attrs
.__getitem
__, (ns_uri
, "attr"))
48 self
.assertEquals(attrs
.getLength(), 0)
49 self
.assertEquals(attrs
.getNames(), [])
50 self
.assertEquals(attrs
.getQNames(), [])
51 self
.assertEquals(len(attrs
), 0)
52 self
.assertFalse(attrs
.has_key((ns_uri
, "attr")))
53 self
.assertEquals(attrs
.keys(), [])
54 self
.assertEquals(attrs
.get((ns_uri
, "attr")), None)
55 self
.assertEquals(attrs
.get((ns_uri
, "attr"), 25), 25)
56 self
.assertEquals(attrs
.items(), [])
57 self
.assertEquals(attrs
.values(), [])
59 def verify_attrs_wattr(self
, attrs
):
60 self
.assertEquals(attrs
.getLength(), 1)
61 self
.assertEquals(attrs
.getNames(), ["attr"])
62 self
.assertEquals(attrs
.getQNames(), ["attr"])
63 self
.assertEquals(len(attrs
), 1)
64 self
.assertTrue(attrs
.has_key("attr"))
65 self
.assertEquals(attrs
.keys(), ["attr"])
66 self
.assertEquals(attrs
.get("attr"), "val")
67 self
.assertEquals(attrs
.get("attr", 25), "val")
68 self
.assertEquals(attrs
.items(), [("attr", "val")])
69 self
.assertEquals(attrs
.values(), ["val"])
70 self
.assertEquals(attrs
.getValue("attr"), "val")
71 self
.assertEquals(attrs
.getValueByQName("attr"), "val")
72 self
.assertEquals(attrs
.getNameByQName("attr"), "attr")
73 self
.assertEquals(attrs
["attr"], "val")
74 self
.assertEquals(attrs
.getQNameByName("attr"), "attr")
76 class MakeParserTest(unittest
.TestCase
):
77 def test_make_parser2(self
):
78 # Creating parsers several times in a row should succeed.
79 # Testing this because there have been failures of this kind
81 from xml
.sax
import make_parser
83 from xml
.sax
import make_parser
85 from xml
.sax
import make_parser
87 from xml
.sax
import make_parser
89 from xml
.sax
import make_parser
91 from xml
.sax
import make_parser
95 # ===========================================================================
99 # ===========================================================================
101 class SaxutilsTest(unittest
.TestCase
):
103 def test_escape_basic(self
):
104 self
.assertEquals(escape("Donald Duck & Co"), "Donald Duck & Co")
106 def test_escape_all(self
):
107 self
.assertEquals(escape("<Donald Duck & Co>"),
108 "<Donald Duck & Co>")
110 def test_escape_extra(self
):
111 self
.assertEquals(escape("Hei på deg", {"å" : "å"}),
115 def test_unescape_basic(self
):
116 self
.assertEquals(unescape("Donald Duck & Co"), "Donald Duck & Co")
118 def test_unescape_all(self
):
119 self
.assertEquals(unescape("<Donald Duck & Co>"),
120 "<Donald Duck & Co>")
122 def test_unescape_extra(self
):
123 self
.assertEquals(unescape("Hei på deg", {"å" : "å"}),
126 def test_unescape_amp_extra(self
):
127 self
.assertEquals(unescape("&foo;", {"&foo;": "splat"}), "&foo;")
130 def test_quoteattr_basic(self
):
131 self
.assertEquals(quoteattr("Donald Duck & Co"),
132 '"Donald Duck & Co"')
134 def test_single_quoteattr(self
):
135 self
.assertEquals(quoteattr('Includes "double" quotes'),
136 '\'Includes "double" quotes\'')
138 def test_double_quoteattr(self
):
139 self
.assertEquals(quoteattr("Includes 'single' quotes"),
140 "\"Includes 'single' quotes\"")
142 def test_single_double_quoteattr(self
):
143 self
.assertEquals(quoteattr("Includes 'single' and \"double\" quotes"),
144 "\"Includes 'single' and "double" quotes\"")
147 def test_make_parser(self
):
148 # Creating a parser should succeed - it should fall back
150 p
= make_parser(['xml.parsers.no_such_parser'])
155 start
= '<?xml version="1.0" encoding="iso-8859-1"?>\n'
157 class XmlgenTest(unittest
.TestCase
):
158 def test_xmlgen_basic(self
):
160 gen
= XMLGenerator(result
)
162 gen
.startElement("doc", {})
163 gen
.endElement("doc")
166 self
.assertEquals(result
.getvalue(), start
+ "<doc></doc>")
168 def test_xmlgen_content(self
):
170 gen
= XMLGenerator(result
)
173 gen
.startElement("doc", {})
174 gen
.characters("huhei")
175 gen
.endElement("doc")
178 self
.assertEquals(result
.getvalue(), start
+ "<doc>huhei</doc>")
180 def test_xmlgen_pi(self
):
182 gen
= XMLGenerator(result
)
185 gen
.processingInstruction("test", "data")
186 gen
.startElement("doc", {})
187 gen
.endElement("doc")
190 self
.assertEquals(result
.getvalue(), start
+ "<?test data?><doc></doc>")
192 def test_xmlgen_content_escape(self
):
194 gen
= XMLGenerator(result
)
197 gen
.startElement("doc", {})
198 gen
.characters("<huhei&")
199 gen
.endElement("doc")
202 self
.assertEquals(result
.getvalue(),
203 start
+ "<doc><huhei&</doc>")
205 def test_xmlgen_attr_escape(self
):
207 gen
= XMLGenerator(result
)
210 gen
.startElement("doc", {"a": '"'})
211 gen
.startElement("e", {"a": "'"})
213 gen
.startElement("e", {"a": "'\""})
215 gen
.startElement("e", {"a": "\n\r\t"})
217 gen
.endElement("doc")
220 self
.assertEquals(result
.getvalue(), start
+
221 ("<doc a='\"'><e a=\"'\"></e>"
222 "<e a=\"'"\"></e>"
223 "<e a=\" 	\"></e></doc>"))
225 def test_xmlgen_ignorable(self
):
227 gen
= XMLGenerator(result
)
230 gen
.startElement("doc", {})
231 gen
.ignorableWhitespace(" ")
232 gen
.endElement("doc")
235 self
.assertEquals(result
.getvalue(), start
+ "<doc> </doc>")
237 def test_xmlgen_ns(self
):
239 gen
= XMLGenerator(result
)
242 gen
.startPrefixMapping("ns1", ns_uri
)
243 gen
.startElementNS((ns_uri
, "doc"), "ns1:doc", {})
244 # add an unqualified name
245 gen
.startElementNS((None, "udoc"), None, {})
246 gen
.endElementNS((None, "udoc"), None)
247 gen
.endElementNS((ns_uri
, "doc"), "ns1:doc")
248 gen
.endPrefixMapping("ns1")
251 self
.assertEquals(result
.getvalue(), start
+ \
252 ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' %
255 def test_1463026_1(self
):
257 gen
= XMLGenerator(result
)
260 gen
.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'})
261 gen
.endElementNS((None, 'a'), 'a')
264 self
.assertEquals(result
.getvalue(), start
+'<a b="c"></a>')
266 def test_1463026_2(self
):
268 gen
= XMLGenerator(result
)
271 gen
.startPrefixMapping(None, 'qux')
272 gen
.startElementNS(('qux', 'a'), 'a', {})
273 gen
.endElementNS(('qux', 'a'), 'a')
274 gen
.endPrefixMapping(None)
277 self
.assertEquals(result
.getvalue(), start
+'<a xmlns="qux"></a>')
279 def test_1463026_3(self
):
281 gen
= XMLGenerator(result
)
284 gen
.startPrefixMapping('my', 'qux')
285 gen
.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'})
286 gen
.endElementNS(('qux', 'a'), 'a')
287 gen
.endPrefixMapping('my')
290 self
.assertEquals(result
.getvalue(),
291 start
+'<my:a xmlns:my="qux" b="c"></my:a>')
294 class XMLFilterBaseTest(unittest
.TestCase
):
295 def test_filter_basic(self
):
297 gen
= XMLGenerator(result
)
298 filter = XMLFilterBase()
299 filter.setContentHandler(gen
)
301 filter.startDocument()
302 filter.startElement("doc", {})
303 filter.characters("content")
304 filter.ignorableWhitespace(" ")
305 filter.endElement("doc")
308 self
.assertEquals(result
.getvalue(), start
+ "<doc>content </doc>")
310 # ===========================================================================
314 # ===========================================================================
316 xml_test_out
= open(TEST_XMLFILE_OUT
).read()
318 class ExpatReaderTest(XmlTestBase
):
320 # ===== XMLReader support
322 def test_expat_file(self
):
323 parser
= create_parser()
325 xmlgen
= XMLGenerator(result
)
327 parser
.setContentHandler(xmlgen
)
328 parser
.parse(open(TEST_XMLFILE
))
330 self
.assertEquals(result
.getvalue(), xml_test_out
)
332 # ===== DTDHandler support
334 class TestDTDHandler
:
340 def notationDecl(self
, name
, publicId
, systemId
):
341 self
._notations
.append((name
, publicId
, systemId
))
343 def unparsedEntityDecl(self
, name
, publicId
, systemId
, ndata
):
344 self
._entities
.append((name
, publicId
, systemId
, ndata
))
346 def test_expat_dtdhandler(self
):
347 parser
= create_parser()
348 handler
= self
.TestDTDHandler()
349 parser
.setDTDHandler(handler
)
351 parser
.feed('<!DOCTYPE doc [\n')
352 parser
.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n')
353 parser
.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n')
355 parser
.feed('<doc></doc>')
358 self
.assertEquals(handler
._notations
,
359 [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)])
360 self
.assertEquals(handler
._entities
, [("img", None, "expat.gif", "GIF")])
362 # ===== EntityResolver support
364 class TestEntityResolver
:
366 def resolveEntity(self
, publicId
, systemId
):
367 inpsrc
= InputSource()
368 inpsrc
.setByteStream(StringIO("<entity/>"))
371 def test_expat_entityresolver(self
):
372 parser
= create_parser()
373 parser
.setEntityResolver(self
.TestEntityResolver())
375 parser
.setContentHandler(XMLGenerator(result
))
377 parser
.feed('<!DOCTYPE doc [\n')
378 parser
.feed(' <!ENTITY test SYSTEM "whatever">\n')
380 parser
.feed('<doc>&test;</doc>')
383 self
.assertEquals(result
.getvalue(), start
+
384 "<doc><entity></entity></doc>")
386 # ===== Attributes support
388 class AttrGatherer(ContentHandler
):
390 def startElement(self
, name
, attrs
):
393 def startElementNS(self
, name
, qname
, attrs
):
396 def test_expat_attrs_empty(self
):
397 parser
= create_parser()
398 gather
= self
.AttrGatherer()
399 parser
.setContentHandler(gather
)
401 parser
.feed("<doc/>")
404 self
.verify_empty_attrs(gather
._attrs
)
406 def test_expat_attrs_wattr(self
):
407 parser
= create_parser()
408 gather
= self
.AttrGatherer()
409 parser
.setContentHandler(gather
)
411 parser
.feed("<doc attr='val'/>")
414 self
.verify_attrs_wattr(gather
._attrs
)
416 def test_expat_nsattrs_empty(self
):
417 parser
= create_parser(1)
418 gather
= self
.AttrGatherer()
419 parser
.setContentHandler(gather
)
421 parser
.feed("<doc/>")
424 self
.verify_empty_nsattrs(gather
._attrs
)
426 def test_expat_nsattrs_wattr(self
):
427 parser
= create_parser(1)
428 gather
= self
.AttrGatherer()
429 parser
.setContentHandler(gather
)
431 parser
.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri
)
434 attrs
= gather
._attrs
436 self
.assertEquals(attrs
.getLength(), 1)
437 self
.assertEquals(attrs
.getNames(), [(ns_uri
, "attr")])
438 self
.assertTrue((attrs
.getQNames() == [] or
439 attrs
.getQNames() == ["ns:attr"]))
440 self
.assertEquals(len(attrs
), 1)
441 self
.assertTrue(attrs
.has_key((ns_uri
, "attr")))
442 self
.assertEquals(attrs
.get((ns_uri
, "attr")), "val")
443 self
.assertEquals(attrs
.get((ns_uri
, "attr"), 25), "val")
444 self
.assertEquals(attrs
.items(), [((ns_uri
, "attr"), "val")])
445 self
.assertEquals(attrs
.values(), ["val"])
446 self
.assertEquals(attrs
.getValue((ns_uri
, "attr")), "val")
447 self
.assertEquals(attrs
[(ns_uri
, "attr")], "val")
449 # ===== InputSource support
451 def test_expat_inpsource_filename(self
):
452 parser
= create_parser()
454 xmlgen
= XMLGenerator(result
)
456 parser
.setContentHandler(xmlgen
)
457 parser
.parse(TEST_XMLFILE
)
459 self
.assertEquals(result
.getvalue(), xml_test_out
)
461 def test_expat_inpsource_sysid(self
):
462 parser
= create_parser()
464 xmlgen
= XMLGenerator(result
)
466 parser
.setContentHandler(xmlgen
)
467 parser
.parse(InputSource(TEST_XMLFILE
))
469 self
.assertEquals(result
.getvalue(), xml_test_out
)
471 def test_expat_inpsource_stream(self
):
472 parser
= create_parser()
474 xmlgen
= XMLGenerator(result
)
476 parser
.setContentHandler(xmlgen
)
477 inpsrc
= InputSource()
478 inpsrc
.setByteStream(open(TEST_XMLFILE
))
481 self
.assertEquals(result
.getvalue(), xml_test_out
)
483 # ===== IncrementalParser support
485 def test_expat_incremental(self
):
487 xmlgen
= XMLGenerator(result
)
488 parser
= create_parser()
489 parser
.setContentHandler(xmlgen
)
492 parser
.feed("</doc>")
495 self
.assertEquals(result
.getvalue(), start
+ "<doc></doc>")
497 def test_expat_incremental_reset(self
):
499 xmlgen
= XMLGenerator(result
)
500 parser
= create_parser()
501 parser
.setContentHandler(xmlgen
)
507 xmlgen
= XMLGenerator(result
)
508 parser
.setContentHandler(xmlgen
)
513 parser
.feed("</doc>")
516 self
.assertEquals(result
.getvalue(), start
+ "<doc>text</doc>")
518 # ===== Locator support
520 def test_expat_locator_noinfo(self
):
522 xmlgen
= XMLGenerator(result
)
523 parser
= create_parser()
524 parser
.setContentHandler(xmlgen
)
527 parser
.feed("</doc>")
530 self
.assertEquals(parser
.getSystemId(), None)
531 self
.assertEquals(parser
.getPublicId(), None)
532 self
.assertEquals(parser
.getLineNumber(), 1)
534 def test_expat_locator_withinfo(self
):
536 xmlgen
= XMLGenerator(result
)
537 parser
= create_parser()
538 parser
.setContentHandler(xmlgen
)
539 parser
.parse(TEST_XMLFILE
)
541 self
.assertEquals(parser
.getSystemId(), TEST_XMLFILE
)
542 self
.assertEquals(parser
.getPublicId(), None)
545 # ===========================================================================
549 # ===========================================================================
551 class ErrorReportingTest(unittest
.TestCase
):
552 def test_expat_inpsource_location(self
):
553 parser
= create_parser()
554 parser
.setContentHandler(ContentHandler()) # do nothing
555 source
= InputSource()
556 source
.setByteStream(StringIO("<foo bar foobar>")) #ill-formed
558 source
.setSystemId(name
)
562 except SAXException
, e
:
563 self
.assertEquals(e
.getSystemId(), name
)
565 def test_expat_incomplete(self
):
566 parser
= create_parser()
567 parser
.setContentHandler(ContentHandler()) # do nothing
568 self
.assertRaises(SAXParseException
, parser
.parse
, StringIO("<foo>"))
570 def test_sax_parse_exception_str(self
):
571 # pass various values from a locator to the SAXParseException to
572 # make sure that the __str__() doesn't fall apart when None is
573 # passed instead of an integer line and column number
575 # use "normal" values for the locator:
576 str(SAXParseException("message", None,
577 self
.DummyLocator(1, 1)))
578 # use None for the line number:
579 str(SAXParseException("message", None,
580 self
.DummyLocator(None, 1)))
581 # use None for the column number:
582 str(SAXParseException("message", None,
583 self
.DummyLocator(1, None)))
585 str(SAXParseException("message", None,
586 self
.DummyLocator(None, None)))
589 def __init__(self
, lineno
, colno
):
590 self
._lineno
= lineno
593 def getPublicId(self
):
596 def getSystemId(self
):
599 def getLineNumber(self
):
602 def getColumnNumber(self
):
605 # ===========================================================================
609 # ===========================================================================
611 class XmlReaderTest(XmlTestBase
):
613 # ===== AttributesImpl
614 def test_attrs_empty(self
):
615 self
.verify_empty_attrs(AttributesImpl({}))
617 def test_attrs_wattr(self
):
618 self
.verify_attrs_wattr(AttributesImpl({"attr" : "val"}))
620 def test_nsattrs_empty(self
):
621 self
.verify_empty_nsattrs(AttributesNSImpl({}, {}))
623 def test_nsattrs_wattr(self
):
624 attrs
= AttributesNSImpl({(ns_uri
, "attr") : "val"},
625 {(ns_uri
, "attr") : "ns:attr"})
627 self
.assertEquals(attrs
.getLength(), 1)
628 self
.assertEquals(attrs
.getNames(), [(ns_uri
, "attr")])
629 self
.assertEquals(attrs
.getQNames(), ["ns:attr"])
630 self
.assertEquals(len(attrs
), 1)
631 self
.assertTrue(attrs
.has_key((ns_uri
, "attr")))
632 self
.assertEquals(attrs
.keys(), [(ns_uri
, "attr")])
633 self
.assertEquals(attrs
.get((ns_uri
, "attr")), "val")
634 self
.assertEquals(attrs
.get((ns_uri
, "attr"), 25), "val")
635 self
.assertEquals(attrs
.items(), [((ns_uri
, "attr"), "val")])
636 self
.assertEquals(attrs
.values(), ["val"])
637 self
.assertEquals(attrs
.getValue((ns_uri
, "attr")), "val")
638 self
.assertEquals(attrs
.getValueByQName("ns:attr"), "val")
639 self
.assertEquals(attrs
.getNameByQName("ns:attr"), (ns_uri
, "attr"))
640 self
.assertEquals(attrs
[(ns_uri
, "attr")], "val")
641 self
.assertEquals(attrs
.getQNameByName((ns_uri
, "attr")), "ns:attr")
644 # During the development of Python 2.5, an attempt to move the "xml"
645 # package implementation to a new package ("xmlcore") proved painful.
646 # The goal of this change was to allow applications to be able to
647 # obtain and rely on behavior in the standard library implementation
648 # of the XML support without needing to be concerned about the
649 # availability of the PyXML implementation.
651 # While the existing import hackery in Lib/xml/__init__.py can cause
652 # PyXML's _xmlpus package to supplant the "xml" package, that only
653 # works because either implementation uses the "xml" package name for
656 # The move resulted in a number of problems related to the fact that
657 # the import machinery's "package context" is based on the name that's
658 # being imported rather than the __name__ of the actual package
659 # containment; it wasn't possible for the "xml" package to be replaced
660 # by a simple module that indirected imports to the "xmlcore" package.
662 # The following two tests exercised bugs that were introduced in that
663 # attempt. Keeping these tests around will help detect problems with
664 # other attempts to provide reliable access to the standard library's
665 # implementation of the XML support.
667 def test_sf_1511497(self
):
668 # Bug report: http://www.python.org/sf/1511497
670 old_modules
= sys
.modules
.copy()
671 for modname
in sys
.modules
.keys():
672 if modname
.startswith("xml."):
673 del sys
.modules
[modname
]
675 import xml
.sax
.expatreader
676 module
= xml
.sax
.expatreader
677 self
.assertEquals(module
.__name
__, "xml.sax.expatreader")
679 sys
.modules
.update(old_modules
)
681 def test_sf_1513611(self
):
682 # Bug report: http://www.python.org/sf/1513611
683 sio
= StringIO("invalid")
684 parser
= make_parser()
685 from xml
.sax
import SAXParseException
686 self
.assertRaises(SAXParseException
, parser
.parse
, sio
)
690 run_unittest(MakeParserTest
,
697 if __name__
== "__main__":