# regression test for SAX 2.0 -*- coding: iso-8859-1 -*-
# $Id: test_sax.py,v 1.27 2004/08/03 10:17:34 mwh Exp $
from xml
.sax
import make_parser
, ContentHandler
, \
SAXException
, SAXReaderNotAvailable
, SAXParseException
except SAXReaderNotAvailable
:
# don't try to test this module if we cannot create a parser
raise ImportError("no XML parsers available")
from xml
.sax
.saxutils
import XMLGenerator
, escape
, unescape
, quoteattr
, \
from xml
.sax
.expatreader
import create_parser
from xml
.sax
.xmlreader
import InputSource
, AttributesImpl
, AttributesNSImpl
from cStringIO
import StringIO
from test
.test_support
import verify
, verbose
, TestFailed
, findfile
def confirm(outcome
, name
):
# Creating parsers several times in a row should succeed.
# Testing this because there have been failures of this kind
from xml
.sax
import make_parser
from xml
.sax
import make_parser
from xml
.sax
import make_parser
from xml
.sax
import make_parser
from xml
.sax
import make_parser
from xml
.sax
import make_parser
# ===========================================================================
# ===========================================================================
return escape("Donald Duck & Co") == "Donald Duck & Co"
return escape("<Donald Duck & Co>") == "<Donald Duck & Co>"
return escape("Hei på deg", {"å" : "å"}) == "Hei på deg"
def test_unescape_basic():
return unescape("Donald Duck & Co") == "Donald Duck & Co"
return unescape("<Donald Duck & Co>") == "<Donald Duck & Co>"
def test_unescape_extra():
return unescape("Hei på deg", {"å" : "å"}) == "Hei på deg"
def test_unescape_amp_extra():
return unescape("&foo;", {"&foo;": "splat"}) == "&foo;"
def test_quoteattr_basic():
return quoteattr("Donald Duck & Co") == '"Donald Duck & Co"'
def test_single_quoteattr():
return (quoteattr('Includes "double" quotes')
== '\'Includes "double" quotes\'')
def test_double_quoteattr():
return (quoteattr("Includes 'single' quotes")
== "\"Includes 'single' quotes\"")
def test_single_double_quoteattr():
return (quoteattr("Includes 'single' and \"double\" quotes")
== "\"Includes 'single' and "double" quotes\"")
# Creating a parser should succeed - it should fall back
p
= make_parser(['xml.parsers.no_such_parser'])
start
= '<?xml version="1.0" encoding="iso-8859-1"?>\n'
gen
= XMLGenerator(result
)
gen
.startElement("doc", {})
return result
.getvalue() == start
+ "<doc></doc>"
def test_xmlgen_content():
gen
= XMLGenerator(result
)
gen
.startElement("doc", {})
return result
.getvalue() == start
+ "<doc>huhei</doc>"
gen
= XMLGenerator(result
)
gen
.processingInstruction("test", "data")
gen
.startElement("doc", {})
return result
.getvalue() == start
+ "<?test data?><doc></doc>"
def test_xmlgen_content_escape():
gen
= XMLGenerator(result
)
gen
.startElement("doc", {})
gen
.characters("<huhei&")
return result
.getvalue() == start
+ "<doc><huhei&</doc>"
def test_xmlgen_attr_escape():
gen
= XMLGenerator(result
)
gen
.startElement("doc", {"a": '"'})
gen
.startElement("e", {"a": "'"})
gen
.startElement("e", {"a": "'\""})
return result
.getvalue() == start \
+ "<doc a='\"'><e a=\"'\"></e><e a=\"'"\"></e></doc>"
def test_xmlgen_ignorable():
gen
= XMLGenerator(result
)
gen
.startElement("doc", {})
gen
.ignorableWhitespace(" ")
return result
.getvalue() == start
+ "<doc> </doc>"
ns_uri
= "http://www.python.org/xml-ns/saxtest/"
gen
= XMLGenerator(result
)
gen
.startPrefixMapping("ns1", ns_uri
)
gen
.startElementNS((ns_uri
, "doc"), "ns1:doc", {})
# add an unqualified name
gen
.startElementNS((None, "udoc"), None, {})
gen
.endElementNS((None, "udoc"), None)
gen
.endElementNS((ns_uri
, "doc"), "ns1:doc")
gen
.endPrefixMapping("ns1")
return result
.getvalue() == start
+ \
('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' %
gen
= XMLGenerator(result
)
filter.setContentHandler(gen
)
filter.startElement("doc", {})
filter.characters("content")
filter.ignorableWhitespace(" ")
return result
.getvalue() == start
+ "<doc>content </doc>"
# ===========================================================================
# ===========================================================================
# ===== XMLReader support
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
parser
.parse(open(findfile("test"+os
.extsep
+"xml")))
return result
.getvalue() == xml_test_out
# ===== DTDHandler support
def notationDecl(self
, name
, publicId
, systemId
):
self
._notations
.append((name
, publicId
, systemId
))
def unparsedEntityDecl(self
, name
, publicId
, systemId
, ndata
):
self
._entities
.append((name
, publicId
, systemId
, ndata
))
def test_expat_dtdhandler():
handler
= TestDTDHandler()
parser
.setDTDHandler(handler
)
parser
.feed('<!DOCTYPE doc [\n')
parser
.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n')
parser
.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n')
parser
.feed('<doc></doc>')
return handler
._notations
== [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)] and \
handler
._entities
== [("img", None, "expat.gif", "GIF")]
# ===== EntityResolver support
class TestEntityResolver
:
def resolveEntity(self
, publicId
, systemId
):
inpsrc
.setByteStream(StringIO("<entity/>"))
def test_expat_entityresolver():
parser
.setEntityResolver(TestEntityResolver())
parser
.setContentHandler(XMLGenerator(result
))
parser
.feed('<!DOCTYPE doc [\n')
parser
.feed(' <!ENTITY test SYSTEM "whatever">\n')
parser
.feed('<doc>&test;</doc>')
return result
.getvalue() == start
+ "<doc><entity></entity></doc>"
# ===== Attributes support
class AttrGatherer(ContentHandler
):
def startElement(self
, name
, attrs
):
def startElementNS(self
, name
, qname
, attrs
):
def test_expat_attrs_empty():
parser
.setContentHandler(gather
)
return verify_empty_attrs(gather
._attrs
)
def test_expat_attrs_wattr():
parser
.setContentHandler(gather
)
parser
.feed("<doc attr='val'/>")
return verify_attrs_wattr(gather
._attrs
)
def test_expat_nsattrs_empty():
parser
= create_parser(1)
parser
.setContentHandler(gather
)
return verify_empty_nsattrs(gather
._attrs
)
def test_expat_nsattrs_wattr():
parser
= create_parser(1)
parser
.setContentHandler(gather
)
parser
.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri
)
return attrs
.getLength() == 1 and \
attrs
.getNames() == [(ns_uri
, "attr")] and \
(attrs
.getQNames() == [] or attrs
.getQNames() == ["ns:attr"]) and \
attrs
.has_key((ns_uri
, "attr")) and \
attrs
.keys() == [(ns_uri
, "attr")] and \
attrs
.get((ns_uri
, "attr")) == "val" and \
attrs
.get((ns_uri
, "attr"), 25) == "val" and \
attrs
.items() == [((ns_uri
, "attr"), "val")] and \
attrs
.values() == ["val"] and \
attrs
.getValue((ns_uri
, "attr")) == "val" and \
attrs
[(ns_uri
, "attr")] == "val"
# ===== InputSource support
xml_test_out
= open(findfile("test"+os
.extsep
+"xml"+os
.extsep
+"out")).read()
def test_expat_inpsource_filename():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
parser
.parse(findfile("test"+os
.extsep
+"xml"))
return result
.getvalue() == xml_test_out
def test_expat_inpsource_sysid():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
parser
.parse(InputSource(findfile("test"+os
.extsep
+"xml")))
return result
.getvalue() == xml_test_out
def test_expat_inpsource_stream():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
inpsrc
.setByteStream(open(findfile("test"+os
.extsep
+"xml")))
return result
.getvalue() == xml_test_out
# ===== IncrementalParser support
def test_expat_incremental():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
return result
.getvalue() == start
+ "<doc></doc>"
def test_expat_incremental_reset():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
return result
.getvalue() == start
+ "<doc>text</doc>"
def test_expat_locator_noinfo():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
return parser
.getSystemId() is None and \
parser
.getPublicId() is None and \
parser
.getLineNumber() == 1
def test_expat_locator_withinfo():
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
parser
.parse(findfile("test.xml"))
return parser
.getSystemId() == findfile("test.xml") and \
parser
.getPublicId() is None
# ===========================================================================
# ===========================================================================
def test_expat_inpsource_location():
parser
.setContentHandler(ContentHandler()) # do nothing
source
.setByteStream(StringIO("<foo bar foobar>")) #ill-formed
return e
.getSystemId() == name
def test_expat_incomplete():
parser
.setContentHandler(ContentHandler()) # do nothing
parser
.parse(StringIO("<foo>"))
except SAXParseException
:
return 1 # ok, error found
def test_sax_parse_exception_str():
# pass various values from a locator to the SAXParseException to
# make sure that the __str__() doesn't fall apart when None is
# passed instead of an integer line and column number
# use "normal" values for the locator:
str(SAXParseException("message", None,
# use None for the line number:
str(SAXParseException("message", None,
# use None for the column number:
str(SAXParseException("message", None,
str(SAXParseException("message", None,
DummyLocator(None, None)))
def __init__(self
, lineno
, colno
):
def getColumnNumber(self
):
# ===========================================================================
# ===========================================================================
def verify_empty_attrs(attrs
):
attrs
.getValueByQName("attr")
attrs
.getNameByQName("attr")
attrs
.getQNameByName("attr")
return attrs
.getLength() == 0 and \
attrs
.getNames() == [] and \
attrs
.getQNames() == [] and \
not attrs
.has_key("attr") and \
attrs
.get("attrs") is None and \
attrs
.get("attrs", 25) == 25 and \
attrs
.items() == [] and \
attrs
.values() == [] and \
gvk
and gvqk
and gnqk
and gik
and gqnk
def verify_attrs_wattr(attrs
):
return attrs
.getLength() == 1 and \
attrs
.getNames() == ["attr"] and \
attrs
.getQNames() == ["attr"] and \
attrs
.has_key("attr") and \
attrs
.keys() == ["attr"] and \
attrs
.get("attr") == "val" and \
attrs
.get("attr", 25) == "val" and \
attrs
.items() == [("attr", "val")] and \
attrs
.values() == ["val"] and \
attrs
.getValue("attr") == "val" and \
attrs
.getValueByQName("attr") == "val" and \
attrs
.getNameByQName("attr") == "attr" and \
attrs
["attr"] == "val" and \
attrs
.getQNameByName("attr") == "attr"
return verify_empty_attrs(AttributesImpl({}))
return verify_attrs_wattr(AttributesImpl({"attr" : "val"}))
def verify_empty_nsattrs(attrs
):
attrs
.getValue((ns_uri
, "attr"))
attrs
.getValueByQName("ns:attr")
attrs
.getNameByQName("ns:attr")
attrs
.getQNameByName((ns_uri
, "attr"))
return attrs
.getLength() == 0 and \
attrs
.getNames() == [] and \
attrs
.getQNames() == [] and \
not attrs
.has_key((ns_uri
, "attr")) and \
attrs
.get((ns_uri
, "attr")) is None and \
attrs
.get((ns_uri
, "attr"), 25) == 25 and \
attrs
.items() == [] and \
attrs
.values() == [] and \
gvk
and gvqk
and gnqk
and gik
and gqnk
def test_nsattrs_empty():
return verify_empty_nsattrs(AttributesNSImpl({}, {}))
def test_nsattrs_wattr():
attrs
= AttributesNSImpl({(ns_uri
, "attr") : "val"},
{(ns_uri
, "attr") : "ns:attr"})
return attrs
.getLength() == 1 and \
attrs
.getNames() == [(ns_uri
, "attr")] and \
attrs
.getQNames() == ["ns:attr"] and \
attrs
.has_key((ns_uri
, "attr")) and \
attrs
.keys() == [(ns_uri
, "attr")] and \
attrs
.get((ns_uri
, "attr")) == "val" and \
attrs
.get((ns_uri
, "attr"), 25) == "val" and \
attrs
.items() == [((ns_uri
, "attr"), "val")] and \
attrs
.values() == ["val"] and \
attrs
.getValue((ns_uri
, "attr")) == "val" and \
attrs
.getValueByQName("ns:attr") == "val" and \
attrs
.getNameByQName("ns:attr") == (ns_uri
, "attr") and \
attrs
[(ns_uri
, "attr")] == "val" and \
attrs
.getQNameByName((ns_uri
, "attr")) == "ns:attr"
xmlgen
= XMLGenerator(result
)
parser
.setContentHandler(xmlgen
)
parser
.parse(findfile("test"+os
.extsep
+"xml"))
outf
= open(findfile("test"+os
.extsep
+"xml"+os
.extsep
+"out"), "w")
outf
.write(result
.getvalue())
for (name
, value
) in items
:
if name
[ : 5] == "test_":
# We delete the items variable so that the assignment to items above
# doesn't pick up the old value of items (which messes with attempts
# to find reference leaks).
print "%d tests, %d failures" % (tests
, len(failures
))
raise TestFailed("%d of %d tests failed: %s"
% (len(failures
), tests
, ", ".join(failures
)))