Move setting of ioready 'wait' earlier in call chain, to
[python/dscho.git] / Lib / test / test_sax.py
blobaf97888793cb68e9883e3d1a98dc2ef5966e9321
1 # regression test for SAX 2.0 -*- coding: iso-8859-1 -*-
2 # $Id$
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.xmlreader import InputSource, AttributesImpl, AttributesNSImpl
15 from cStringIO import StringIO
16 from test.test_support import verify, verbose, TestFailed, findfile
17 import os
19 # ===== Utilities
21 tests = 0
22 failures = []
24 def confirm(outcome, name):
25 global tests
27 tests = tests + 1
28 if outcome:
29 if verbose:
30 print "Failed", name
31 else:
32 failures.append(name)
34 def test_make_parser2():
35 try:
36 # Creating parsers several times in a row should succeed.
37 # Testing this because there have been failures of this kind
38 # before.
39 from xml.sax import make_parser
40 p = make_parser()
41 from xml.sax import make_parser
42 p = make_parser()
43 from xml.sax import make_parser
44 p = make_parser()
45 from xml.sax import make_parser
46 p = make_parser()
47 from xml.sax import make_parser
48 p = make_parser()
49 from xml.sax import make_parser
50 p = make_parser()
51 except:
52 return 0
53 else:
54 return p
57 # ===========================================================================
59 # saxutils tests
61 # ===========================================================================
63 # ===== escape
65 def test_escape_basic():
66 return escape("Donald Duck & Co") == "Donald Duck & Co"
68 def test_escape_all():
69 return escape("<Donald Duck & Co>") == "&lt;Donald Duck &amp; Co&gt;"
71 def test_escape_extra():
72 return escape("Hei på deg", {"å" : "&aring;"}) == "Hei p&aring; deg"
74 # ===== unescape
76 def test_unescape_basic():
77 return unescape("Donald Duck &amp; Co") == "Donald Duck & Co"
79 def test_unescape_all():
80 return unescape("&lt;Donald Duck &amp; Co&gt;") == "<Donald Duck & Co>"
82 def test_unescape_extra():
83 return unescape("Hei på deg", {"å" : "&aring;"}) == "Hei p&aring; deg"
85 def test_unescape_amp_extra():
86 return unescape("&amp;foo;", {"&foo;": "splat"}) == "&foo;"
88 # ===== quoteattr
90 def test_quoteattr_basic():
91 return quoteattr("Donald Duck & Co") == '"Donald Duck &amp; Co"'
93 def test_single_quoteattr():
94 return (quoteattr('Includes "double" quotes')
95 == '\'Includes "double" quotes\'')
97 def test_double_quoteattr():
98 return (quoteattr("Includes 'single' quotes")
99 == "\"Includes 'single' quotes\"")
101 def test_single_double_quoteattr():
102 return (quoteattr("Includes 'single' and \"double\" quotes")
103 == "\"Includes 'single' and &quot;double&quot; quotes\"")
105 # ===== make_parser
107 def test_make_parser():
108 try:
109 # Creating a parser should succeed - it should fall back
110 # to the expatreader
111 p = make_parser(['xml.parsers.no_such_parser'])
112 except:
113 return 0
114 else:
115 return p
118 # ===== XMLGenerator
120 start = '<?xml version="1.0" encoding="iso-8859-1"?>\n'
122 def test_xmlgen_basic():
123 result = StringIO()
124 gen = XMLGenerator(result)
125 gen.startDocument()
126 gen.startElement("doc", {})
127 gen.endElement("doc")
128 gen.endDocument()
130 return result.getvalue() == start + "<doc></doc>"
132 def test_xmlgen_content():
133 result = StringIO()
134 gen = XMLGenerator(result)
136 gen.startDocument()
137 gen.startElement("doc", {})
138 gen.characters("huhei")
139 gen.endElement("doc")
140 gen.endDocument()
142 return result.getvalue() == start + "<doc>huhei</doc>"
144 def test_xmlgen_pi():
145 result = StringIO()
146 gen = XMLGenerator(result)
148 gen.startDocument()
149 gen.processingInstruction("test", "data")
150 gen.startElement("doc", {})
151 gen.endElement("doc")
152 gen.endDocument()
154 return result.getvalue() == start + "<?test data?><doc></doc>"
156 def test_xmlgen_content_escape():
157 result = StringIO()
158 gen = XMLGenerator(result)
160 gen.startDocument()
161 gen.startElement("doc", {})
162 gen.characters("<huhei&")
163 gen.endElement("doc")
164 gen.endDocument()
166 return result.getvalue() == start + "<doc>&lt;huhei&amp;</doc>"
168 def test_xmlgen_attr_escape():
169 result = StringIO()
170 gen = XMLGenerator(result)
172 gen.startDocument()
173 gen.startElement("doc", {"a": '"'})
174 gen.startElement("e", {"a": "'"})
175 gen.endElement("e")
176 gen.startElement("e", {"a": "'\""})
177 gen.endElement("e")
178 gen.endElement("doc")
179 gen.endDocument()
181 return result.getvalue() == start \
182 + "<doc a='\"'><e a=\"'\"></e><e a=\"'&quot;\"></e></doc>"
184 def test_xmlgen_ignorable():
185 result = StringIO()
186 gen = XMLGenerator(result)
188 gen.startDocument()
189 gen.startElement("doc", {})
190 gen.ignorableWhitespace(" ")
191 gen.endElement("doc")
192 gen.endDocument()
194 return result.getvalue() == start + "<doc> </doc>"
196 ns_uri = "http://www.python.org/xml-ns/saxtest/"
198 def test_xmlgen_ns():
199 result = StringIO()
200 gen = XMLGenerator(result)
202 gen.startDocument()
203 gen.startPrefixMapping("ns1", ns_uri)
204 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {})
205 # add an unqualified name
206 gen.startElementNS((None, "udoc"), None, {})
207 gen.endElementNS((None, "udoc"), None)
208 gen.endElementNS((ns_uri, "doc"), "ns1:doc")
209 gen.endPrefixMapping("ns1")
210 gen.endDocument()
212 return result.getvalue() == start + \
213 ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' %
214 ns_uri)
216 # ===== XMLFilterBase
218 def test_filter_basic():
219 result = StringIO()
220 gen = XMLGenerator(result)
221 filter = XMLFilterBase()
222 filter.setContentHandler(gen)
224 filter.startDocument()
225 filter.startElement("doc", {})
226 filter.characters("content")
227 filter.ignorableWhitespace(" ")
228 filter.endElement("doc")
229 filter.endDocument()
231 return result.getvalue() == start + "<doc>content </doc>"
233 # ===========================================================================
235 # expatreader tests
237 # ===========================================================================
239 # ===== XMLReader support
241 def test_expat_file():
242 parser = create_parser()
243 result = StringIO()
244 xmlgen = XMLGenerator(result)
246 parser.setContentHandler(xmlgen)
247 parser.parse(open(findfile("test"+os.extsep+"xml")))
249 return result.getvalue() == xml_test_out
251 # ===== DTDHandler support
253 class TestDTDHandler:
255 def __init__(self):
256 self._notations = []
257 self._entities = []
259 def notationDecl(self, name, publicId, systemId):
260 self._notations.append((name, publicId, systemId))
262 def unparsedEntityDecl(self, name, publicId, systemId, ndata):
263 self._entities.append((name, publicId, systemId, ndata))
265 def test_expat_dtdhandler():
266 parser = create_parser()
267 handler = TestDTDHandler()
268 parser.setDTDHandler(handler)
270 parser.feed('<!DOCTYPE doc [\n')
271 parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n')
272 parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n')
273 parser.feed(']>\n')
274 parser.feed('<doc></doc>')
275 parser.close()
277 return handler._notations == [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)] and \
278 handler._entities == [("img", None, "expat.gif", "GIF")]
280 # ===== EntityResolver support
282 class TestEntityResolver:
284 def resolveEntity(self, publicId, systemId):
285 inpsrc = InputSource()
286 inpsrc.setByteStream(StringIO("<entity/>"))
287 return inpsrc
289 def test_expat_entityresolver():
290 parser = create_parser()
291 parser.setEntityResolver(TestEntityResolver())
292 result = StringIO()
293 parser.setContentHandler(XMLGenerator(result))
295 parser.feed('<!DOCTYPE doc [\n')
296 parser.feed(' <!ENTITY test SYSTEM "whatever">\n')
297 parser.feed(']>\n')
298 parser.feed('<doc>&test;</doc>')
299 parser.close()
301 return result.getvalue() == start + "<doc><entity></entity></doc>"
303 # ===== Attributes support
305 class AttrGatherer(ContentHandler):
307 def startElement(self, name, attrs):
308 self._attrs = attrs
310 def startElementNS(self, name, qname, attrs):
311 self._attrs = attrs
313 def test_expat_attrs_empty():
314 parser = create_parser()
315 gather = AttrGatherer()
316 parser.setContentHandler(gather)
318 parser.feed("<doc/>")
319 parser.close()
321 return verify_empty_attrs(gather._attrs)
323 def test_expat_attrs_wattr():
324 parser = create_parser()
325 gather = AttrGatherer()
326 parser.setContentHandler(gather)
328 parser.feed("<doc attr='val'/>")
329 parser.close()
331 return verify_attrs_wattr(gather._attrs)
333 def test_expat_nsattrs_empty():
334 parser = create_parser(1)
335 gather = AttrGatherer()
336 parser.setContentHandler(gather)
338 parser.feed("<doc/>")
339 parser.close()
341 return verify_empty_nsattrs(gather._attrs)
343 def test_expat_nsattrs_wattr():
344 parser = create_parser(1)
345 gather = AttrGatherer()
346 parser.setContentHandler(gather)
348 parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri)
349 parser.close()
351 attrs = gather._attrs
353 return attrs.getLength() == 1 and \
354 attrs.getNames() == [(ns_uri, "attr")] and \
355 (attrs.getQNames() == [] or attrs.getQNames() == ["ns:attr"]) and \
356 len(attrs) == 1 and \
357 attrs.has_key((ns_uri, "attr")) and \
358 attrs.keys() == [(ns_uri, "attr")] and \
359 attrs.get((ns_uri, "attr")) == "val" and \
360 attrs.get((ns_uri, "attr"), 25) == "val" and \
361 attrs.items() == [((ns_uri, "attr"), "val")] and \
362 attrs.values() == ["val"] and \
363 attrs.getValue((ns_uri, "attr")) == "val" and \
364 attrs[(ns_uri, "attr")] == "val"
366 # ===== InputSource support
368 xml_test_out = open(findfile("test"+os.extsep+"xml"+os.extsep+"out")).read()
370 def test_expat_inpsource_filename():
371 parser = create_parser()
372 result = StringIO()
373 xmlgen = XMLGenerator(result)
375 parser.setContentHandler(xmlgen)
376 parser.parse(findfile("test"+os.extsep+"xml"))
378 return result.getvalue() == xml_test_out
380 def test_expat_inpsource_sysid():
381 parser = create_parser()
382 result = StringIO()
383 xmlgen = XMLGenerator(result)
385 parser.setContentHandler(xmlgen)
386 parser.parse(InputSource(findfile("test"+os.extsep+"xml")))
388 return result.getvalue() == xml_test_out
390 def test_expat_inpsource_stream():
391 parser = create_parser()
392 result = StringIO()
393 xmlgen = XMLGenerator(result)
395 parser.setContentHandler(xmlgen)
396 inpsrc = InputSource()
397 inpsrc.setByteStream(open(findfile("test"+os.extsep+"xml")))
398 parser.parse(inpsrc)
400 return result.getvalue() == xml_test_out
402 # ===== IncrementalParser support
404 def test_expat_incremental():
405 result = StringIO()
406 xmlgen = XMLGenerator(result)
407 parser = create_parser()
408 parser.setContentHandler(xmlgen)
410 parser.feed("<doc>")
411 parser.feed("</doc>")
412 parser.close()
414 return result.getvalue() == start + "<doc></doc>"
416 def test_expat_incremental_reset():
417 result = StringIO()
418 xmlgen = XMLGenerator(result)
419 parser = create_parser()
420 parser.setContentHandler(xmlgen)
422 parser.feed("<doc>")
423 parser.feed("text")
425 result = StringIO()
426 xmlgen = XMLGenerator(result)
427 parser.setContentHandler(xmlgen)
428 parser.reset()
430 parser.feed("<doc>")
431 parser.feed("text")
432 parser.feed("</doc>")
433 parser.close()
435 return result.getvalue() == start + "<doc>text</doc>"
437 # ===== Locator support
439 def test_expat_locator_noinfo():
440 result = StringIO()
441 xmlgen = XMLGenerator(result)
442 parser = create_parser()
443 parser.setContentHandler(xmlgen)
445 parser.feed("<doc>")
446 parser.feed("</doc>")
447 parser.close()
449 return parser.getSystemId() is None and \
450 parser.getPublicId() is None and \
451 parser.getLineNumber() == 1
453 def test_expat_locator_withinfo():
454 result = StringIO()
455 xmlgen = XMLGenerator(result)
456 parser = create_parser()
457 parser.setContentHandler(xmlgen)
458 parser.parse(findfile("test.xml"))
460 return parser.getSystemId() == findfile("test.xml") and \
461 parser.getPublicId() is None
464 # ===========================================================================
466 # error reporting
468 # ===========================================================================
470 def test_expat_inpsource_location():
471 parser = create_parser()
472 parser.setContentHandler(ContentHandler()) # do nothing
473 source = InputSource()
474 source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed
475 name = "a file name"
476 source.setSystemId(name)
477 try:
478 parser.parse(source)
479 except SAXException, e:
480 return e.getSystemId() == name
482 def test_expat_incomplete():
483 parser = create_parser()
484 parser.setContentHandler(ContentHandler()) # do nothing
485 try:
486 parser.parse(StringIO("<foo>"))
487 except SAXParseException:
488 return 1 # ok, error found
489 else:
490 return 0
493 # ===========================================================================
495 # xmlreader tests
497 # ===========================================================================
499 # ===== AttributesImpl
501 def verify_empty_attrs(attrs):
502 try:
503 attrs.getValue("attr")
504 gvk = 0
505 except KeyError:
506 gvk = 1
508 try:
509 attrs.getValueByQName("attr")
510 gvqk = 0
511 except KeyError:
512 gvqk = 1
514 try:
515 attrs.getNameByQName("attr")
516 gnqk = 0
517 except KeyError:
518 gnqk = 1
520 try:
521 attrs.getQNameByName("attr")
522 gqnk = 0
523 except KeyError:
524 gqnk = 1
526 try:
527 attrs["attr"]
528 gik = 0
529 except KeyError:
530 gik = 1
532 return attrs.getLength() == 0 and \
533 attrs.getNames() == [] and \
534 attrs.getQNames() == [] and \
535 len(attrs) == 0 and \
536 not attrs.has_key("attr") and \
537 attrs.keys() == [] and \
538 attrs.get("attrs") is None and \
539 attrs.get("attrs", 25) == 25 and \
540 attrs.items() == [] and \
541 attrs.values() == [] and \
542 gvk and gvqk and gnqk and gik and gqnk
544 def verify_attrs_wattr(attrs):
545 return attrs.getLength() == 1 and \
546 attrs.getNames() == ["attr"] and \
547 attrs.getQNames() == ["attr"] and \
548 len(attrs) == 1 and \
549 attrs.has_key("attr") and \
550 attrs.keys() == ["attr"] and \
551 attrs.get("attr") == "val" and \
552 attrs.get("attr", 25) == "val" and \
553 attrs.items() == [("attr", "val")] and \
554 attrs.values() == ["val"] and \
555 attrs.getValue("attr") == "val" and \
556 attrs.getValueByQName("attr") == "val" and \
557 attrs.getNameByQName("attr") == "attr" and \
558 attrs["attr"] == "val" and \
559 attrs.getQNameByName("attr") == "attr"
561 def test_attrs_empty():
562 return verify_empty_attrs(AttributesImpl({}))
564 def test_attrs_wattr():
565 return verify_attrs_wattr(AttributesImpl({"attr" : "val"}))
567 # ===== AttributesImpl
569 def verify_empty_nsattrs(attrs):
570 try:
571 attrs.getValue((ns_uri, "attr"))
572 gvk = 0
573 except KeyError:
574 gvk = 1
576 try:
577 attrs.getValueByQName("ns:attr")
578 gvqk = 0
579 except KeyError:
580 gvqk = 1
582 try:
583 attrs.getNameByQName("ns:attr")
584 gnqk = 0
585 except KeyError:
586 gnqk = 1
588 try:
589 attrs.getQNameByName((ns_uri, "attr"))
590 gqnk = 0
591 except KeyError:
592 gqnk = 1
594 try:
595 attrs[(ns_uri, "attr")]
596 gik = 0
597 except KeyError:
598 gik = 1
600 return attrs.getLength() == 0 and \
601 attrs.getNames() == [] and \
602 attrs.getQNames() == [] and \
603 len(attrs) == 0 and \
604 not attrs.has_key((ns_uri, "attr")) and \
605 attrs.keys() == [] and \
606 attrs.get((ns_uri, "attr")) is None and \
607 attrs.get((ns_uri, "attr"), 25) == 25 and \
608 attrs.items() == [] and \
609 attrs.values() == [] and \
610 gvk and gvqk and gnqk and gik and gqnk
612 def test_nsattrs_empty():
613 return verify_empty_nsattrs(AttributesNSImpl({}, {}))
615 def test_nsattrs_wattr():
616 attrs = AttributesNSImpl({(ns_uri, "attr") : "val"},
617 {(ns_uri, "attr") : "ns:attr"})
619 return attrs.getLength() == 1 and \
620 attrs.getNames() == [(ns_uri, "attr")] and \
621 attrs.getQNames() == ["ns:attr"] and \
622 len(attrs) == 1 and \
623 attrs.has_key((ns_uri, "attr")) and \
624 attrs.keys() == [(ns_uri, "attr")] and \
625 attrs.get((ns_uri, "attr")) == "val" and \
626 attrs.get((ns_uri, "attr"), 25) == "val" and \
627 attrs.items() == [((ns_uri, "attr"), "val")] and \
628 attrs.values() == ["val"] and \
629 attrs.getValue((ns_uri, "attr")) == "val" and \
630 attrs.getValueByQName("ns:attr") == "val" and \
631 attrs.getNameByQName("ns:attr") == (ns_uri, "attr") and \
632 attrs[(ns_uri, "attr")] == "val" and \
633 attrs.getQNameByName((ns_uri, "attr")) == "ns:attr"
636 # ===== Main program
638 def make_test_output():
639 parser = create_parser()
640 result = StringIO()
641 xmlgen = XMLGenerator(result)
643 parser.setContentHandler(xmlgen)
644 parser.parse(findfile("test"+os.extsep+"xml"))
646 outf = open(findfile("test"+os.extsep+"xml"+os.extsep+"out"), "w")
647 outf.write(result.getvalue())
648 outf.close()
650 items = locals().items()
651 items.sort()
652 for (name, value) in items:
653 if name[ : 5] == "test_":
654 confirm(value(), name)
656 if verbose:
657 print "%d tests, %d failures" % (tests, len(failures))
658 if failures:
659 raise TestFailed("%d of %d tests failed: %s"
660 % (len(failures), tests, ", ".join(failures)))