blob: 87a8658672b83e00ec57cb4bea96583bdf8dbd2f [file] [log] [blame]
Antoine Pitroub27ddc72010-10-27 18:58:04 +00001# regression test for SAX 2.0 -*- coding: utf-8 -*-
Lars Gustäbel96753b32000-09-24 12:24:24 +00002# $Id$
3
Fred Drakefbdeaad2006-07-29 16:56:15 +00004from xml.sax import make_parser, ContentHandler, \
Victor Stinnerd81f9e22017-05-05 10:11:55 +02005 SAXException, SAXReaderNotAvailable, SAXParseException, \
6 saxutils
Martin v. Löwis962c9e72000-10-06 17:41:52 +00007try:
8 make_parser()
Martin v. Löwis80670bc2000-10-06 21:13:23 +00009except SAXReaderNotAvailable:
Martin v. Löwis962c9e72000-10-06 17:41:52 +000010 # don't try to test this module if we cannot create a parser
11 raise ImportError("no XML parsers available")
Fred Drakefbdeaad2006-07-29 16:56:15 +000012from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \
Serhiy Storchakae9d4dc12015-04-02 20:55:46 +030013 XMLFilterBase, prepare_input_source
Fred Drakefbdeaad2006-07-29 16:56:15 +000014from xml.sax.expatreader import create_parser
Antoine Pitrou7f081022010-10-27 18:43:21 +000015from xml.sax.handler import feature_namespaces
Fred Drakefbdeaad2006-07-29 16:56:15 +000016from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl
Lars Gustäbel96753b32000-09-24 12:24:24 +000017from cStringIO import StringIO
Serhiy Storchakaf8980382013-02-10 14:26:08 +020018import io
Serhiy Storchakaaff77f32015-04-02 23:05:23 +030019import gc
Serhiy Storchaka23298cb2013-02-02 12:16:22 +020020import os.path
Serhiy Storchaka8673ab92013-02-02 10:28:30 +020021import shutil
22import test.test_support as support
Serhiy Storchakaaff77f32015-04-02 23:05:23 +030023from test.test_support import findfile, run_unittest, TESTFN
Collin Winterd28fcbc2007-03-28 23:34:06 +000024import unittest
Lars Gustäbel96753b32000-09-24 12:24:24 +000025
Florent Xicluna1b51c3d2010-03-13 12:41:48 +000026TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata")
27TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata")
Florent Xicluna13ba1a12010-03-13 11:18:49 +000028
Serhiy Storchaka23298cb2013-02-02 12:16:22 +020029supports_unicode_filenames = True
30if not os.path.supports_unicode_filenames:
31 try:
32 support.TESTFN_UNICODE.encode(support.TESTFN_ENCODING)
33 except (AttributeError, UnicodeError, TypeError):
34 # Either the file system encoding is None, or the file name
35 # cannot be encoded in the file system encoding.
36 supports_unicode_filenames = False
37requires_unicode_filenames = unittest.skipUnless(
38 supports_unicode_filenames,
39 'Requires unicode filenames support')
40
Collin Winterd28fcbc2007-03-28 23:34:06 +000041ns_uri = "http://www.python.org/xml-ns/saxtest/"
Lars Gustäbel96753b32000-09-24 12:24:24 +000042
Collin Winterd28fcbc2007-03-28 23:34:06 +000043class XmlTestBase(unittest.TestCase):
44 def verify_empty_attrs(self, attrs):
45 self.assertRaises(KeyError, attrs.getValue, "attr")
46 self.assertRaises(KeyError, attrs.getValueByQName, "attr")
47 self.assertRaises(KeyError, attrs.getNameByQName, "attr")
48 self.assertRaises(KeyError, attrs.getQNameByName, "attr")
49 self.assertRaises(KeyError, attrs.__getitem__, "attr")
Ezio Melotti2623a372010-11-21 13:34:58 +000050 self.assertEqual(attrs.getLength(), 0)
51 self.assertEqual(attrs.getNames(), [])
52 self.assertEqual(attrs.getQNames(), [])
53 self.assertEqual(len(attrs), 0)
Collin Winterd28fcbc2007-03-28 23:34:06 +000054 self.assertFalse(attrs.has_key("attr"))
Ezio Melotti2623a372010-11-21 13:34:58 +000055 self.assertEqual(attrs.keys(), [])
56 self.assertEqual(attrs.get("attrs"), None)
57 self.assertEqual(attrs.get("attrs", 25), 25)
58 self.assertEqual(attrs.items(), [])
59 self.assertEqual(attrs.values(), [])
Neal Norwitz0d4c06e2007-04-25 06:30:05 +000060
Collin Winterd28fcbc2007-03-28 23:34:06 +000061 def verify_empty_nsattrs(self, attrs):
62 self.assertRaises(KeyError, attrs.getValue, (ns_uri, "attr"))
63 self.assertRaises(KeyError, attrs.getValueByQName, "ns:attr")
64 self.assertRaises(KeyError, attrs.getNameByQName, "ns:attr")
65 self.assertRaises(KeyError, attrs.getQNameByName, (ns_uri, "attr"))
66 self.assertRaises(KeyError, attrs.__getitem__, (ns_uri, "attr"))
Ezio Melotti2623a372010-11-21 13:34:58 +000067 self.assertEqual(attrs.getLength(), 0)
68 self.assertEqual(attrs.getNames(), [])
69 self.assertEqual(attrs.getQNames(), [])
70 self.assertEqual(len(attrs), 0)
Collin Winterd28fcbc2007-03-28 23:34:06 +000071 self.assertFalse(attrs.has_key((ns_uri, "attr")))
Ezio Melotti2623a372010-11-21 13:34:58 +000072 self.assertEqual(attrs.keys(), [])
73 self.assertEqual(attrs.get((ns_uri, "attr")), None)
74 self.assertEqual(attrs.get((ns_uri, "attr"), 25), 25)
75 self.assertEqual(attrs.items(), [])
76 self.assertEqual(attrs.values(), [])
Lars Gustäbel96753b32000-09-24 12:24:24 +000077
Collin Winterd28fcbc2007-03-28 23:34:06 +000078 def verify_attrs_wattr(self, attrs):
Ezio Melotti2623a372010-11-21 13:34:58 +000079 self.assertEqual(attrs.getLength(), 1)
80 self.assertEqual(attrs.getNames(), ["attr"])
81 self.assertEqual(attrs.getQNames(), ["attr"])
82 self.assertEqual(len(attrs), 1)
Collin Winterd28fcbc2007-03-28 23:34:06 +000083 self.assertTrue(attrs.has_key("attr"))
Ezio Melotti2623a372010-11-21 13:34:58 +000084 self.assertEqual(attrs.keys(), ["attr"])
85 self.assertEqual(attrs.get("attr"), "val")
86 self.assertEqual(attrs.get("attr", 25), "val")
87 self.assertEqual(attrs.items(), [("attr", "val")])
88 self.assertEqual(attrs.values(), ["val"])
89 self.assertEqual(attrs.getValue("attr"), "val")
90 self.assertEqual(attrs.getValueByQName("attr"), "val")
91 self.assertEqual(attrs.getNameByQName("attr"), "attr")
92 self.assertEqual(attrs["attr"], "val")
93 self.assertEqual(attrs.getQNameByName("attr"), "attr")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +000094
Serhiy Storchakaaff77f32015-04-02 23:05:23 +030095
96def xml_unicode(doc, encoding=None):
97 if encoding is None:
98 return doc
99 return u'<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, doc)
100
101def xml_bytes(doc, encoding, decl_encoding=Ellipsis):
102 if decl_encoding is Ellipsis:
103 decl_encoding = encoding
104 return xml_unicode(doc, decl_encoding).encode(encoding, 'xmlcharrefreplace')
105
106def make_xml_file(doc, encoding, decl_encoding=Ellipsis):
107 if decl_encoding is Ellipsis:
108 decl_encoding = encoding
109 with io.open(TESTFN, 'w', encoding=encoding, errors='xmlcharrefreplace') as f:
110 f.write(xml_unicode(doc, decl_encoding))
111
112
113class ParseTest(unittest.TestCase):
114 data = support.u(r'<money value="$\xa3\u20ac\U0001017b">'
115 r'$\xa3\u20ac\U0001017b</money>')
116
117 def tearDown(self):
118 support.unlink(TESTFN)
119
120 def check_parse(self, f):
121 from xml.sax import parse
122 result = StringIO()
123 parse(f, XMLGenerator(result, 'utf-8'))
124 self.assertEqual(result.getvalue(), xml_bytes(self.data, 'utf-8'))
125
126 def test_parse_bytes(self):
127 # UTF-8 is default encoding, US-ASCII is compatible with UTF-8,
128 # UTF-16 is autodetected
129 encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be')
130 for encoding in encodings:
131 self.check_parse(io.BytesIO(xml_bytes(self.data, encoding)))
132 make_xml_file(self.data, encoding)
133 self.check_parse(TESTFN)
134 with io.open(TESTFN, 'rb') as f:
135 self.check_parse(f)
136 self.check_parse(io.BytesIO(xml_bytes(self.data, encoding, None)))
137 make_xml_file(self.data, encoding, None)
138 self.check_parse(TESTFN)
139 with io.open(TESTFN, 'rb') as f:
140 self.check_parse(f)
141 # accept UTF-8 with BOM
142 self.check_parse(io.BytesIO(xml_bytes(self.data, 'utf-8-sig', 'utf-8')))
143 make_xml_file(self.data, 'utf-8-sig', 'utf-8')
144 self.check_parse(TESTFN)
145 with io.open(TESTFN, 'rb') as f:
146 self.check_parse(f)
147 self.check_parse(io.BytesIO(xml_bytes(self.data, 'utf-8-sig', None)))
148 make_xml_file(self.data, 'utf-8-sig', None)
149 self.check_parse(TESTFN)
150 with io.open(TESTFN, 'rb') as f:
151 self.check_parse(f)
152 # accept data with declared encoding
153 self.check_parse(io.BytesIO(xml_bytes(self.data, 'iso-8859-1')))
154 make_xml_file(self.data, 'iso-8859-1')
155 self.check_parse(TESTFN)
156 with io.open(TESTFN, 'rb') as f:
157 self.check_parse(f)
158 # fail on non-UTF-8 incompatible data without declared encoding
159 with self.assertRaises(SAXException):
160 self.check_parse(io.BytesIO(xml_bytes(self.data, 'iso-8859-1', None)))
161 make_xml_file(self.data, 'iso-8859-1', None)
162 with self.assertRaises(SAXException):
163 self.check_parse(TESTFN)
164 with io.open(TESTFN, 'rb') as f:
165 with self.assertRaises(SAXException):
166 self.check_parse(f)
167
168 def test_parse_InputSource(self):
169 # accept data without declared but with explicitly specified encoding
170 make_xml_file(self.data, 'iso-8859-1', None)
171 with io.open(TESTFN, 'rb') as f:
172 input = InputSource()
173 input.setByteStream(f)
174 input.setEncoding('iso-8859-1')
175 self.check_parse(input)
176
Victor Stinnerd81f9e22017-05-05 10:11:55 +0200177 def test_parse_close_source(self):
178 builtin_open = open
179 non_local = {'fileobj': None}
180
181 def mock_open(*args):
182 fileobj = builtin_open(*args)
183 non_local['fileobj'] = fileobj
184 return fileobj
185
186 with support.swap_attr(saxutils, 'open', mock_open):
187 make_xml_file(self.data, 'iso-8859-1', None)
188 with self.assertRaises(SAXException):
189 self.check_parse(TESTFN)
190 self.assertTrue(non_local['fileobj'].closed)
191
Serhiy Storchakaaff77f32015-04-02 23:05:23 +0300192 def check_parseString(self, s):
193 from xml.sax import parseString
194 result = StringIO()
195 parseString(s, XMLGenerator(result, 'utf-8'))
196 self.assertEqual(result.getvalue(), xml_bytes(self.data, 'utf-8'))
197
198 def test_parseString_bytes(self):
199 # UTF-8 is default encoding, US-ASCII is compatible with UTF-8,
200 # UTF-16 is autodetected
201 encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be')
202 for encoding in encodings:
203 self.check_parseString(xml_bytes(self.data, encoding))
204 self.check_parseString(xml_bytes(self.data, encoding, None))
205 # accept UTF-8 with BOM
206 self.check_parseString(xml_bytes(self.data, 'utf-8-sig', 'utf-8'))
207 self.check_parseString(xml_bytes(self.data, 'utf-8-sig', None))
208 # accept data with declared encoding
209 self.check_parseString(xml_bytes(self.data, 'iso-8859-1'))
210 # fail on non-UTF-8 incompatible data without declared encoding
211 with self.assertRaises(SAXException):
212 self.check_parseString(xml_bytes(self.data, 'iso-8859-1', None))
213
214
Collin Winterd28fcbc2007-03-28 23:34:06 +0000215class MakeParserTest(unittest.TestCase):
216 def test_make_parser2(self):
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000217 # Creating parsers several times in a row should succeed.
218 # Testing this because there have been failures of this kind
219 # before.
Fred Drakefbdeaad2006-07-29 16:56:15 +0000220 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000221 p = make_parser()
Fred Drakefbdeaad2006-07-29 16:56:15 +0000222 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000223 p = make_parser()
Fred Drakefbdeaad2006-07-29 16:56:15 +0000224 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000225 p = make_parser()
Fred Drakefbdeaad2006-07-29 16:56:15 +0000226 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000227 p = make_parser()
Fred Drakefbdeaad2006-07-29 16:56:15 +0000228 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000229 p = make_parser()
Fred Drakefbdeaad2006-07-29 16:56:15 +0000230 from xml.sax import make_parser
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000231 p = make_parser()
Tim Petersd2bf3b72001-01-18 02:22:22 +0000232
233
Lars Gustäbel96753b32000-09-24 12:24:24 +0000234# ===========================================================================
235#
236# saxutils tests
237#
238# ===========================================================================
239
Collin Winterd28fcbc2007-03-28 23:34:06 +0000240class SaxutilsTest(unittest.TestCase):
241 # ===== escape
242 def test_escape_basic(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000243 self.assertEqual(escape("Donald Duck & Co"), "Donald Duck &amp; Co")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000244
Collin Winterd28fcbc2007-03-28 23:34:06 +0000245 def test_escape_all(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000246 self.assertEqual(escape("<Donald Duck & Co>"),
247 "&lt;Donald Duck &amp; Co&gt;")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000248
Collin Winterd28fcbc2007-03-28 23:34:06 +0000249 def test_escape_extra(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000250 self.assertEqual(escape("Hei på deg", {"å" : "&aring;"}),
251 "Hei p&aring; deg")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000252
Collin Winterd28fcbc2007-03-28 23:34:06 +0000253 # ===== unescape
254 def test_unescape_basic(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000255 self.assertEqual(unescape("Donald Duck &amp; Co"), "Donald Duck & Co")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000256
Collin Winterd28fcbc2007-03-28 23:34:06 +0000257 def test_unescape_all(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000258 self.assertEqual(unescape("&lt;Donald Duck &amp; Co&gt;"),
259 "<Donald Duck & Co>")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000260
Collin Winterd28fcbc2007-03-28 23:34:06 +0000261 def test_unescape_extra(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000262 self.assertEqual(unescape("Hei på deg", {"å" : "&aring;"}),
263 "Hei p&aring; deg")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000264
Collin Winterd28fcbc2007-03-28 23:34:06 +0000265 def test_unescape_amp_extra(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000266 self.assertEqual(unescape("&amp;foo;", {"&foo;": "splat"}), "&foo;")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000267
Collin Winterd28fcbc2007-03-28 23:34:06 +0000268 # ===== quoteattr
269 def test_quoteattr_basic(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000270 self.assertEqual(quoteattr("Donald Duck & Co"),
271 '"Donald Duck &amp; Co"')
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000272
Collin Winterd28fcbc2007-03-28 23:34:06 +0000273 def test_single_quoteattr(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000274 self.assertEqual(quoteattr('Includes "double" quotes'),
275 '\'Includes "double" quotes\'')
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000276
Collin Winterd28fcbc2007-03-28 23:34:06 +0000277 def test_double_quoteattr(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000278 self.assertEqual(quoteattr("Includes 'single' quotes"),
279 "\"Includes 'single' quotes\"")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000280
Collin Winterd28fcbc2007-03-28 23:34:06 +0000281 def test_single_double_quoteattr(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000282 self.assertEqual(quoteattr("Includes 'single' and \"double\" quotes"),
283 "\"Includes 'single' and &quot;double&quot; quotes\"")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000284
Collin Winterd28fcbc2007-03-28 23:34:06 +0000285 # ===== make_parser
286 def test_make_parser(self):
Martin v. Löwis962c9e72000-10-06 17:41:52 +0000287 # Creating a parser should succeed - it should fall back
288 # to the expatreader
Fred Drakefbdeaad2006-07-29 16:56:15 +0000289 p = make_parser(['xml.parsers.no_such_parser'])
Martin v. Löwis962c9e72000-10-06 17:41:52 +0000290
291
Serhiy Storchakae9d4dc12015-04-02 20:55:46 +0300292class PrepareInputSourceTest(unittest.TestCase):
293
294 def setUp(self):
295 self.file = support.TESTFN
296 with open(self.file, "w") as tmp:
297 tmp.write("This was read from a file.")
298
299 def tearDown(self):
300 support.unlink(self.file)
301
302 def make_byte_stream(self):
303 return io.BytesIO(b"This is a byte stream.")
304
305 def checkContent(self, stream, content):
306 self.assertIsNotNone(stream)
307 self.assertEqual(stream.read(), content)
308 stream.close()
309
310
311 def test_byte_stream(self):
312 # If the source is an InputSource that does not have a character
313 # stream but does have a byte stream, use the byte stream.
314 src = InputSource(self.file)
315 src.setByteStream(self.make_byte_stream())
316 prep = prepare_input_source(src)
317 self.assertIsNone(prep.getCharacterStream())
318 self.checkContent(prep.getByteStream(),
319 b"This is a byte stream.")
320
321 def test_system_id(self):
322 # If the source is an InputSource that has neither a character
323 # stream nor a byte stream, open the system ID.
324 src = InputSource(self.file)
325 prep = prepare_input_source(src)
326 self.assertIsNone(prep.getCharacterStream())
327 self.checkContent(prep.getByteStream(),
328 b"This was read from a file.")
329
330 def test_string(self):
331 # If the source is a string, use it as a system ID and open it.
332 prep = prepare_input_source(self.file)
333 self.assertIsNone(prep.getCharacterStream())
334 self.checkContent(prep.getByteStream(),
335 b"This was read from a file.")
336
337 def test_binary_file(self):
338 # If the source is a binary file-like object, use it as a byte
339 # stream.
340 prep = prepare_input_source(self.make_byte_stream())
341 self.assertIsNone(prep.getCharacterStream())
342 self.checkContent(prep.getByteStream(),
343 b"This is a byte stream.")
344
345
Lars Gustäbel96753b32000-09-24 12:24:24 +0000346# ===== XMLGenerator
347
348start = '<?xml version="1.0" encoding="iso-8859-1"?>\n'
349
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200350class XmlgenTest:
Collin Winterd28fcbc2007-03-28 23:34:06 +0000351 def test_xmlgen_basic(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200352 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000353 gen = XMLGenerator(result)
354 gen.startDocument()
355 gen.startElement("doc", {})
356 gen.endElement("doc")
357 gen.endDocument()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000358
Ezio Melotti2623a372010-11-21 13:34:58 +0000359 self.assertEqual(result.getvalue(), start + "<doc></doc>")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000360
Collin Winterd28fcbc2007-03-28 23:34:06 +0000361 def test_xmlgen_content(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200362 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000363 gen = XMLGenerator(result)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000364
Collin Winterd28fcbc2007-03-28 23:34:06 +0000365 gen.startDocument()
366 gen.startElement("doc", {})
367 gen.characters("huhei")
368 gen.endElement("doc")
369 gen.endDocument()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000370
Ezio Melotti2623a372010-11-21 13:34:58 +0000371 self.assertEqual(result.getvalue(), start + "<doc>huhei</doc>")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000372
Collin Winterd28fcbc2007-03-28 23:34:06 +0000373 def test_xmlgen_pi(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200374 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000375 gen = XMLGenerator(result)
Lars Gustäbel96753b32000-09-24 12:24:24 +0000376
Collin Winterd28fcbc2007-03-28 23:34:06 +0000377 gen.startDocument()
378 gen.processingInstruction("test", "data")
379 gen.startElement("doc", {})
380 gen.endElement("doc")
381 gen.endDocument()
Fred Drake004d5e62000-10-23 17:22:08 +0000382
Ezio Melotti2623a372010-11-21 13:34:58 +0000383 self.assertEqual(result.getvalue(), start + "<?test data?><doc></doc>")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000384
Collin Winterd28fcbc2007-03-28 23:34:06 +0000385 def test_xmlgen_content_escape(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200386 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000387 gen = XMLGenerator(result)
Lars Gustäbel96753b32000-09-24 12:24:24 +0000388
Collin Winterd28fcbc2007-03-28 23:34:06 +0000389 gen.startDocument()
390 gen.startElement("doc", {})
391 gen.characters("<huhei&")
392 gen.endElement("doc")
393 gen.endDocument()
Fred Drake004d5e62000-10-23 17:22:08 +0000394
Ezio Melotti2623a372010-11-21 13:34:58 +0000395 self.assertEqual(result.getvalue(),
Collin Winterd28fcbc2007-03-28 23:34:06 +0000396 start + "<doc>&lt;huhei&amp;</doc>")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000397
Collin Winterd28fcbc2007-03-28 23:34:06 +0000398 def test_xmlgen_attr_escape(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200399 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000400 gen = XMLGenerator(result)
Lars Gustäbel96753b32000-09-24 12:24:24 +0000401
Collin Winterd28fcbc2007-03-28 23:34:06 +0000402 gen.startDocument()
403 gen.startElement("doc", {"a": '"'})
404 gen.startElement("e", {"a": "'"})
405 gen.endElement("e")
406 gen.startElement("e", {"a": "'\""})
407 gen.endElement("e")
408 gen.startElement("e", {"a": "\n\r\t"})
409 gen.endElement("e")
410 gen.endElement("doc")
411 gen.endDocument()
Fred Drake004d5e62000-10-23 17:22:08 +0000412
Ezio Melotti2623a372010-11-21 13:34:58 +0000413 self.assertEqual(result.getvalue(), start +
Collin Winterd28fcbc2007-03-28 23:34:06 +0000414 ("<doc a='\"'><e a=\"'\"></e>"
415 "<e a=\"'&quot;\"></e>"
416 "<e a=\"&#10;&#13;&#9;\"></e></doc>"))
Lars Gustäbel96753b32000-09-24 12:24:24 +0000417
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200418 def test_xmlgen_encoding(self):
419 encodings = ('iso-8859-15', 'utf-8',
420 'utf-16be', 'utf-16le',
421 'utf-32be', 'utf-32le')
422 for encoding in encodings:
423 result = self.ioclass()
424 gen = XMLGenerator(result, encoding=encoding)
425
426 gen.startDocument()
427 gen.startElement("doc", {"a": u'\u20ac'})
428 gen.characters(u"\u20ac")
429 gen.endElement("doc")
430 gen.endDocument()
431
432 self.assertEqual(result.getvalue(), (
433 u'<?xml version="1.0" encoding="%s"?>\n'
434 u'<doc a="\u20ac">\u20ac</doc>' % encoding
435 ).encode(encoding, 'xmlcharrefreplace'))
436
437 def test_xmlgen_unencodable(self):
438 result = self.ioclass()
439 gen = XMLGenerator(result, encoding='ascii')
440
441 gen.startDocument()
442 gen.startElement("doc", {"a": u'\u20ac'})
443 gen.characters(u"\u20ac")
444 gen.endElement("doc")
445 gen.endDocument()
446
447 self.assertEqual(result.getvalue(),
448 '<?xml version="1.0" encoding="ascii"?>\n'
449 '<doc a="&#8364;">&#8364;</doc>')
450
Collin Winterd28fcbc2007-03-28 23:34:06 +0000451 def test_xmlgen_ignorable(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200452 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000453 gen = XMLGenerator(result)
Lars Gustäbel96753b32000-09-24 12:24:24 +0000454
Collin Winterd28fcbc2007-03-28 23:34:06 +0000455 gen.startDocument()
456 gen.startElement("doc", {})
457 gen.ignorableWhitespace(" ")
458 gen.endElement("doc")
459 gen.endDocument()
Fred Drakec9fadf92001-08-07 19:17:06 +0000460
Ezio Melotti2623a372010-11-21 13:34:58 +0000461 self.assertEqual(result.getvalue(), start + "<doc> </doc>")
Fred Drakec9fadf92001-08-07 19:17:06 +0000462
Serhiy Storchaka74239032013-05-12 17:29:34 +0300463 def test_xmlgen_encoding_bytes(self):
464 encodings = ('iso-8859-15', 'utf-8',
465 'utf-16be', 'utf-16le',
466 'utf-32be', 'utf-32le')
467 for encoding in encodings:
468 result = self.ioclass()
469 gen = XMLGenerator(result, encoding=encoding)
470
471 gen.startDocument()
472 gen.startElement("doc", {"a": u'\u20ac'})
473 gen.characters(u"\u20ac".encode(encoding))
474 gen.ignorableWhitespace(" ".encode(encoding))
475 gen.endElement("doc")
476 gen.endDocument()
477
478 self.assertEqual(result.getvalue(), (
479 u'<?xml version="1.0" encoding="%s"?>\n'
480 u'<doc a="\u20ac">\u20ac </doc>' % encoding
481 ).encode(encoding, 'xmlcharrefreplace'))
482
Collin Winterd28fcbc2007-03-28 23:34:06 +0000483 def test_xmlgen_ns(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200484 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000485 gen = XMLGenerator(result)
Fred Drakec9fadf92001-08-07 19:17:06 +0000486
Collin Winterd28fcbc2007-03-28 23:34:06 +0000487 gen.startDocument()
488 gen.startPrefixMapping("ns1", ns_uri)
489 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {})
490 # add an unqualified name
491 gen.startElementNS((None, "udoc"), None, {})
492 gen.endElementNS((None, "udoc"), None)
493 gen.endElementNS((ns_uri, "doc"), "ns1:doc")
494 gen.endPrefixMapping("ns1")
495 gen.endDocument()
Fred Drake004d5e62000-10-23 17:22:08 +0000496
Ezio Melotti2623a372010-11-21 13:34:58 +0000497 self.assertEqual(result.getvalue(), start + \
Martin v. Löwiscf0a1cc2000-10-03 22:35:29 +0000498 ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' %
Collin Winterd28fcbc2007-03-28 23:34:06 +0000499 ns_uri))
Lars Gustäbel96753b32000-09-24 12:24:24 +0000500
Collin Winterd28fcbc2007-03-28 23:34:06 +0000501 def test_1463026_1(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200502 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000503 gen = XMLGenerator(result)
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000504
Collin Winterd28fcbc2007-03-28 23:34:06 +0000505 gen.startDocument()
506 gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'})
507 gen.endElementNS((None, 'a'), 'a')
508 gen.endDocument()
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000509
Ezio Melotti2623a372010-11-21 13:34:58 +0000510 self.assertEqual(result.getvalue(), start+'<a b="c"></a>')
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000511
Collin Winterd28fcbc2007-03-28 23:34:06 +0000512 def test_1463026_2(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200513 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000514 gen = XMLGenerator(result)
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000515
Collin Winterd28fcbc2007-03-28 23:34:06 +0000516 gen.startDocument()
517 gen.startPrefixMapping(None, 'qux')
518 gen.startElementNS(('qux', 'a'), 'a', {})
519 gen.endElementNS(('qux', 'a'), 'a')
520 gen.endPrefixMapping(None)
521 gen.endDocument()
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000522
Ezio Melotti2623a372010-11-21 13:34:58 +0000523 self.assertEqual(result.getvalue(), start+'<a xmlns="qux"></a>')
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000524
Collin Winterd28fcbc2007-03-28 23:34:06 +0000525 def test_1463026_3(self):
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200526 result = self.ioclass()
Collin Winterd28fcbc2007-03-28 23:34:06 +0000527 gen = XMLGenerator(result)
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000528
Collin Winterd28fcbc2007-03-28 23:34:06 +0000529 gen.startDocument()
530 gen.startPrefixMapping('my', 'qux')
531 gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'})
532 gen.endElementNS(('qux', 'a'), 'a')
533 gen.endPrefixMapping('my')
534 gen.endDocument()
Martin v. Löwis2bad58f2007-02-12 12:21:10 +0000535
Ezio Melotti2623a372010-11-21 13:34:58 +0000536 self.assertEqual(result.getvalue(),
Collin Winterd28fcbc2007-03-28 23:34:06 +0000537 start+'<my:a xmlns:my="qux" b="c"></my:a>')
Tim Petersea5962f2007-03-12 18:07:52 +0000538
Antoine Pitrou7f081022010-10-27 18:43:21 +0000539 def test_5027_1(self):
540 # The xml prefix (as in xml:lang below) is reserved and bound by
541 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had
Andrew Svetlov4bb142b2012-12-18 21:27:37 +0200542 # a bug whereby a KeyError is raised because this namespace is missing
Antoine Pitrou7f081022010-10-27 18:43:21 +0000543 # from a dictionary.
544 #
545 # This test demonstrates the bug by parsing a document.
546 test_xml = StringIO(
547 '<?xml version="1.0"?>'
548 '<a:g1 xmlns:a="http://example.com/ns">'
549 '<a:g2 xml:lang="en">Hello</a:g2>'
550 '</a:g1>')
551
552 parser = make_parser()
553 parser.setFeature(feature_namespaces, True)
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200554 result = self.ioclass()
Antoine Pitrou7f081022010-10-27 18:43:21 +0000555 gen = XMLGenerator(result)
556 parser.setContentHandler(gen)
557 parser.parse(test_xml)
558
Ezio Melotti2623a372010-11-21 13:34:58 +0000559 self.assertEqual(result.getvalue(),
560 start + (
561 '<a:g1 xmlns:a="http://example.com/ns">'
562 '<a:g2 xml:lang="en">Hello</a:g2>'
563 '</a:g1>'))
Antoine Pitrou7f081022010-10-27 18:43:21 +0000564
565 def test_5027_2(self):
566 # The xml prefix (as in xml:lang below) is reserved and bound by
567 # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had
Andrew Svetlov4bb142b2012-12-18 21:27:37 +0200568 # a bug whereby a KeyError is raised because this namespace is missing
Antoine Pitrou7f081022010-10-27 18:43:21 +0000569 # from a dictionary.
570 #
571 # This test demonstrates the bug by direct manipulation of the
572 # XMLGenerator.
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200573 result = self.ioclass()
Antoine Pitrou7f081022010-10-27 18:43:21 +0000574 gen = XMLGenerator(result)
575
576 gen.startDocument()
577 gen.startPrefixMapping('a', 'http://example.com/ns')
578 gen.startElementNS(('http://example.com/ns', 'g1'), 'g1', {})
579 lang_attr = {('http://www.w3.org/XML/1998/namespace', 'lang'): 'en'}
580 gen.startElementNS(('http://example.com/ns', 'g2'), 'g2', lang_attr)
581 gen.characters('Hello')
582 gen.endElementNS(('http://example.com/ns', 'g2'), 'g2')
583 gen.endElementNS(('http://example.com/ns', 'g1'), 'g1')
584 gen.endPrefixMapping('a')
585 gen.endDocument()
586
Ezio Melotti2623a372010-11-21 13:34:58 +0000587 self.assertEqual(result.getvalue(),
588 start + (
589 '<a:g1 xmlns:a="http://example.com/ns">'
590 '<a:g2 xml:lang="en">Hello</a:g2>'
591 '</a:g1>'))
Antoine Pitrou7f081022010-10-27 18:43:21 +0000592
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200593 def test_no_close_file(self):
594 result = self.ioclass()
595 def func(out):
596 gen = XMLGenerator(out)
597 gen.startDocument()
598 gen.startElement("doc", {})
599 func(result)
600 self.assertFalse(result.closed)
601
Serhiy Storchaka93bfe7d2013-02-25 13:31:29 +0200602 def test_xmlgen_fragment(self):
603 result = self.ioclass()
604 gen = XMLGenerator(result)
605
606 # Don't call gen.startDocument()
607 gen.startElement("foo", {"a": "1.0"})
608 gen.characters("Hello")
609 gen.endElement("foo")
610 gen.startElement("bar", {"b": "2.0"})
611 gen.endElement("bar")
612 # Don't call gen.endDocument()
613
614 self.assertEqual(result.getvalue(),
615 '<foo a="1.0">Hello</foo><bar b="2.0"></bar>')
616
Serhiy Storchakaf8980382013-02-10 14:26:08 +0200617class StringXmlgenTest(XmlgenTest, unittest.TestCase):
618 ioclass = StringIO
619
620class BytesIOXmlgenTest(XmlgenTest, unittest.TestCase):
621 ioclass = io.BytesIO
622
623class WriterXmlgenTest(XmlgenTest, unittest.TestCase):
624 class ioclass(list):
625 write = list.append
626 closed = False
627
628 def getvalue(self):
629 return b''.join(self)
630
Lars Gustäbel96753b32000-09-24 12:24:24 +0000631
Collin Winterd28fcbc2007-03-28 23:34:06 +0000632class XMLFilterBaseTest(unittest.TestCase):
633 def test_filter_basic(self):
634 result = StringIO()
635 gen = XMLGenerator(result)
636 filter = XMLFilterBase()
637 filter.setContentHandler(gen)
Fred Drake004d5e62000-10-23 17:22:08 +0000638
Collin Winterd28fcbc2007-03-28 23:34:06 +0000639 filter.startDocument()
640 filter.startElement("doc", {})
641 filter.characters("content")
642 filter.ignorableWhitespace(" ")
643 filter.endElement("doc")
644 filter.endDocument()
Lars Gustäbel96753b32000-09-24 12:24:24 +0000645
Ezio Melotti2623a372010-11-21 13:34:58 +0000646 self.assertEqual(result.getvalue(), start + "<doc>content </doc>")
Lars Gustäbel96753b32000-09-24 12:24:24 +0000647
648# ===========================================================================
649#
650# expatreader tests
651#
652# ===========================================================================
653
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000654xml_test_out = open(TEST_XMLFILE_OUT).read()
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000655
Collin Winterd28fcbc2007-03-28 23:34:06 +0000656class ExpatReaderTest(XmlTestBase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000657
Collin Winterd28fcbc2007-03-28 23:34:06 +0000658 # ===== XMLReader support
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000659
Serhiy Storchakae9d4dc12015-04-02 20:55:46 +0300660 def test_expat_binary_file(self):
Collin Winterd28fcbc2007-03-28 23:34:06 +0000661 parser = create_parser()
662 result = StringIO()
663 xmlgen = XMLGenerator(result)
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000664
Collin Winterd28fcbc2007-03-28 23:34:06 +0000665 parser.setContentHandler(xmlgen)
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000666 parser.parse(open(TEST_XMLFILE))
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000667
Ezio Melotti2623a372010-11-21 13:34:58 +0000668 self.assertEqual(result.getvalue(), xml_test_out)
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000669
Serhiy Storchaka23298cb2013-02-02 12:16:22 +0200670 @requires_unicode_filenames
Serhiy Storchaka8673ab92013-02-02 10:28:30 +0200671 def test_expat_file_unicode(self):
672 fname = support.TESTFN_UNICODE
673 shutil.copyfile(TEST_XMLFILE, fname)
674 self.addCleanup(support.unlink, fname)
675
676 parser = create_parser()
677 result = StringIO()
678 xmlgen = XMLGenerator(result)
679
680 parser.setContentHandler(xmlgen)
681 parser.parse(open(fname))
682
683 self.assertEqual(result.getvalue(), xml_test_out)
684
Collin Winterd28fcbc2007-03-28 23:34:06 +0000685 # ===== DTDHandler support
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000686
Collin Winterd28fcbc2007-03-28 23:34:06 +0000687 class TestDTDHandler:
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000688
Collin Winterd28fcbc2007-03-28 23:34:06 +0000689 def __init__(self):
690 self._notations = []
691 self._entities = []
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000692
Collin Winterd28fcbc2007-03-28 23:34:06 +0000693 def notationDecl(self, name, publicId, systemId):
694 self._notations.append((name, publicId, systemId))
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000695
Collin Winterd28fcbc2007-03-28 23:34:06 +0000696 def unparsedEntityDecl(self, name, publicId, systemId, ndata):
697 self._entities.append((name, publicId, systemId, ndata))
Lars Gustäbelb7536d52000-09-24 18:53:56 +0000698
Collin Winterd28fcbc2007-03-28 23:34:06 +0000699 def test_expat_dtdhandler(self):
700 parser = create_parser()
701 handler = self.TestDTDHandler()
702 parser.setDTDHandler(handler)
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000703
Collin Winterd28fcbc2007-03-28 23:34:06 +0000704 parser.feed('<!DOCTYPE doc [\n')
705 parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n')
706 parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n')
707 parser.feed(']>\n')
708 parser.feed('<doc></doc>')
709 parser.close()
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000710
Ezio Melotti2623a372010-11-21 13:34:58 +0000711 self.assertEqual(handler._notations,
Collin Winterd28fcbc2007-03-28 23:34:06 +0000712 [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)])
Ezio Melotti2623a372010-11-21 13:34:58 +0000713 self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")])
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000714
Collin Winterd28fcbc2007-03-28 23:34:06 +0000715 # ===== EntityResolver support
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000716
Collin Winterd28fcbc2007-03-28 23:34:06 +0000717 class TestEntityResolver:
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000718
Collin Winterd28fcbc2007-03-28 23:34:06 +0000719 def resolveEntity(self, publicId, systemId):
720 inpsrc = InputSource()
721 inpsrc.setByteStream(StringIO("<entity/>"))
722 return inpsrc
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000723
Collin Winterd28fcbc2007-03-28 23:34:06 +0000724 def test_expat_entityresolver(self):
725 parser = create_parser()
726 parser.setEntityResolver(self.TestEntityResolver())
727 result = StringIO()
728 parser.setContentHandler(XMLGenerator(result))
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000729
Collin Winterd28fcbc2007-03-28 23:34:06 +0000730 parser.feed('<!DOCTYPE doc [\n')
731 parser.feed(' <!ENTITY test SYSTEM "whatever">\n')
732 parser.feed(']>\n')
733 parser.feed('<doc>&test;</doc>')
734 parser.close()
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000735
Ezio Melotti2623a372010-11-21 13:34:58 +0000736 self.assertEqual(result.getvalue(), start +
737 "<doc><entity></entity></doc>")
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000738
Collin Winterd28fcbc2007-03-28 23:34:06 +0000739 # ===== Attributes support
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000740
Collin Winterd28fcbc2007-03-28 23:34:06 +0000741 class AttrGatherer(ContentHandler):
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000742
Collin Winterd28fcbc2007-03-28 23:34:06 +0000743 def startElement(self, name, attrs):
744 self._attrs = attrs
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000745
Collin Winterd28fcbc2007-03-28 23:34:06 +0000746 def startElementNS(self, name, qname, attrs):
747 self._attrs = attrs
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000748
Collin Winterd28fcbc2007-03-28 23:34:06 +0000749 def test_expat_attrs_empty(self):
750 parser = create_parser()
751 gather = self.AttrGatherer()
752 parser.setContentHandler(gather)
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000753
Collin Winterd28fcbc2007-03-28 23:34:06 +0000754 parser.feed("<doc/>")
755 parser.close()
Lars Gustäbel2fc52942000-10-24 15:35:07 +0000756
Collin Winterd28fcbc2007-03-28 23:34:06 +0000757 self.verify_empty_attrs(gather._attrs)
Martin v. Löwis80670bc2000-10-06 21:13:23 +0000758
Collin Winterd28fcbc2007-03-28 23:34:06 +0000759 def test_expat_attrs_wattr(self):
760 parser = create_parser()
761 gather = self.AttrGatherer()
762 parser.setContentHandler(gather)
763
764 parser.feed("<doc attr='val'/>")
765 parser.close()
766
767 self.verify_attrs_wattr(gather._attrs)
768
769 def test_expat_nsattrs_empty(self):
770 parser = create_parser(1)
771 gather = self.AttrGatherer()
772 parser.setContentHandler(gather)
773
774 parser.feed("<doc/>")
775 parser.close()
776
777 self.verify_empty_nsattrs(gather._attrs)
778
779 def test_expat_nsattrs_wattr(self):
780 parser = create_parser(1)
781 gather = self.AttrGatherer()
782 parser.setContentHandler(gather)
783
784 parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri)
785 parser.close()
786
787 attrs = gather._attrs
788
Ezio Melotti2623a372010-11-21 13:34:58 +0000789 self.assertEqual(attrs.getLength(), 1)
790 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")])
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000791 self.assertTrue((attrs.getQNames() == [] or
Collin Winterd28fcbc2007-03-28 23:34:06 +0000792 attrs.getQNames() == ["ns:attr"]))
Ezio Melotti2623a372010-11-21 13:34:58 +0000793 self.assertEqual(len(attrs), 1)
Collin Winterd28fcbc2007-03-28 23:34:06 +0000794 self.assertTrue(attrs.has_key((ns_uri, "attr")))
Ezio Melotti2623a372010-11-21 13:34:58 +0000795 self.assertEqual(attrs.get((ns_uri, "attr")), "val")
796 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val")
797 self.assertEqual(attrs.items(), [((ns_uri, "attr"), "val")])
798 self.assertEqual(attrs.values(), ["val"])
799 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val")
800 self.assertEqual(attrs[(ns_uri, "attr")], "val")
Collin Winterd28fcbc2007-03-28 23:34:06 +0000801
802 # ===== InputSource support
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000803
Collin Winterd28fcbc2007-03-28 23:34:06 +0000804 def test_expat_inpsource_filename(self):
805 parser = create_parser()
806 result = StringIO()
807 xmlgen = XMLGenerator(result)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000808
Collin Winterd28fcbc2007-03-28 23:34:06 +0000809 parser.setContentHandler(xmlgen)
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000810 parser.parse(TEST_XMLFILE)
Collin Winterd28fcbc2007-03-28 23:34:06 +0000811
Ezio Melotti2623a372010-11-21 13:34:58 +0000812 self.assertEqual(result.getvalue(), xml_test_out)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000813
Collin Winterd28fcbc2007-03-28 23:34:06 +0000814 def test_expat_inpsource_sysid(self):
815 parser = create_parser()
816 result = StringIO()
817 xmlgen = XMLGenerator(result)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000818
Collin Winterd28fcbc2007-03-28 23:34:06 +0000819 parser.setContentHandler(xmlgen)
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000820 parser.parse(InputSource(TEST_XMLFILE))
Collin Winterd28fcbc2007-03-28 23:34:06 +0000821
Ezio Melotti2623a372010-11-21 13:34:58 +0000822 self.assertEqual(result.getvalue(), xml_test_out)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000823
Serhiy Storchaka23298cb2013-02-02 12:16:22 +0200824 @requires_unicode_filenames
Serhiy Storchaka8673ab92013-02-02 10:28:30 +0200825 def test_expat_inpsource_sysid_unicode(self):
826 fname = support.TESTFN_UNICODE
827 shutil.copyfile(TEST_XMLFILE, fname)
828 self.addCleanup(support.unlink, fname)
829
830 parser = create_parser()
831 result = StringIO()
832 xmlgen = XMLGenerator(result)
833
834 parser.setContentHandler(xmlgen)
835 parser.parse(InputSource(fname))
836
837 self.assertEqual(result.getvalue(), xml_test_out)
838
Serhiy Storchakae9d4dc12015-04-02 20:55:46 +0300839 def test_expat_inpsource_byte_stream(self):
Collin Winterd28fcbc2007-03-28 23:34:06 +0000840 parser = create_parser()
841 result = StringIO()
842 xmlgen = XMLGenerator(result)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000843
Collin Winterd28fcbc2007-03-28 23:34:06 +0000844 parser.setContentHandler(xmlgen)
845 inpsrc = InputSource()
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000846 inpsrc.setByteStream(open(TEST_XMLFILE))
Collin Winterd28fcbc2007-03-28 23:34:06 +0000847 parser.parse(inpsrc)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000848
Ezio Melotti2623a372010-11-21 13:34:58 +0000849 self.assertEqual(result.getvalue(), xml_test_out)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000850
Collin Winterd28fcbc2007-03-28 23:34:06 +0000851 # ===== IncrementalParser support
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000852
Collin Winterd28fcbc2007-03-28 23:34:06 +0000853 def test_expat_incremental(self):
854 result = StringIO()
855 xmlgen = XMLGenerator(result)
856 parser = create_parser()
857 parser.setContentHandler(xmlgen)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000858
Collin Winterd28fcbc2007-03-28 23:34:06 +0000859 parser.feed("<doc>")
860 parser.feed("</doc>")
861 parser.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000862
Ezio Melotti2623a372010-11-21 13:34:58 +0000863 self.assertEqual(result.getvalue(), start + "<doc></doc>")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000864
Collin Winterd28fcbc2007-03-28 23:34:06 +0000865 def test_expat_incremental_reset(self):
866 result = StringIO()
867 xmlgen = XMLGenerator(result)
868 parser = create_parser()
869 parser.setContentHandler(xmlgen)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000870
Collin Winterd28fcbc2007-03-28 23:34:06 +0000871 parser.feed("<doc>")
872 parser.feed("text")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000873
Collin Winterd28fcbc2007-03-28 23:34:06 +0000874 result = StringIO()
875 xmlgen = XMLGenerator(result)
876 parser.setContentHandler(xmlgen)
877 parser.reset()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000878
Collin Winterd28fcbc2007-03-28 23:34:06 +0000879 parser.feed("<doc>")
880 parser.feed("text")
881 parser.feed("</doc>")
882 parser.close()
883
Ezio Melotti2623a372010-11-21 13:34:58 +0000884 self.assertEqual(result.getvalue(), start + "<doc>text</doc>")
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000885
Collin Winterd28fcbc2007-03-28 23:34:06 +0000886 # ===== Locator support
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000887
Collin Winterd28fcbc2007-03-28 23:34:06 +0000888 def test_expat_locator_noinfo(self):
889 result = StringIO()
890 xmlgen = XMLGenerator(result)
891 parser = create_parser()
892 parser.setContentHandler(xmlgen)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000893
Collin Winterd28fcbc2007-03-28 23:34:06 +0000894 parser.feed("<doc>")
895 parser.feed("</doc>")
896 parser.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000897
Ezio Melotti2623a372010-11-21 13:34:58 +0000898 self.assertEqual(parser.getSystemId(), None)
899 self.assertEqual(parser.getPublicId(), None)
900 self.assertEqual(parser.getLineNumber(), 1)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000901
Collin Winterd28fcbc2007-03-28 23:34:06 +0000902 def test_expat_locator_withinfo(self):
903 result = StringIO()
904 xmlgen = XMLGenerator(result)
905 parser = create_parser()
906 parser.setContentHandler(xmlgen)
Florent Xicluna13ba1a12010-03-13 11:18:49 +0000907 parser.parse(TEST_XMLFILE)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000908
Ezio Melotti2623a372010-11-21 13:34:58 +0000909 self.assertEqual(parser.getSystemId(), TEST_XMLFILE)
910 self.assertEqual(parser.getPublicId(), None)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000911
Serhiy Storchaka23298cb2013-02-02 12:16:22 +0200912 @requires_unicode_filenames
Serhiy Storchaka8673ab92013-02-02 10:28:30 +0200913 def test_expat_locator_withinfo_unicode(self):
914 fname = support.TESTFN_UNICODE
915 shutil.copyfile(TEST_XMLFILE, fname)
916 self.addCleanup(support.unlink, fname)
917
918 result = StringIO()
919 xmlgen = XMLGenerator(result)
920 parser = create_parser()
921 parser.setContentHandler(xmlgen)
922 parser.parse(fname)
923
924 self.assertEqual(parser.getSystemId(), fname)
925 self.assertEqual(parser.getPublicId(), None)
926
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000927
Martin v. Löwis80670bc2000-10-06 21:13:23 +0000928# ===========================================================================
929#
930# error reporting
931#
932# ===========================================================================
933
Collin Winterd28fcbc2007-03-28 23:34:06 +0000934class ErrorReportingTest(unittest.TestCase):
935 def test_expat_inpsource_location(self):
936 parser = create_parser()
937 parser.setContentHandler(ContentHandler()) # do nothing
938 source = InputSource()
939 source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed
940 name = "a file name"
941 source.setSystemId(name)
942 try:
943 parser.parse(source)
944 self.fail()
945 except SAXException, e:
Ezio Melotti2623a372010-11-21 13:34:58 +0000946 self.assertEqual(e.getSystemId(), name)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000947
Collin Winterd28fcbc2007-03-28 23:34:06 +0000948 def test_expat_incomplete(self):
949 parser = create_parser()
950 parser.setContentHandler(ContentHandler()) # do nothing
951 self.assertRaises(SAXParseException, parser.parse, StringIO("<foo>"))
Serhiy Storchaka3234abb2015-05-06 09:35:52 +0300952 self.assertEqual(parser.getColumnNumber(), 5)
953 self.assertEqual(parser.getLineNumber(), 1)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000954
Collin Winterd28fcbc2007-03-28 23:34:06 +0000955 def test_sax_parse_exception_str(self):
956 # pass various values from a locator to the SAXParseException to
957 # make sure that the __str__() doesn't fall apart when None is
958 # passed instead of an integer line and column number
959 #
960 # use "normal" values for the locator:
961 str(SAXParseException("message", None,
962 self.DummyLocator(1, 1)))
963 # use None for the line number:
964 str(SAXParseException("message", None,
965 self.DummyLocator(None, 1)))
966 # use None for the column number:
967 str(SAXParseException("message", None,
968 self.DummyLocator(1, None)))
969 # use None for both:
970 str(SAXParseException("message", None,
971 self.DummyLocator(None, None)))
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000972
Collin Winterd28fcbc2007-03-28 23:34:06 +0000973 class DummyLocator:
974 def __init__(self, lineno, colno):
975 self._lineno = lineno
976 self._colno = colno
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000977
Collin Winterd28fcbc2007-03-28 23:34:06 +0000978 def getPublicId(self):
979 return "pubid"
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000980
Collin Winterd28fcbc2007-03-28 23:34:06 +0000981 def getSystemId(self):
982 return "sysid"
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000983
Collin Winterd28fcbc2007-03-28 23:34:06 +0000984 def getLineNumber(self):
985 return self._lineno
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000986
Collin Winterd28fcbc2007-03-28 23:34:06 +0000987 def getColumnNumber(self):
988 return self._colno
Martin v. Löwis80670bc2000-10-06 21:13:23 +0000989
Lars Gustäbelab647872000-09-24 18:40:52 +0000990# ===========================================================================
991#
992# xmlreader tests
993#
994# ===========================================================================
995
Collin Winterd28fcbc2007-03-28 23:34:06 +0000996class XmlReaderTest(XmlTestBase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000997
Collin Winterd28fcbc2007-03-28 23:34:06 +0000998 # ===== AttributesImpl
999 def test_attrs_empty(self):
1000 self.verify_empty_attrs(AttributesImpl({}))
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001001
Collin Winterd28fcbc2007-03-28 23:34:06 +00001002 def test_attrs_wattr(self):
1003 self.verify_attrs_wattr(AttributesImpl({"attr" : "val"}))
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001004
Collin Winterd28fcbc2007-03-28 23:34:06 +00001005 def test_nsattrs_empty(self):
1006 self.verify_empty_nsattrs(AttributesNSImpl({}, {}))
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001007
Collin Winterd28fcbc2007-03-28 23:34:06 +00001008 def test_nsattrs_wattr(self):
1009 attrs = AttributesNSImpl({(ns_uri, "attr") : "val"},
1010 {(ns_uri, "attr") : "ns:attr"})
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001011
Ezio Melotti2623a372010-11-21 13:34:58 +00001012 self.assertEqual(attrs.getLength(), 1)
1013 self.assertEqual(attrs.getNames(), [(ns_uri, "attr")])
1014 self.assertEqual(attrs.getQNames(), ["ns:attr"])
1015 self.assertEqual(len(attrs), 1)
Collin Winterd28fcbc2007-03-28 23:34:06 +00001016 self.assertTrue(attrs.has_key((ns_uri, "attr")))
Ezio Melotti2623a372010-11-21 13:34:58 +00001017 self.assertEqual(attrs.keys(), [(ns_uri, "attr")])
1018 self.assertEqual(attrs.get((ns_uri, "attr")), "val")
1019 self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val")
1020 self.assertEqual(attrs.items(), [((ns_uri, "attr"), "val")])
1021 self.assertEqual(attrs.values(), ["val"])
1022 self.assertEqual(attrs.getValue((ns_uri, "attr")), "val")
1023 self.assertEqual(attrs.getValueByQName("ns:attr"), "val")
1024 self.assertEqual(attrs.getNameByQName("ns:attr"), (ns_uri, "attr"))
1025 self.assertEqual(attrs[(ns_uri, "attr")], "val")
1026 self.assertEqual(attrs.getQNameByName((ns_uri, "attr")), "ns:attr")
Fred Drake004d5e62000-10-23 17:22:08 +00001027
Lars Gustäbelab647872000-09-24 18:40:52 +00001028
Collin Winterd28fcbc2007-03-28 23:34:06 +00001029 # During the development of Python 2.5, an attempt to move the "xml"
1030 # package implementation to a new package ("xmlcore") proved painful.
1031 # The goal of this change was to allow applications to be able to
1032 # obtain and rely on behavior in the standard library implementation
1033 # of the XML support without needing to be concerned about the
1034 # availability of the PyXML implementation.
1035 #
1036 # While the existing import hackery in Lib/xml/__init__.py can cause
1037 # PyXML's _xmlpus package to supplant the "xml" package, that only
1038 # works because either implementation uses the "xml" package name for
1039 # imports.
1040 #
1041 # The move resulted in a number of problems related to the fact that
1042 # the import machinery's "package context" is based on the name that's
1043 # being imported rather than the __name__ of the actual package
1044 # containment; it wasn't possible for the "xml" package to be replaced
1045 # by a simple module that indirected imports to the "xmlcore" package.
1046 #
1047 # The following two tests exercised bugs that were introduced in that
1048 # attempt. Keeping these tests around will help detect problems with
1049 # other attempts to provide reliable access to the standard library's
1050 # implementation of the XML support.
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001051
Collin Winterd28fcbc2007-03-28 23:34:06 +00001052 def test_sf_1511497(self):
1053 # Bug report: http://www.python.org/sf/1511497
1054 import sys
1055 old_modules = sys.modules.copy()
1056 for modname in sys.modules.keys():
1057 if modname.startswith("xml."):
1058 del sys.modules[modname]
1059 try:
1060 import xml.sax.expatreader
1061 module = xml.sax.expatreader
Ezio Melotti2623a372010-11-21 13:34:58 +00001062 self.assertEqual(module.__name__, "xml.sax.expatreader")
Collin Winterd28fcbc2007-03-28 23:34:06 +00001063 finally:
1064 sys.modules.update(old_modules)
Neal Norwitz0d4c06e2007-04-25 06:30:05 +00001065
Collin Winterd28fcbc2007-03-28 23:34:06 +00001066 def test_sf_1513611(self):
1067 # Bug report: http://www.python.org/sf/1513611
1068 sio = StringIO("invalid")
1069 parser = make_parser()
1070 from xml.sax import SAXParseException
1071 self.assertRaises(SAXParseException, parser.parse, sio)
Fred Drakefbdeaad2006-07-29 16:56:15 +00001072
Fred Drakefbdeaad2006-07-29 16:56:15 +00001073
Neal Norwitzab364c42008-03-28 07:36:31 +00001074def test_main():
Collin Winterd28fcbc2007-03-28 23:34:06 +00001075 run_unittest(MakeParserTest,
Serhiy Storchakaaff77f32015-04-02 23:05:23 +03001076 ParseTest,
Collin Winterd28fcbc2007-03-28 23:34:06 +00001077 SaxutilsTest,
Serhiy Storchakae9d4dc12015-04-02 20:55:46 +03001078 PrepareInputSourceTest,
Serhiy Storchakaf8980382013-02-10 14:26:08 +02001079 StringXmlgenTest,
1080 BytesIOXmlgenTest,
1081 WriterXmlgenTest,
Collin Winterd28fcbc2007-03-28 23:34:06 +00001082 ExpatReaderTest,
1083 ErrorReportingTest,
1084 XmlReaderTest)
Fred Drakefbdeaad2006-07-29 16:56:15 +00001085
Collin Winterd28fcbc2007-03-28 23:34:06 +00001086if __name__ == "__main__":
Neal Norwitzab364c42008-03-28 07:36:31 +00001087 test_main()